cloudflock 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. data/bin/flock +32 -0
  2. data/bin/flock-profile +17 -0
  3. data/bin/flock-servers +24 -0
  4. data/bin/flock.default +24 -0
  5. data/lib/cloudflock/error.rb +26 -0
  6. data/lib/cloudflock/interface/cli/app/common/servers.rb +128 -0
  7. data/lib/cloudflock/interface/cli/app/servers/migrate.rb +357 -0
  8. data/lib/cloudflock/interface/cli/app/servers/profile.rb +88 -0
  9. data/lib/cloudflock/interface/cli/app/servers.rb +2 -0
  10. data/lib/cloudflock/interface/cli/console.rb +213 -0
  11. data/lib/cloudflock/interface/cli/opts/servers.rb +20 -0
  12. data/lib/cloudflock/interface/cli/opts.rb +87 -0
  13. data/lib/cloudflock/interface/cli.rb +15 -0
  14. data/lib/cloudflock/patch/fog.rb +113 -0
  15. data/lib/cloudflock/remote/ssh.rb +311 -0
  16. data/lib/cloudflock/target/servers/data/exceptions/base.txt +44 -0
  17. data/lib/cloudflock/target/servers/data/exceptions/platform/amazon.txt +10 -0
  18. data/lib/cloudflock/target/servers/data/exceptions/platform/debian.txt +0 -0
  19. data/lib/cloudflock/target/servers/data/exceptions/platform/redhat.txt +5 -0
  20. data/lib/cloudflock/target/servers/data/exceptions/platform/suse.txt +1 -0
  21. data/lib/cloudflock/target/servers/data/post-migration/chroot/base.txt +1 -0
  22. data/lib/cloudflock/target/servers/data/post-migration/chroot/platform/amazon.txt +19 -0
  23. data/lib/cloudflock/target/servers/data/post-migration/pre/base.txt +3 -0
  24. data/lib/cloudflock/target/servers/data/post-migration/pre/platform/amazon.txt +4 -0
  25. data/lib/cloudflock/target/servers/migrate.rb +466 -0
  26. data/lib/cloudflock/target/servers/platform/v1.rb +97 -0
  27. data/lib/cloudflock/target/servers/platform/v2.rb +93 -0
  28. data/lib/cloudflock/target/servers/platform.rb +133 -0
  29. data/lib/cloudflock/target/servers/profile.rb +394 -0
  30. data/lib/cloudflock/target/servers.rb +5 -0
  31. data/lib/cloudflock/version.rb +3 -0
  32. data/lib/cloudflock.rb +10 -0
  33. metadata +128 -0
@@ -0,0 +1,311 @@
1
+ require 'cloudflock'
2
+ require 'expectr'
3
+ require 'socket'
4
+
5
+ # Public: Wrap the tasks of logging into remote hosts via ssh and interacting
6
+ # with them through Expectr.
7
+ #
8
+ # Examples
9
+ #
10
+ # # Log in to remote host 'host.example.com'
11
+ # shell = SSH.new(host: 'host.example.com', pass: 'examplepass')
12
+ class CloudFlock::Remote::SSH
13
+ # Public: Arguments to pass to ssh for when logging into remote hosts
14
+ SSH_ARGUMENTS = %w{-o UserKnownHostsFile=/dev/null -o
15
+ StrictHostKeyChecking=no -o NumberOfPasswordPrompts=1 -o
16
+ ConnectTimeout=15 -o ServerAliveInterval=30}.join(' ')
17
+
18
+ # Public: String used to standardize and identify the shell's prompt
19
+ PROMPT = '@@MIGRATE@@'
20
+
21
+ # Public: String representing the location of the history file
22
+ HISTFILE = '/root/.migration_history'
23
+
24
+ # Public: Initialize a new SSH object and automatically log in via SSH to
25
+ # the host with provided address/credentials.
26
+ #
27
+ # args - A Hash used to specify optional arguments (default: {}):
28
+ # :host - String used to specify the address to which to
29
+ # connect.
30
+ # :username - String containing the remote user to use.
31
+ # (default: "root")
32
+ # :password - String containing the password to use for the user
33
+ # specified with :user. (default: "")
34
+ # :port - Fixnum specifying the port to which to connect.
35
+ # (default: 22)
36
+ # :flush_buffer - Boolean specifying whether or not to flush output
37
+ # to STDOUT. (default: false)
38
+ # :timeout - Fixnum specifying the timeout for the Expectr object
39
+ # to use. (default: 30)
40
+ #
41
+ # Raises InvalidHostname if no host is specified.
42
+ # Raises InvalidHostname if looking up the host fails.
43
+ # Raises LoginFailed if logging in fails.
44
+ def initialize(args = {})
45
+ unless args[:host].kind_of?(String) && args[:host].length > 0
46
+ raise InvalidHostname, "No host specified"
47
+ end
48
+
49
+ begin
50
+ args[:host] = lookup_hostname(args[:host])
51
+ rescue SocketError
52
+ raise InvalidHostname, "Unable to look up host: #{args[:host]}"
53
+ end
54
+
55
+ # Set up the rest of the arguments Hash
56
+ args[:username] ||= ''
57
+ args[:password] ||= ''
58
+ args[:flush_buffer] = false if args[:flush_buffer].nil?
59
+ args[:username] ||= 'root'
60
+ args[:timeout] ||= 30
61
+ args[:port] ||= 22
62
+ args[:public_key] ||= ''
63
+
64
+ # Sanitize the arguments Hash
65
+ args[:username].downcase!
66
+ args[:username].gsub!(/[^a-z0-9_-]/, '')
67
+ args[:port] = args[:port].to_i
68
+ args[:password].tr!("\u0000-\u001f\u007f\u2028-\u2029", '')
69
+
70
+ # Build the SSH command to send to the system
71
+ command = "ssh #{SSH_ARGUMENTS}"
72
+ if File.file?(File.expand_path(args[:public_key]))
73
+ command << " -i #{File.expand_path(args[:public_key])}"
74
+ end
75
+ if args[:username].length > 0
76
+ args[:username] << '@'
77
+ end
78
+ command << " #{args[:username]}#{args[:host]} -p #{args[:port]}"
79
+ @expect = Expectr.new(command, flush_buffer: args[:flush_buffer],
80
+ timeout: args[:timeout])
81
+
82
+ raise LoginFailed unless login(args[:password])
83
+ rescue Timeout::Error, Expectr::ProcessError
84
+ raise LoginFailed
85
+ end
86
+
87
+ # Public: Verify the host passed and return network address to which it's
88
+ # mapped.
89
+ #
90
+ # host - String containing the hostname or IP to verify
91
+ #
92
+ # Returns a String containing an IP
93
+ def lookup_hostname(host)
94
+ Socket.getaddrinfo(host, nil, nil, Socket::SOCK_STREAM)[0][3]
95
+ end
96
+
97
+ # Public: Wrap authentication and provide passwords if requested. Upon
98
+ # detecting successful authentication, set the shell's PS1 to PROMPT.
99
+ #
100
+ # password - String containing the password to provide if requested.
101
+ # (default: "")
102
+ #
103
+ # Returns true or false indicating login success.
104
+ def login(password = '')
105
+ @expect.expect(/password/i, true) do |match|
106
+ @expect.puts(password) if match.to_s =~ /password/i
107
+ end
108
+
109
+ # Wait to get a response (e.g. prompt), then set PS1
110
+ count = 0
111
+ continue = false
112
+ until continue
113
+ if count == 5
114
+ @expect.kill!
115
+ return false
116
+ end
117
+
118
+ sleep 5
119
+ @expect.clear_buffer!
120
+ @expect.puts
121
+ continue = @expect.expect(/./, true).to_s =~ /./
122
+ count += 1
123
+ end
124
+ return false if @expect.pid == 0
125
+ @expect.puts("export PS1='#{PROMPT} '")
126
+ true
127
+ end
128
+
129
+ # Public: Check to see if the shell attached to the SSH object has superuser
130
+ # priveleges.
131
+ #
132
+ # Returns false if root privileges are detected, true otherwise.
133
+ def check_root
134
+ @expect.clear_buffer!
135
+ uid = query("UID_CHECK", command = "id")
136
+ root = /uid=0\(.*$/.match(uid)
137
+ root.nil?
138
+ end
139
+
140
+ # Public: Determine if currently logged in user is the superuser and, if
141
+ # not, attempt to gain superuser permissions via su/sudo.
142
+ #
143
+ # password - String containing password to use.
144
+ # use_sudo - Boolean value denoting whether to use sudo to obtain root
145
+ # privileges. (default: false)
146
+ #
147
+ # Returns nothing.
148
+ # Raises StandardError if we are unable to obtain root.
149
+ def get_root(password, use_sudo = false)
150
+ match = nil
151
+ if check_root
152
+ @expect.send("sudo ") if use_sudo
153
+ @expect.puts("su -")
154
+ login(password)
155
+ end
156
+
157
+ raise RootFailed, "Unable to obtain root permissions" if check_root
158
+
159
+ @expect.puts("export PS1='#{PROMPT} '")
160
+ @expect.puts("export HISTFILE='#{HISTFILE}'")
161
+
162
+ set_timeout(5) do
163
+ while prompt(true)
164
+ end
165
+ end
166
+ end
167
+
168
+ # Public: Log out of any active shells. Attempt a maximum of 10 logouts,
169
+ # then kill the ssh process if the Expectr object still reports a live pid.
170
+ #
171
+ # Returns nothing.
172
+ def logout!
173
+ count = 0
174
+ while @expect.pid > 0 && count < 10
175
+ count += 1
176
+ @expect.clear_buffer!
177
+ @expect.puts
178
+ prompt(true)
179
+ @expect.puts("exit")
180
+
181
+ sleep 1
182
+ end
183
+
184
+ @expect.kill!
185
+ rescue ArgumentError, Expectr::ProcessError
186
+ # Raised if puts or kill! fails.
187
+ end
188
+
189
+ # Public: Wait for a prompt from the SSH object.
190
+ #
191
+ # recoverable - Boolean specifying whether the prompt is recoverable if no
192
+ # match is found. (default: false)
193
+ #
194
+ # Returns nothing.
195
+ def prompt(recoverable=false)
196
+ unless recoverable.kind_of?(TrueClass) || recoverable.kind_of?(FalseClass)
197
+ raise ArgumentError, "Should specify true or false"
198
+ end
199
+
200
+ @expect.expect(PROMPT, recoverable)
201
+ end
202
+
203
+ # Public: Set the Expectr object's timeout.
204
+ #
205
+ # timeout - Fixnum containing the number of seconds which the Expectr object
206
+ # should use as its timeout value, either temporarily or until set
207
+ # explicitly again.
208
+ #
209
+ # Returns nothing.
210
+ # Yields nothing.
211
+ # Raises ArgumentError if timeout is not a Fixnum.
212
+ def set_timeout(timeout)
213
+ unless timeout.kind_of?(Fixnum) && timeout > 0
214
+ raise ArgumentError, "Expected an integer greater than 0"
215
+ end
216
+
217
+ if block_given?
218
+ old_timeout = @expect.timeout
219
+ @expect.timeout = timeout
220
+ result = yield
221
+ @expect.timeout = old_timeout
222
+ return result
223
+ else
224
+ @expect.timeout = timeout
225
+ end
226
+ end
227
+
228
+ # Public: Print a tag to a new line, followed by the output of an arbitrary
229
+ # command, then the tag again. Return everything between the two tags.
230
+ #
231
+ # tag - String containing the tag to use to isolate command output.
232
+ # This string must not be empty after being constrained to
233
+ # /[a-zA-Z0-9_-]/. (default: "SSH_TAG")
234
+ # command - Command to send to the active ssh session.
235
+ # recoverable - Whether a timeout should be considered recoverable or fatal.
236
+ # (default: false)
237
+ #
238
+ # Returns a MatchData object from the Expectr object.
239
+ # Raises ArgumentError if tag or command aren't Strings.
240
+ # Raises ArgumentError if the tag String is empty.
241
+ def query(tag = "SSH_TAG", command = "", recoverable = false)
242
+ raise ArgumentError unless command.kind_of?(String) && tag.kind_of?(String)
243
+
244
+ tag.gsub!(/[^a-zA-Z0-9_-]/, '')
245
+ raise ArgumentError, "Alphanumeric tag required" if tag.empty?
246
+
247
+ @expect.send('printf "' + tag + '\n";')
248
+ @expect.send(command.gsub(/[\r\n]/, ' '))
249
+ @expect.puts(';printf "\n' + tag + '\n";')
250
+
251
+ match = @expect.expect(/^#{tag}.*^#{tag}/m, recoverable)
252
+ prompt(recoverable)
253
+
254
+ match.to_s.gsub(/^#{tag}(.*)^#{tag}$/m, '\1').strip
255
+ end
256
+
257
+ # Public: Set whether or not the Expectr object will flush the internal
258
+ # buffer to STDOUT.
259
+ #
260
+ # flush - Boolean denoting whether to flush the buffer.
261
+ #
262
+ # Returns nothing.
263
+ def flush_buffer(flush)
264
+ @expect.flush_buffer = flush
265
+ end
266
+
267
+ # Public: Provide access to the Expectr object's output buffer.
268
+ #
269
+ # Returns a String containing the buffer.
270
+ def buffer
271
+ @expect.buffer
272
+ end
273
+
274
+ # Public: Wrap Expectr#send.
275
+ #
276
+ # command - String containing the data to send to the Expectr object.
277
+ #
278
+ # Returns nothing.
279
+ def send(command)
280
+ @expect.send(command)
281
+ end
282
+
283
+ # Public: Wrap Expectr#puts.
284
+ #
285
+ # command - String containing the data to send to the Expectr object.
286
+ # (default: "")
287
+ #
288
+ # Returns nothing.
289
+ def puts(command = "")
290
+ @expect.puts(command)
291
+ end
292
+
293
+ # Public: Wrap Expectr#expect.
294
+ #
295
+ # args - Variable length list of arguments to send to Expectr#expect.
296
+ #
297
+ # Returns a MatchData object once a match is found if no block is given.
298
+ # Yields the MatchData object representing the match.
299
+ # Raises TypeError if something other than a String or Regexp is passed.
300
+ # Raises Timeout::Error if a match isn't found, unless recoverable.
301
+ def expect(*args)
302
+ @expect.expect(*args)
303
+ end
304
+
305
+ # Public: Wrap Expectr#clear_buffer!
306
+ #
307
+ # Returns nothing.
308
+ def clear
309
+ @expect.clear_buffer!
310
+ end
311
+ end
@@ -0,0 +1,44 @@
1
+ /boot
2
+ /dev
3
+ /etc/fstab
4
+ /etc/hosts
5
+ /etc/init.d/nova-agent*
6
+ /etc/driveclient
7
+ /etc/initramfs-tools
8
+ /etc/issue
9
+ /etc/issue.net
10
+ /etc/lvm
11
+ /etc/mdadm*
12
+ /etc/mtab
13
+ /etc/mod*
14
+ /etc/network/
15
+ /etc/network.d/*
16
+ /etc/networks
17
+ /etc/rc3.d/S99Galaxy
18
+ /etc/resolv.conf
19
+ /etc/sysconfig/network/*
20
+ /etc/sysconfig/network-scripts/*
21
+ /etc/system-release
22
+ /etc/system-release-cpe
23
+ /etc/udev
24
+ /etc/prelink*
25
+ /etc/rc.conf
26
+ /etc/conf.d/net
27
+ /lib/init/rw
28
+ /lib/firmware
29
+ /lib/modules
30
+ /lib/udev
31
+ /root/.rackspace
32
+ /mnt
33
+ /net
34
+ /opt/galaxy/
35
+ /proc
36
+ /sys
37
+ /tmp
38
+ /usr/sbin/nova-agent*
39
+ /usr/share/initramfs-tools
40
+ /usr/share/nova-agent*
41
+ /var/cache/yum/*
42
+ /var/lib/initramfs-tools
43
+ /var/lock
44
+ /var/log
@@ -0,0 +1,10 @@
1
+ /usr/lib/yum-plugins
2
+ /etc/yum.conf
3
+ /etc/yum
4
+ /etc/yum.repos.d
5
+ /etc/cloud/templates/amzn*
6
+ /etc/rpm
7
+ /etc/issue
8
+ /etc/selinux/config
9
+ /etc/sysctl.conf
10
+ /etc/yum.repos.d/amzn-*
@@ -0,0 +1,5 @@
1
+ /etc/yum.repos.d/
2
+ /usr/lib/yum-plugins
3
+ /etc/yum.conf
4
+ /etc/yum
5
+ /etc/yum.repos.d
@@ -0,0 +1 @@
1
+ find /var/run -type f -exec rm {} \;
@@ -0,0 +1,19 @@
1
+ for I in $(chkconfig --list|awk '/cloud-init/ {print $1}'); do chkconfig $I off; done
2
+ for I in $(rpm -qa | grep aws); do rpm -e --nodeps $I; done
3
+ rpm -e --nodeps sysvinit
4
+ rpm -e --justdb --nodeps system-release
5
+ rpm -e --justdb --nodeps epel-release
6
+ rpm -e --justdb --nodeps initscripts
7
+ rpm -e --justdb --nodeps yum
8
+ rpm -i --justdb --nodeps /root/.rackspace/rpms/db/*.rpm
9
+ rpm -i --nodeps /root/.rackspace/rpms/full/*.rpm
10
+ rpm -qa | grep mysql55 && rpm -e --justdb --nodeps mysql{55-common,-server}
11
+ cp -a /etc/my.cnf /etc/my.cnf.migration_backup
12
+ yum clean all
13
+ yum install -y sysvinit-tools
14
+ yum install -y initscripts
15
+ for I in $(rpm -qa | grep libselinux);do rpm -e --justdb --nodeps $I; printf "$I\n"|sed 's/-[0-9].*\././'>>/tmp/selinux-install; done
16
+ for I in $(cat /tmp/selinux-install); do yum install -y $I; done
17
+ yum update -y
18
+ cp -a /etc/my.cnf /etc/my.cnf.rpmnew
19
+ mv -f /etc/my.cnf.migration_backup /etc/my.cnf
@@ -0,0 +1,3 @@
1
+ mount proc -t proc /mnt/migration_target/proc
2
+ mount /dev /mnt/migration_target/dev -o rbind
3
+ mount /sys /mnt/migration_target/sys -o rbind
@@ -0,0 +1,4 @@
1
+ yum install -y yum-plugin-downloadonly
2
+ mkdir -p /mnt/migration_target/root/.rackspace/rpms/{db,full}
3
+ yum reinstall -y --downloadonly --downloaddir=/mnt/migration_target/root/.rackspace/rpms/db/ redhat-release
4
+ yum reinstall -y --downloadonly --downloaddir=/mnt/migration_target/root/.rackspace/rpms/full/ yum