cloudflock 0.6.1 → 0.7.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.
- checksums.yaml +15 -0
- data/bin/cloudflock +7 -1
- data/bin/cloudflock-files +2 -14
- data/bin/cloudflock-profile +3 -15
- data/bin/cloudflock-servers +3 -22
- data/bin/cloudflock.default +3 -22
- data/lib/cloudflock/app/common/cleanup/unix.rb +23 -0
- data/lib/cloudflock/app/common/cleanup.rb +107 -0
- data/lib/cloudflock/app/common/exclusions/unix/centos.rb +18 -0
- data/lib/cloudflock/app/common/exclusions/unix/redhat.rb +18 -0
- data/lib/cloudflock/app/common/exclusions/unix.rb +58 -0
- data/lib/cloudflock/app/common/exclusions.rb +57 -0
- data/lib/cloudflock/app/common/platform_action.rb +59 -0
- data/lib/cloudflock/app/common/rackspace.rb +63 -0
- data/lib/cloudflock/app/common/servers.rb +673 -0
- data/lib/cloudflock/app/files-migrate.rb +246 -0
- data/lib/cloudflock/app/server-migrate.rb +327 -0
- data/lib/cloudflock/app/server-profile.rb +130 -0
- data/lib/cloudflock/app.rb +87 -0
- data/lib/cloudflock/error.rb +6 -19
- data/lib/cloudflock/errstr.rb +31 -0
- data/lib/cloudflock/remote/files.rb +82 -22
- data/lib/cloudflock/remote/ssh.rb +234 -278
- data/lib/cloudflock/target/servers/platform.rb +92 -115
- data/lib/cloudflock/target/servers/profile.rb +331 -340
- data/lib/cloudflock/task/server-profile.rb +651 -0
- data/lib/cloudflock.rb +6 -8
- metadata +49 -68
- data/lib/cloudflock/interface/cli/app/common/servers.rb +0 -128
- data/lib/cloudflock/interface/cli/app/files.rb +0 -179
- data/lib/cloudflock/interface/cli/app/servers/migrate.rb +0 -491
- data/lib/cloudflock/interface/cli/app/servers/profile.rb +0 -88
- data/lib/cloudflock/interface/cli/app/servers.rb +0 -2
- data/lib/cloudflock/interface/cli/console.rb +0 -213
- data/lib/cloudflock/interface/cli/opts/servers.rb +0 -20
- data/lib/cloudflock/interface/cli/opts.rb +0 -87
- data/lib/cloudflock/interface/cli.rb +0 -15
- data/lib/cloudflock/target/servers/data/exceptions/base.txt +0 -44
- data/lib/cloudflock/target/servers/data/exceptions/platform/amazon.txt +0 -10
- data/lib/cloudflock/target/servers/data/exceptions/platform/centos.txt +0 -7
- data/lib/cloudflock/target/servers/data/exceptions/platform/debian.txt +0 -0
- data/lib/cloudflock/target/servers/data/exceptions/platform/redhat.txt +0 -7
- data/lib/cloudflock/target/servers/data/exceptions/platform/suse.txt +0 -1
- data/lib/cloudflock/target/servers/data/post-migration/chroot/base.txt +0 -1
- data/lib/cloudflock/target/servers/data/post-migration/chroot/platform/amazon.txt +0 -19
- data/lib/cloudflock/target/servers/data/post-migration/pre/base.txt +0 -3
- data/lib/cloudflock/target/servers/data/post-migration/pre/platform/amazon.txt +0 -4
- data/lib/cloudflock/target/servers/migrate.rb +0 -466
- data/lib/cloudflock/target/servers/platform/v1.rb +0 -97
- data/lib/cloudflock/target/servers/platform/v2.rb +0 -93
- data/lib/cloudflock/target/servers.rb +0 -5
- data/lib/cloudflock/version.rb +0 -3
@@ -1,491 +0,0 @@
|
|
1
|
-
require 'fog'
|
2
|
-
require 'socket'
|
3
|
-
require 'tempfile'
|
4
|
-
require 'cloudflock/target/servers'
|
5
|
-
require 'cloudflock/interface/cli/app/common/servers'
|
6
|
-
|
7
|
-
# Public: The Servers::Migrate app provides the interface to Servers migrations
|
8
|
-
# (primarily targeting Managed/Unmanaged Rackspace First-gen and Open Cloud,
|
9
|
-
# but other migrations are possible) on the CLI.
|
10
|
-
class CloudFlock::Interface::CLI::App::Servers::Migrate
|
11
|
-
include CloudFlock::Interface::CLI::App::Common::Servers
|
12
|
-
CLI = CloudFlock::Interface::CLI::Console
|
13
|
-
|
14
|
-
# Public: Begin Servers migration on the command line
|
15
|
-
#
|
16
|
-
# opts - Hash containing options mappings.
|
17
|
-
def initialize(opts)
|
18
|
-
opencloud = (opts[:function] == :opencloud)
|
19
|
-
|
20
|
-
resume = opts[:resume]
|
21
|
-
source_host_def = define_source(opts[:config])
|
22
|
-
source_host_ssh = CLI.spinner("Logging in to #{source_host_def[:host]}") do
|
23
|
-
host_login(source_host_def)
|
24
|
-
end
|
25
|
-
|
26
|
-
source_profile = CLI.spinner("Checking source host") do
|
27
|
-
profile = Profile.new(source_host_ssh)
|
28
|
-
profile.build
|
29
|
-
profile
|
30
|
-
end
|
31
|
-
|
32
|
-
if opencloud
|
33
|
-
target_platform = Platform::V2
|
34
|
-
else
|
35
|
-
target_platform = Platform::V1
|
36
|
-
end
|
37
|
-
platform = target_platform.new(source_profile[:cpe])
|
38
|
-
build_target = platform.build_recommendation(source_profile)
|
39
|
-
flavor_list = target_platform::FLAVOR_LIST
|
40
|
-
default_target = flavor_list[build_target[:flavor]]
|
41
|
-
|
42
|
-
# Generate and display a brief summary of the server Platform/Profile
|
43
|
-
os_tag = source_profile[:cpe].vendor == "Unknown" ? CLI.red : CLI.blue
|
44
|
-
ram_qty = default_target[:mem]
|
45
|
-
hdd_qty = default_target[:hdd]
|
46
|
-
decision_reason = "#{CLI.bold}#{build_target[:flavor_reason]}#{CLI.reset}"
|
47
|
-
|
48
|
-
puts "OS: #{CLI.bold}#{os_tag}#{platform}#{CLI.reset}"
|
49
|
-
puts "---"
|
50
|
-
puts "Recommended server:"
|
51
|
-
puts "RAM: #{CLI.bold} % 6d MiB#{CLI.reset}" % ram_qty
|
52
|
-
puts "HDD: #{CLI.bold} % 7d GB#{CLI.reset}" % hdd_qty
|
53
|
-
puts "The reason for this decision is: #{decision_reason}"
|
54
|
-
puts "---"
|
55
|
-
unless source_profile.warnings.empty?
|
56
|
-
print CLI.red + CLI.bold
|
57
|
-
source_profile.warnings.each { |warning| puts warning }
|
58
|
-
print CLI.reset
|
59
|
-
puts "---"
|
60
|
-
end
|
61
|
-
|
62
|
-
if resume
|
63
|
-
destination_host_def = define_destination
|
64
|
-
migration_exclusions = determine_exclusions(source_profile[:cpe])
|
65
|
-
platform.managed = CLI.prompt_yn("Managed service level? (Y/N)",
|
66
|
-
default_answer: "Y")
|
67
|
-
platform.rack_connect = CLI.prompt_yn("Rack Connected? (Y/N)",
|
68
|
-
default_answer: "N")
|
69
|
-
else
|
70
|
-
api = {}
|
71
|
-
api[:version] = opencloud ? :v2 : :v1
|
72
|
-
|
73
|
-
proceed = CLI.prompt_yn("Spin up this server? (Y/N)", default_answer: "Y")
|
74
|
-
if proceed
|
75
|
-
api[:flavor] = default_target[:id]
|
76
|
-
else
|
77
|
-
puts CLI.build_grid(flavor_list,
|
78
|
-
{id: "ID", mem: "RAM (MiB)", hdd: "HDD (GB)" })
|
79
|
-
api[:flavor] = CLI.prompt("Flavor ID",
|
80
|
-
default_answer: default_target[:id])
|
81
|
-
api[:flavor] = api[:flavor].to_i
|
82
|
-
end
|
83
|
-
|
84
|
-
migration_exclusions = determine_exclusions(source_profile[:cpe])
|
85
|
-
platform.managed = CLI.prompt_yn("Managed service level? (Y/N)",
|
86
|
-
default_answer: "Y")
|
87
|
-
platform.rack_connect = CLI.prompt_yn("Rack Connected? (Y/N)",
|
88
|
-
default_answer: "N")
|
89
|
-
|
90
|
-
# Warn against Rack Connect
|
91
|
-
if platform.rack_connect
|
92
|
-
puts "#{CLI.bold}#{CLI.red}*** Rack Connect servers might not " +
|
93
|
-
"provision properly when spun up from the API! Resume " +
|
94
|
-
"recommended!#{CLI.reset}"
|
95
|
-
sleep 5
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
# Check to make sure we have a valid flavor ID
|
100
|
-
exit 0 if api[:flavor] == 0 or flavor_list[api[:flavor]-1].nil?
|
101
|
-
|
102
|
-
# Build our API call
|
103
|
-
api[:hostname] = CLI.prompt("New Server Name",
|
104
|
-
default_answer: source_profile[:hostname])
|
105
|
-
|
106
|
-
# OpenCloud only supports US migrations presently
|
107
|
-
if opts[:function] == :opencloud
|
108
|
-
api[:region] = CLI.prompt("Region (dfw, ord)", default_answer: "dfw",
|
109
|
-
valid_answers: ["ord", "dfw"])
|
110
|
-
else
|
111
|
-
api[:region] = :dfw
|
112
|
-
end
|
113
|
-
|
114
|
-
api[:username] = CLI.prompt("RS Cloud Username")
|
115
|
-
api[:api_key] = CLI.prompt("RS Cloud API key")
|
116
|
-
|
117
|
-
# Name cannot have any special characters in it
|
118
|
-
api[:hostname].gsub!(/[^A-Za-z0-9.]/, '-')
|
119
|
-
|
120
|
-
rack_api = Fog::Compute.new(provider: 'rackspace',
|
121
|
-
rackspace_username: api[:username],
|
122
|
-
rackspace_api_key: api[:api_key],
|
123
|
-
rackspace_region: api[:region],
|
124
|
-
version: api[:version])
|
125
|
-
|
126
|
-
# Send API call
|
127
|
-
new_server = CLI.spinner("Spinning up new server: #{api[:hostname]}") do
|
128
|
-
rack_api.servers::create(name: api[:hostname],
|
129
|
-
image_id: platform.image,
|
130
|
-
flavor_id: api[:flavor])
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
# Set the destination host address
|
135
|
-
destination_host_def = {}
|
136
|
-
CLI.spinner("Obtaining information for new instance") do
|
137
|
-
# Obtain the administrative pass for the new host.
|
138
|
-
destination_host_def[:password] = new_server.password
|
139
|
-
server_id = new_server.id
|
140
|
-
|
141
|
-
until new_server.state == 'ACTIVE'
|
142
|
-
sleep 30
|
143
|
-
begin
|
144
|
-
new_server.update
|
145
|
-
rescue NoMethodError
|
146
|
-
new_server.reload
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
if opencloud
|
151
|
-
dest_host = new_server.addresses["public"].select do |e|
|
152
|
-
e["version"] == 4
|
153
|
-
end
|
154
|
-
destination_host_def[:host] = dest_host[0]["addr"]
|
155
|
-
else
|
156
|
-
destination_host_def[:host] = new_server.addresses["public"][0]
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
# If we're working within the Managed service level, ensure that Chef
|
161
|
-
# has finished successfully
|
162
|
-
if platform.managed
|
163
|
-
r = 0
|
164
|
-
destination_host_ssh = destination_login(destination_host_def)
|
165
|
-
|
166
|
-
begin
|
167
|
-
message =
|
168
|
-
finished = CLI.spinner("Waiting for Chef to finish") do
|
169
|
-
# Sleep 180 seconds before trying
|
170
|
-
sleep 180
|
171
|
-
Migrate.setup_managed(destination_host_ssh)
|
172
|
-
end
|
173
|
-
unless finished
|
174
|
-
panic = "#{CLI.bold}#{CLI.red}*** MGC Cloud Scripts appear to " +
|
175
|
-
"have failed to run in a reasonable amount of time." +
|
176
|
-
"#{CLI.reset}"
|
177
|
-
puts panic
|
178
|
-
exit unless CLI.prompt_yn("Continue? (Y/N)", default_answer: "Y")
|
179
|
-
end
|
180
|
-
destination_host_ssh.logout!
|
181
|
-
rescue
|
182
|
-
panic = "#{CLI.bold}#{CLI.red}*** Unable to communicate with the " +
|
183
|
-
"destination host. Bailing out.#{CLI.reset}"
|
184
|
-
puts panic
|
185
|
-
raise
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
if opts[:function] == :opencloud
|
190
|
-
host = destination_host_def[:host]
|
191
|
-
CLI.spinner("Putting #{host} into rescue mode") do
|
192
|
-
new_server.rescue
|
193
|
-
destination_host_def[:password] = new_server.password
|
194
|
-
new_server.update
|
195
|
-
end
|
196
|
-
else
|
197
|
-
pass_prompt = "Please put #{api[:hostname]} into rescue mode and " +
|
198
|
-
"give password"
|
199
|
-
destination_host_def[:password] = CLI.prompt(pass_prompt)
|
200
|
-
end
|
201
|
-
|
202
|
-
CLI.spinner "Letting rescue mode come up..." do
|
203
|
-
until new_server.state == 'RESCUE'
|
204
|
-
sleep 30
|
205
|
-
begin
|
206
|
-
new_server.update
|
207
|
-
rescue NoMethodError
|
208
|
-
sleep 60
|
209
|
-
new_server.reload
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
Thread.new do
|
215
|
-
continue = false
|
216
|
-
until continue
|
217
|
-
r = 0
|
218
|
-
message = "Checking for SSH on #{destination_host_def[:host]}"
|
219
|
-
ssh_command = "ssh #{SSH::SSH_ARGUMENTS} " +
|
220
|
-
"root@#{destination_host_def[:host]}"
|
221
|
-
continue = CLI.spinner(message) do
|
222
|
-
begin
|
223
|
-
sleep 20
|
224
|
-
ssh_expect = Expectr.new(ssh_command, flush_buffer: false)
|
225
|
-
ssh_expect.expect("password")
|
226
|
-
rescue
|
227
|
-
retry if (r+=1) < 10
|
228
|
-
raise
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end.join
|
233
|
-
end
|
234
|
-
|
235
|
-
destination_host_ssh = destination_login(destination_host_def)
|
236
|
-
|
237
|
-
unless destination_host_def[:pre_steps] == false
|
238
|
-
# Attempt to set up the source host 5 times. If there is a failure,
|
239
|
-
# sleep for 60 seconds before retrying.
|
240
|
-
r = 0
|
241
|
-
begin
|
242
|
-
message = "Setting up source host (attempt #{r + 1}/5)"
|
243
|
-
pubkey = CLI.spinner(message) do
|
244
|
-
begin
|
245
|
-
message.gsub!(/\d\/5/, "#{r + 1}/5")
|
246
|
-
sleep 60 unless r == 0
|
247
|
-
Migrate.setup_source(source_host_ssh, migration_exclusions)
|
248
|
-
rescue
|
249
|
-
retry if (r += 1) < 5
|
250
|
-
raise
|
251
|
-
end
|
252
|
-
end
|
253
|
-
rescue
|
254
|
-
panic = "#{CLI.bold}#{CLI.red}*** Unable to communicate with the " +
|
255
|
-
"source host. Bailing out.#{CLI.reset}"
|
256
|
-
puts panic
|
257
|
-
raise
|
258
|
-
end
|
259
|
-
|
260
|
-
# Attempt to set up the destination host 5 times. If there is a
|
261
|
-
# failure, sleep for 60 seconds before retrying.
|
262
|
-
r = 0
|
263
|
-
begin
|
264
|
-
message = "Setting up destination host (attempt #{r + 1}/5)"
|
265
|
-
CLI.spinner(message) do
|
266
|
-
begin
|
267
|
-
message.gsub!(/\d\/5/, "#{r + 1}/5")
|
268
|
-
sleep 60 unless r == 0
|
269
|
-
Migrate.setup_destination(destination_host_ssh, pubkey)
|
270
|
-
rescue
|
271
|
-
retry if (r += 1) < 5
|
272
|
-
raise
|
273
|
-
end
|
274
|
-
end
|
275
|
-
rescue
|
276
|
-
panic = "#{CLI.bold}#{CLI.red}*** Unable to communicate with the " +
|
277
|
-
"destination host. Bailing out.#{CLI.reset}"
|
278
|
-
puts panic
|
279
|
-
raise
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
# Determine if Service Net can be used
|
284
|
-
begin
|
285
|
-
CLI.spinner "Checking for ServiceNet" do
|
286
|
-
target_addr = Migrate.check_servicenet(source_host_ssh,
|
287
|
-
destination_host_ssh)
|
288
|
-
raise if target_addr.nil?
|
289
|
-
destination_host_def[:target_addr] = target_addr
|
290
|
-
end
|
291
|
-
rescue
|
292
|
-
destination_host_def[:target_addr] = destination_host_def[:host]
|
293
|
-
end
|
294
|
-
|
295
|
-
# Set rsync path and no timeout for the migration rsync
|
296
|
-
destination_host_def[:timeout] = -1
|
297
|
-
destination_host_def[:rsync] = source_profile[:rsync]
|
298
|
-
|
299
|
-
# Kick off the migration proper
|
300
|
-
if opts[:verbose]
|
301
|
-
Migrate.migrate_server(source_host_ssh, destination_host_def)
|
302
|
-
else
|
303
|
-
CLI.spinner "Performing migration" do
|
304
|
-
Migrate.migrate_server(source_host_ssh, destination_host_def)
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
CLI.spinner "Logging out of source host" do
|
309
|
-
source_host_ssh.logout!
|
310
|
-
end
|
311
|
-
|
312
|
-
CLI.spinner "Cleaning up destination host" do
|
313
|
-
Migrate.clean_destination(destination_host_ssh, source_profile[:cpe])
|
314
|
-
end
|
315
|
-
|
316
|
-
ip_list = determine_ips(source_profile[:ip][:public])
|
317
|
-
ip_list.each { |ip| remediate_ip_config(destination_host_ssh, ip, destination_host_def[:host]) }
|
318
|
-
|
319
|
-
CLI.spinner "Logging out of destination host" do
|
320
|
-
source_host_ssh.logout!
|
321
|
-
end
|
322
|
-
|
323
|
-
puts
|
324
|
-
puts "#{CLI.bold}#{CLI.blue}*** Migration complete#{CLI.reset}\a"
|
325
|
-
end
|
326
|
-
|
327
|
-
# Internal: Ask whether or not to edit the default exclusion list for a given
|
328
|
-
# platform, and facilitate the edit if so.
|
329
|
-
#
|
330
|
-
# cpe - CPE object for the host in question.
|
331
|
-
#
|
332
|
-
# Returns a String containing the exclusions.
|
333
|
-
# Raises ArgumentError if cpe isn't a CPE object.
|
334
|
-
def determine_exclusions(cpe)
|
335
|
-
raise ArgumentError unless cpe.kind_of?(CPE)
|
336
|
-
|
337
|
-
exclusion_string = Migrate.build_default_exclusions(cpe)
|
338
|
-
alter = CLI.prompt_yn("Edit default exclusions list? (Y/N)",
|
339
|
-
default_answer: "N")
|
340
|
-
|
341
|
-
if alter
|
342
|
-
tmp_file = Tempfile.new("exclude")
|
343
|
-
tmp_file.write(exclusion_string)
|
344
|
-
tmp_file.close
|
345
|
-
|
346
|
-
# Allow for "other" editors
|
347
|
-
if File.exists?("/usr/bin/editor")
|
348
|
-
editor = "/usr/bin/editor"
|
349
|
-
else
|
350
|
-
editor = "vim"
|
351
|
-
end
|
352
|
-
|
353
|
-
system("#{editor} #{tmp_file.path}")
|
354
|
-
tmp_file.open
|
355
|
-
exclusion_string = tmp_file.read
|
356
|
-
tmp_file.close
|
357
|
-
tmp_file.unlink
|
358
|
-
end
|
359
|
-
|
360
|
-
exclusion_string
|
361
|
-
end
|
362
|
-
|
363
|
-
# Internal: Show the list of IPs detected and ascertain whether to perform
|
364
|
-
# remediation of configuration files.
|
365
|
-
#
|
366
|
-
# ip_list - Array containing Strings containing IP addresses detected from
|
367
|
-
# the source host.
|
368
|
-
#
|
369
|
-
# Returns an Array of Strings containing IPs to consider for remediation.
|
370
|
-
# Raises an ArgumentError if ip_list is not an Array.
|
371
|
-
def determine_ips(ip_list)
|
372
|
-
unless ip_list.kind_of?(Array)
|
373
|
-
raise ArgumentError, "Expected an Array, got a #{ip_list.class.name}"
|
374
|
-
end
|
375
|
-
return([]) if ip_list.empty?
|
376
|
-
|
377
|
-
puts "IP Addresses detected for remediation:"
|
378
|
-
puts ip_list
|
379
|
-
|
380
|
-
if CLI.prompt_yn("Edit IP list? (Y/N)", default_answer: "N")
|
381
|
-
tmp_file = Tempfile.new("ips")
|
382
|
-
tmp_file.write(ip_list.join("\n"))
|
383
|
-
tmp_file.close
|
384
|
-
|
385
|
-
launch_editor(tmp_file.path)
|
386
|
-
tmp_file.open
|
387
|
-
|
388
|
-
ip_list = tmp_file.lines.select do |ip|
|
389
|
-
begin
|
390
|
-
Socket.getaddrinfo(ip, nil, nil, Socket::SOCK_STREAM)[0][3]
|
391
|
-
rescue SocketError
|
392
|
-
false
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
tmp_file.close
|
397
|
-
tmp_file.unlink
|
398
|
-
end
|
399
|
-
|
400
|
-
ip_list
|
401
|
-
end
|
402
|
-
|
403
|
-
# Internal: Find instances of a given IP in configuration files and
|
404
|
-
# selectively replace them with an IP available on the target system.
|
405
|
-
#
|
406
|
-
# destination_shell - CloudFlock::Remote::SSH object connected to the
|
407
|
-
# destination host.
|
408
|
-
# source_ip - String containing an IP.
|
409
|
-
# destination_ip - String containing primary IP detected for the host.
|
410
|
-
#
|
411
|
-
# Returns nothing.
|
412
|
-
# Raises ArgumentError if source_ip is not a String.
|
413
|
-
def remediate_ip_config(destination_shell, source_ip, destination_ip)
|
414
|
-
unless destination_shell.kind_of?(CloudFlock::Remote::SSH)
|
415
|
-
raise ArgumentError, "Expected an SSH session, got a " +
|
416
|
-
"#{destination_shell.class.name}"
|
417
|
-
end
|
418
|
-
unless source_ip.kind_of?(String)
|
419
|
-
raise ArgumentError, "Expected a String, got a #{source_ip.class.name}"
|
420
|
-
end
|
421
|
-
source_ip = Socket.getaddrinfo(source_ip, nil, nil,
|
422
|
-
Socket::SOCK_STREAM)[0][3]
|
423
|
-
|
424
|
-
grep_command = "grep -rl #{source_ip} /mnt/migration_target/etc " +
|
425
|
-
"2>/dev/null | grep -v log"
|
426
|
-
files = destination_shell.query("IP_CHECK", grep_command)
|
427
|
-
unless files.strip.empty?
|
428
|
-
tmp_file = Tempfile.new("filelist")
|
429
|
-
tmp_file.write("Configuration files found for #{source_ip}:\n\n#{files}")
|
430
|
-
tmp_file.close
|
431
|
-
system("less -C #{tmp_file.path}")
|
432
|
-
tmp_file.unlink
|
433
|
-
|
434
|
-
proceed = CLI.prompt_yn("Replace #{source_ip} in these files? (Y/N)",
|
435
|
-
default_answer: "Y")
|
436
|
-
if proceed
|
437
|
-
edit = false
|
438
|
-
else
|
439
|
-
edit = CLI.prompt_yn("Edit the list? (Y/N)", default: "Y")
|
440
|
-
end
|
441
|
-
|
442
|
-
if edit
|
443
|
-
tmp_file = Tempfile.new("filelist")
|
444
|
-
tmp_file.write("#{files}")
|
445
|
-
tmp_file.close
|
446
|
-
launch_editor(tmp_file.path)
|
447
|
-
tmp_file.open
|
448
|
-
files = tmp_file.read
|
449
|
-
tmp_file.unlink
|
450
|
-
proceed = true
|
451
|
-
end
|
452
|
-
|
453
|
-
if proceed
|
454
|
-
replacement_ip = CLI.prompt("IP to replace #{source_ip}",
|
455
|
-
default_answer: destination_ip)
|
456
|
-
CLI.spinner("Remediating configuration files for #{source_ip}") do
|
457
|
-
files.each_line do |file|
|
458
|
-
file.strip!
|
459
|
-
next if file.empty?
|
460
|
-
file.gsub!(/'/, "\\'")
|
461
|
-
|
462
|
-
sed_command = "sed -i 's/#{source_ip}/#{replacement_ip}/' '#{file}'"
|
463
|
-
destination_shell.puts(sed_command)
|
464
|
-
destination_shell.prompt
|
465
|
-
end
|
466
|
-
end
|
467
|
-
end
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
# Internal: Launch the user's editor to edit a file.
|
472
|
-
#
|
473
|
-
# path - String containing the path to the file to edit
|
474
|
-
#
|
475
|
-
# Returns nothing.
|
476
|
-
# Raises Errno::ENOENT if the path does not point to an existing file.
|
477
|
-
def launch_editor(path)
|
478
|
-
unless File.exists?(path)
|
479
|
-
raise Errno::ENOENT, "Unable to open #{path}"
|
480
|
-
end
|
481
|
-
|
482
|
-
# Allow for "other" editors
|
483
|
-
if File.exists?("/usr/bin/editor")
|
484
|
-
editor = "/usr/bin/editor"
|
485
|
-
else
|
486
|
-
editor = "vim"
|
487
|
-
end
|
488
|
-
|
489
|
-
system("#{editor} #{path}")
|
490
|
-
end
|
491
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
require 'cloudflock/target/servers'
|
2
|
-
require 'cloudflock/interface/cli/app/common/servers'
|
3
|
-
|
4
|
-
# Public: The Profile class provides the interface to produces profiles
|
5
|
-
# describing servers running Unix-like operating systems.
|
6
|
-
class CloudFlock::Interface::CLI::App::Servers::Profile
|
7
|
-
include CloudFlock::Interface::CLI::App::Common::Servers
|
8
|
-
include CloudFlock::Target::Servers
|
9
|
-
CLI = CloudFlock::Interface::CLI::Console
|
10
|
-
|
11
|
-
# Public: Begin Servers migration on the command line
|
12
|
-
#
|
13
|
-
# opts - Hash containing options mappings.
|
14
|
-
def initialize(opts)
|
15
|
-
resume = opts[:resume]
|
16
|
-
source_host_def = define_source(opts[:config])
|
17
|
-
source_host_ssh = CLI.spinner("Logging in to #{source_host_def[:host]}") do
|
18
|
-
host_login(source_host_def)
|
19
|
-
end
|
20
|
-
|
21
|
-
profile = CLI.spinner("Checking source host") do
|
22
|
-
profile = Profile.new(source_host_ssh)
|
23
|
-
profile.build
|
24
|
-
profile
|
25
|
-
end
|
26
|
-
platform = Platform::V2.new(profile[:cpe])
|
27
|
-
|
28
|
-
memory = profile[:memory]
|
29
|
-
memory_percent = memory[:mem_used].to_f / memory[:total] * 100
|
30
|
-
swapping = memory[:swapping?]
|
31
|
-
ftag = "#{CLI.bold}%15s#{CLI.reset}:"
|
32
|
-
hist_mem = profile[:memory_hist][:mem_used]
|
33
|
-
|
34
|
-
puts
|
35
|
-
puts "#{CLI.bold}System Information#{CLI.reset}"
|
36
|
-
puts "#{ftag} #{platform} (#{profile[:cpe]})" % "OS"
|
37
|
-
puts "#{ftag} #{profile[:arch]}" % "Arch"
|
38
|
-
puts "#{ftag} #{profile[:hostname]}" % "Hostname"
|
39
|
-
puts
|
40
|
-
|
41
|
-
puts "#{CLI.bold}CPU Statistics#{CLI.reset}"
|
42
|
-
puts "#{ftag} %d" % ["CPU Count", profile[:cpu][:count]]
|
43
|
-
puts "#{ftag} %d MHz" % ["CPU Speed", profile[:cpu][:speed]]
|
44
|
-
puts
|
45
|
-
|
46
|
-
puts "#{CLI.bold}Memory Statistics#{CLI.reset}"
|
47
|
-
puts "#{ftag} %d MiB" % ["Total RAM", memory[:total]]
|
48
|
-
puts "#{ftag} %d MiB (%2.1f%%)" % ["RAM Used", memory[:mem_used],
|
49
|
-
memory_percent]
|
50
|
-
puts "#{ftag} %d MiB" % ["Swap Used", memory[:swap_used]] if swapping
|
51
|
-
puts "#{ftag} %d%%" % ["Hist. RAM Used", hist_mem] unless hist_mem.nil?
|
52
|
-
puts
|
53
|
-
|
54
|
-
puts "#{CLI.bold}Hard Disk Statistics#{CLI.reset}"
|
55
|
-
puts "#{ftag} %2.1f GB" % ["Disk Used", profile[:disk]]
|
56
|
-
puts
|
57
|
-
|
58
|
-
puts "#{CLI.bold}System Statistics#{CLI.reset}"
|
59
|
-
puts "#{ftag} #{profile[:io][:uptime]}" % "Uptime"
|
60
|
-
puts "#{ftag} #{profile[:io][:wait]}" % "I/O Wait"
|
61
|
-
puts
|
62
|
-
|
63
|
-
puts "#{CLI.bold}IP Information#{CLI.reset}"
|
64
|
-
puts "#{ftag} #{profile[:ip][:public].join(', ')}" % "Public"
|
65
|
-
puts "#{ftag} #{profile[:ip][:private].join(', ')}" % "Private"
|
66
|
-
puts
|
67
|
-
|
68
|
-
puts "#{CLI.bold}MySQL Databases#{CLI.reset}"
|
69
|
-
puts "#{ftag} #{profile[:db][:count]}" % "Number"
|
70
|
-
puts "#{ftag} #{profile[:db][:size]}" % "Total Size"
|
71
|
-
puts
|
72
|
-
|
73
|
-
puts "#{CLI.bold}Libraries#{CLI.reset}"
|
74
|
-
puts "#{ftag} #{profile[:lib][:libc]}" % "LIBC"
|
75
|
-
puts "#{ftag} #{profile[:lib][:perl]}" % "Perl"
|
76
|
-
puts "#{ftag} #{profile[:lib][:python]}" % "Python"
|
77
|
-
puts "#{ftag} #{profile[:lib][:ruby]}" % "Ruby"
|
78
|
-
puts "#{ftag} #{profile[:lib][:php]}" % "PHP"
|
79
|
-
unless profile.warnings.empty?
|
80
|
-
puts
|
81
|
-
print CLI.red + CLI.bold
|
82
|
-
profile.warnings.each { |warning| puts warning }
|
83
|
-
print CLI.reset
|
84
|
-
end
|
85
|
-
|
86
|
-
source_host_ssh.logout!
|
87
|
-
end
|
88
|
-
end
|