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.
- data/bin/flock +32 -0
- data/bin/flock-profile +17 -0
- data/bin/flock-servers +24 -0
- data/bin/flock.default +24 -0
- data/lib/cloudflock/error.rb +26 -0
- data/lib/cloudflock/interface/cli/app/common/servers.rb +128 -0
- data/lib/cloudflock/interface/cli/app/servers/migrate.rb +357 -0
- data/lib/cloudflock/interface/cli/app/servers/profile.rb +88 -0
- data/lib/cloudflock/interface/cli/app/servers.rb +2 -0
- data/lib/cloudflock/interface/cli/console.rb +213 -0
- data/lib/cloudflock/interface/cli/opts/servers.rb +20 -0
- data/lib/cloudflock/interface/cli/opts.rb +87 -0
- data/lib/cloudflock/interface/cli.rb +15 -0
- data/lib/cloudflock/patch/fog.rb +113 -0
- data/lib/cloudflock/remote/ssh.rb +311 -0
- data/lib/cloudflock/target/servers/data/exceptions/base.txt +44 -0
- data/lib/cloudflock/target/servers/data/exceptions/platform/amazon.txt +10 -0
- data/lib/cloudflock/target/servers/data/exceptions/platform/debian.txt +0 -0
- data/lib/cloudflock/target/servers/data/exceptions/platform/redhat.txt +5 -0
- data/lib/cloudflock/target/servers/data/exceptions/platform/suse.txt +1 -0
- data/lib/cloudflock/target/servers/data/post-migration/chroot/base.txt +1 -0
- data/lib/cloudflock/target/servers/data/post-migration/chroot/platform/amazon.txt +19 -0
- data/lib/cloudflock/target/servers/data/post-migration/pre/base.txt +3 -0
- data/lib/cloudflock/target/servers/data/post-migration/pre/platform/amazon.txt +4 -0
- data/lib/cloudflock/target/servers/migrate.rb +466 -0
- data/lib/cloudflock/target/servers/platform/v1.rb +97 -0
- data/lib/cloudflock/target/servers/platform/v2.rb +93 -0
- data/lib/cloudflock/target/servers/platform.rb +133 -0
- data/lib/cloudflock/target/servers/profile.rb +394 -0
- data/lib/cloudflock/target/servers.rb +5 -0
- data/lib/cloudflock/version.rb +3 -0
- data/lib/cloudflock.rb +10 -0
- 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
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
/etc/zypp
|
@@ -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,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
|