cloudflock 0.7.2 → 0.7.3
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 +4 -4
- data/lib/cloudflock/app/common/rackspace.rb +1 -1
- data/lib/cloudflock/app/common/servers.rb +267 -49
- data/lib/cloudflock/app/server-migrate.rb +10 -1
- data/lib/cloudflock/app/server-profile.rb +3 -4
- data/lib/cloudflock/app.rb +4 -3
- data/lib/cloudflock/errstr.rb +7 -7
- data/lib/cloudflock/remote/ssh.rb +24 -26
- data/lib/cloudflock.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77bad0c9dba5e0acc40d97db07f2275c580ccd83
|
4
|
+
data.tar.gz: ea1c657a195ab4a6643e6c8b9267d6db074f7339
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a827d7f5a0882d97e6d8d3be223b24e66580d89e59b91123f19e0b1f5bafb15f0ce076876806e6e3b501859a1b2c59d4f24883ccf05605b90218b2307f4fa0e
|
7
|
+
data.tar.gz: 6bc57df99c5cb032eba75095634eb974cea9050f7f063d4195978739391772532acfdefda92e4d711c3fe2fb580cacefcdc842b2f7a04faa14145201844decd5
|
@@ -39,7 +39,7 @@ module CloudFlock; module App
|
|
39
39
|
def define_rackspace_cloudservers_region(api)
|
40
40
|
api.merge(define_rackspace_service_region(api, 'cloudServersOpenStack'))
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
# Public: Wrap define_rackspace_service_region, specifying 'cloudFiles' as
|
44
44
|
# the service type.
|
45
45
|
#
|
@@ -93,6 +93,57 @@ module CloudFlock; module App
|
|
93
93
|
host
|
94
94
|
end
|
95
95
|
|
96
|
+
# Public: Attempt to log in to a source server to be migrated.
|
97
|
+
#
|
98
|
+
# source_host - Hash containing any options which may pertain to the host.
|
99
|
+
#
|
100
|
+
# Returns a CloudFlock::Remote::SSH object logged in to a remote host.
|
101
|
+
def connect_source(source_host)
|
102
|
+
connect_host(source_host, :define_source)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Public: Attempt to log in to a destination server to which data will be
|
106
|
+
# migrated.
|
107
|
+
#
|
108
|
+
# dest_host - Hash containing any options which may pertain to the host.
|
109
|
+
#
|
110
|
+
# Returns a CloudFlock::Remote::SSH object logged in to a remote host.
|
111
|
+
def connect_destination(dest_host)
|
112
|
+
connect_host(source_host, :define_destination)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Public: Attempt to log in to a target host.
|
116
|
+
#
|
117
|
+
# host - Hash containing any applicable options mappings for the
|
118
|
+
# server in question.
|
119
|
+
# define_method - Name of the method to call when re-defining host to
|
120
|
+
# recover from an exception.
|
121
|
+
#
|
122
|
+
# Returns a CloudFlock::Remote::SSH object logged in to the target host.
|
123
|
+
def connect_host(host, define_method)
|
124
|
+
UI.spinner("Logging in to #{host[:hostname]}") do
|
125
|
+
SSH.new(host)
|
126
|
+
end
|
127
|
+
rescue CloudFlock::Remote::SSH::InvalidHostname => e
|
128
|
+
error = "Cannot look up #{host[:hostname]}"
|
129
|
+
retry_exit(e.message, 'Try another host? (Y/N)')
|
130
|
+
|
131
|
+
host = self.send(define_method, (host.merge({hostname: nil})))
|
132
|
+
retry
|
133
|
+
rescue CloudFlock::Remote::SSH::SSHCannotConnect => e
|
134
|
+
retry if retry_prompt(e.message)
|
135
|
+
retry_exit('', 'Try another host? (Y/N)')
|
136
|
+
|
137
|
+
host = self.send(define_method, (host.merge({hostname: nil})))
|
138
|
+
retry
|
139
|
+
rescue Net::SSH::AuthenticationFailed => e
|
140
|
+
retry_exit("Cannot log in as #{host[:username]}.")
|
141
|
+
|
142
|
+
options = {username: nil, password: nil}
|
143
|
+
host = self.send(define_method, (host.merge(options)))
|
144
|
+
retry
|
145
|
+
end
|
146
|
+
|
96
147
|
# Public: Have the user select from a list of available images to provision
|
97
148
|
# a new host.
|
98
149
|
#
|
@@ -142,8 +193,8 @@ module CloudFlock; module App
|
|
142
193
|
end
|
143
194
|
image_list.map! { |image| { name: image.name, id: image.id } }
|
144
195
|
rescue Excon::Errors::Timeout
|
145
|
-
|
146
|
-
|
196
|
+
retry_exit('Unable to fetch a list of available images.')
|
197
|
+
retry
|
147
198
|
end
|
148
199
|
|
149
200
|
# Public: Have the user select from a list of available flavors to
|
@@ -191,6 +242,9 @@ module CloudFlock; module App
|
|
191
242
|
flavor_list.select! { |flavor| flavor.disk > hdd && flavor.ram > ram }
|
192
243
|
end
|
193
244
|
flavor_list.map! { |flavor| { name: flavor.name, id: flavor.id } }
|
245
|
+
rescue Fog::Errors::TimeoutError, Excon::Errors::Timeout
|
246
|
+
retry_exit('Unable to fetch flavor list.')
|
247
|
+
retry
|
194
248
|
end
|
195
249
|
|
196
250
|
# Public: Prompt user for the name of a new host to be created, presenting
|
@@ -242,7 +296,7 @@ module CloudFlock; module App
|
|
242
296
|
{ username: 'root', port: '22' }.merge(get_host_details(host))
|
243
297
|
rescue Fog::Errors::TimeoutError, Excon::Errors::Timeout
|
244
298
|
retry if retry_prompt('Provisioning failed.')
|
245
|
-
|
299
|
+
exit
|
246
300
|
end
|
247
301
|
|
248
302
|
# Public: Wait for a Rackspace Cloud instance to be provisioned.
|
@@ -260,7 +314,7 @@ module CloudFlock; module App
|
|
260
314
|
|
261
315
|
retry if UI.prompt_yn("#{error} Continue waiting? (Y/N)",
|
262
316
|
default_answer: 'Y')
|
263
|
-
|
317
|
+
exit
|
264
318
|
end
|
265
319
|
|
266
320
|
# Public: Wait for a Rackspace Cloud instance with Managed service level to
|
@@ -277,6 +331,11 @@ module CloudFlock; module App
|
|
277
331
|
UI.spinner('Waiting for managed cloud automation to complete') do
|
278
332
|
ssh.as_root("while [ ! -f #{finished} ]; do sleep 5; done", 3600)
|
279
333
|
end
|
334
|
+
rescue Timeout::Error
|
335
|
+
retry if retry_prompt('Managed cloud automation timed out.')
|
336
|
+
host.destroy if UI.prompt_yn('Delete newly created host?')
|
337
|
+
|
338
|
+
exit
|
280
339
|
end
|
281
340
|
|
282
341
|
# Public: Get details for a Fog::Compute instance.
|
@@ -305,10 +364,12 @@ module CloudFlock; module App
|
|
305
364
|
retry if retry_prompt('Timeout exceeded waiting for the host.')
|
306
365
|
|
307
366
|
host.destroy
|
308
|
-
|
367
|
+
exit
|
309
368
|
end
|
310
369
|
rescue Excon::Errors::Timeout
|
311
|
-
retry if retry_prompt('API timed out waiting
|
370
|
+
retry if retry_prompt('API timed out.', 'Continue waiting?')
|
371
|
+
|
372
|
+
exit
|
312
373
|
end
|
313
374
|
|
314
375
|
# Public: Connect to a host via SSH, automatically retrying a set number of
|
@@ -333,8 +394,27 @@ module CloudFlock; module App
|
|
333
394
|
end
|
334
395
|
end
|
335
396
|
rescue Net::SSH::Disconnect
|
336
|
-
|
337
|
-
|
397
|
+
retry_exit('Unable to establish a connection.')
|
398
|
+
retry
|
399
|
+
rescue ArgumentError
|
400
|
+
retry_exit('Incorrect passphrase provided for ssh key.')
|
401
|
+
|
402
|
+
host.delete(:passphrase)
|
403
|
+
check_option_pw(host, :passphrase, "Key passphrase", default_answer: '',
|
404
|
+
allow_empty: true)
|
405
|
+
retry
|
406
|
+
end
|
407
|
+
|
408
|
+
# Public: Get details for a Fog::Compute instance.
|
409
|
+
#
|
410
|
+
# host - Fog::Compute instance.
|
411
|
+
#
|
412
|
+
# Returns a Hash containing the host's address and root password.
|
413
|
+
def destroy_host(host)
|
414
|
+
host.destroy
|
415
|
+
rescue Fog::Errors::TimeoutError, Excon::Errors::Timeout
|
416
|
+
retry_exit('API Timed out trying to delete the host.')
|
417
|
+
retry
|
338
418
|
end
|
339
419
|
|
340
420
|
# Public: Perform the final preperatory steps necessary as well as the
|
@@ -346,37 +426,113 @@ module CloudFlock; module App
|
|
346
426
|
#
|
347
427
|
# Returns a String containing the host's new ssh public key.
|
348
428
|
def migrate_server(source_shell, dest_shell, exclusions)
|
349
|
-
pubkey =
|
350
|
-
|
429
|
+
pubkey = prepare_source_ssh_keygen(source_shell)
|
430
|
+
prepare_source_exclusions(source_shell, exclusions)
|
431
|
+
setup_destination(dest_shell, pubkey)
|
432
|
+
rsync = prepare_source_rsync(source_shell, dest_shell)
|
433
|
+
dest_address = prepare_source_servicenet(source_shell, dest_shell)
|
434
|
+
|
435
|
+
rsync = "#{rsync} -azP -e 'ssh #{SSH_ARGUMENTS} -i #{PRIVATE_KEY}' " +
|
436
|
+
"--exclude-from='#{EXCLUSIONS}' / #{dest_address}:#{MOUNT_POINT}"
|
437
|
+
rsync_migrate(source_shell, rsync)
|
438
|
+
end
|
439
|
+
|
440
|
+
# Public: Generate a new ssh keypair to be used for the migration.
|
441
|
+
#
|
442
|
+
# shell - SSH object logged in to the source host.
|
443
|
+
#
|
444
|
+
# Returns a String containing the new public key.
|
445
|
+
def prepare_source_ssh_keygen(shell)
|
446
|
+
UI.spinner('Generating a keypair for the source environment') do
|
447
|
+
generate_keypair(shell)
|
351
448
|
end
|
449
|
+
rescue Timeout::Error
|
450
|
+
retry_exit('Host is taking a long time generating an ssh keypair.')
|
451
|
+
retry
|
452
|
+
end
|
352
453
|
|
353
|
-
|
354
|
-
|
454
|
+
# Public: Generate a new ssh keypair to be used for the migration.
|
455
|
+
#
|
456
|
+
# shell - SSH object logged in to the source host.
|
457
|
+
# exclusions - String containing the exclusions list for the source host.
|
458
|
+
#
|
459
|
+
# Returns a String containing the new public key.
|
460
|
+
def prepare_source_exclusions(shell, exclusions)
|
461
|
+
UI.spinner('Setting up migration exclusions') do
|
462
|
+
shell.as_root("cat <<EOF> #{EXCLUSIONS}\n#{exclusions}\nEOF")
|
355
463
|
end
|
464
|
+
rescue Timeout::Error
|
465
|
+
retry_exit('Host is taking a long time to respond.')
|
466
|
+
retry
|
467
|
+
end
|
356
468
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
469
|
+
# Public: Generate a new ssh keypair to be used for the migration.
|
470
|
+
#
|
471
|
+
# source_shell - SSH object logged in to the source host.
|
472
|
+
# dest_shell - SSH object logged in to the destination host.
|
473
|
+
#
|
474
|
+
# Returns a String containing the location rsync on the source host.
|
475
|
+
def prepare_source_rsync(source_shell, dest_shell)
|
476
|
+
UI.spinner('Determining rsync location') do
|
477
|
+
location = determine_rsync(source_shell)
|
478
|
+
location = transfer_rsync(source_shell, dest_shell) if location.empty?
|
362
479
|
|
363
480
|
location
|
364
481
|
end
|
482
|
+
rescue Timeout::Error
|
483
|
+
retry_exit('Host is taking a long detecting/installing rsync.')
|
484
|
+
retry
|
485
|
+
end
|
365
486
|
|
487
|
+
# Public: Determine the target IP address to use for rsync.
|
488
|
+
#
|
489
|
+
# source_shell - SSH object logged in to the source host.
|
490
|
+
# dest_shell - SSH object logged in to the destination host.
|
491
|
+
#
|
492
|
+
# Returns a String containing the new IP address to use.
|
493
|
+
def prepare_source_servicenet(source_shell, dest_shell)
|
366
494
|
dest_address = UI.spinner('Checking for ServiceNet') do
|
367
495
|
determine_target_address(source_shell, dest_shell)
|
368
496
|
end
|
497
|
+
rescue Timeout::Error
|
498
|
+
retry_exit('Host is taking a long detecting available networks.')
|
499
|
+
retry
|
500
|
+
end
|
369
501
|
|
370
|
-
|
371
|
-
|
372
|
-
|
502
|
+
# Public: Wrap performing an rsync migration.
|
503
|
+
#
|
504
|
+
# shell - SSH object logged in to the source host.
|
505
|
+
# rsync - Command to be run on the source host.
|
506
|
+
#
|
507
|
+
# Returns nothing.
|
508
|
+
def rsync_migrate(shell, rsync)
|
373
509
|
UI.spinner('Performing rsync migration') do
|
374
510
|
2.times do
|
375
|
-
|
376
|
-
source_shell.as_root(rsync, 7200)
|
377
|
-
source_shell.as_root("sed -i 's/\/var\/log//g' #{EXCLUSIONS}")
|
511
|
+
rsync_migrate_commands(shell, rsync)
|
378
512
|
end
|
513
|
+
shell.logout!
|
379
514
|
end
|
515
|
+
rescue Timeout::Error
|
516
|
+
retry if retry_prompt('Server sync is taking a very long time')
|
517
|
+
exit
|
518
|
+
end
|
519
|
+
|
520
|
+
# Public: Issue an rsync command, keeping track of how many times a timeout
|
521
|
+
# has occurred, raising an error past a threshhold of 3 timeouts.
|
522
|
+
#
|
523
|
+
# shell - SSH object logged in to the source host.
|
524
|
+
# rsync - Rsync command to be run on the source host.
|
525
|
+
# timeout - Number of times the timeout has been reached. (default: 0)
|
526
|
+
#
|
527
|
+
# Returns nothing.
|
528
|
+
def rsync_migrate_commands(shell, rsync, timeout = 0)
|
529
|
+
shell.as_root(rsync, 7200)
|
530
|
+
shell.as_root("sed -i 's/\/var\/log//g' #{EXCLUSIONS}")
|
531
|
+
rescue Timeout::Error
|
532
|
+
timeout += 1
|
533
|
+
retry if timeout < 3
|
534
|
+
|
535
|
+
raise
|
380
536
|
end
|
381
537
|
|
382
538
|
# Public: Create a temporary ssh key to be used for passwordless access to
|
@@ -396,11 +552,9 @@ module CloudFlock; module App
|
|
396
552
|
# location of rsync on the system.
|
397
553
|
#
|
398
554
|
# shell - SSH object logged in to the source host.
|
399
|
-
# exclusions - String containing the exclusions list for the source host.
|
400
555
|
#
|
401
556
|
# Returns a String containing path to rsync on the host if present.
|
402
|
-
def
|
403
|
-
shell.as_root("cat <<EOF> #{EXCLUSIONS}\n#{exclusions}\nEOF")
|
557
|
+
def determine_rsync(shell)
|
404
558
|
shell.as_root('which rsync 2>/dev/null')
|
405
559
|
end
|
406
560
|
|
@@ -410,8 +564,6 @@ module CloudFlock; module App
|
|
410
564
|
# source_shell - SSH object logged in to the source host.
|
411
565
|
# dest_shell - SSH object logged in to the source host.
|
412
566
|
#
|
413
|
-
# Raises NoRsyncAvailable if rsync doesn't exist on the destination host.
|
414
|
-
#
|
415
567
|
# Returns a String.
|
416
568
|
def transfer_rsync(source_shell, dest_shell)
|
417
569
|
host = dest_shell.hostname
|
@@ -436,33 +588,86 @@ module CloudFlock; module App
|
|
436
588
|
#
|
437
589
|
# Returns nothing.
|
438
590
|
def setup_destination(shell, pubkey)
|
591
|
+
prepare_destination_filesystem(shell)
|
592
|
+
prepare_destination_rsync(shell)
|
593
|
+
prepare_destination_pubkey(shell, pubkey)
|
594
|
+
end
|
595
|
+
|
596
|
+
# Public: Mount the destination host's target device and make backups of
|
597
|
+
# authentication-related files (passwd, group, shadow).
|
598
|
+
#
|
599
|
+
# shell - SSH object logged in to the destination host.
|
600
|
+
#
|
601
|
+
# TODO: Dynamic mountpoint/block device support
|
602
|
+
# Presently mount point and block device are hard-coded. This will be
|
603
|
+
# changed in a future release.
|
604
|
+
#
|
605
|
+
# Returns nothing.
|
606
|
+
def prepare_destination_filesystem(shell)
|
439
607
|
preserve_files = ['passwd', 'shadow', 'group']
|
440
608
|
path = "#{MOUNT_POINT}/etc"
|
441
609
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
shell.as_root("mkdir -p #{MOUNT_POINT}")
|
446
|
-
shell.as_root("mount -o acl /dev/xvdb1 #{MOUNT_POINT}")
|
610
|
+
UI.spinner('Preparing the destination filesystem') do
|
611
|
+
shell.as_root("mkdir -p #{MOUNT_POINT}")
|
612
|
+
shell.as_root("mount -o acl /dev/xvdb1 #{MOUNT_POINT}")
|
447
613
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
614
|
+
preserve_files.each do |file|
|
615
|
+
original = "#{path}/#{file}"
|
616
|
+
backup = "#{original}.migration"
|
617
|
+
shell.as_root("[ -f #{backup} ] || /bin/cp -a #{original} #{backup}")
|
618
|
+
end
|
452
619
|
end
|
620
|
+
rescue Timeout::Error
|
621
|
+
retry_exit('Host is slow to respond while preparing the destination.')
|
622
|
+
retry
|
623
|
+
end
|
453
624
|
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
625
|
+
# Public: Verify that rsync is installed on the destination host,
|
626
|
+
# installing needed.
|
627
|
+
#
|
628
|
+
# shell - SSH object logged in to the destination host.
|
629
|
+
#
|
630
|
+
# TODO: Better distro support
|
631
|
+
# Only Debian- and RedHat-based Unix hosts support automatic rsync
|
632
|
+
# installation at this time. This will be fixed in a future release.
|
633
|
+
#
|
634
|
+
# Raises NoRsyncAvailable if rsync doesn't exist on the destination host.
|
635
|
+
#
|
636
|
+
# Returns nothing.
|
637
|
+
def prepare_destination_rsync(shell)
|
638
|
+
UI.spinner('Verifying rsync is present on the destination host') do
|
639
|
+
unless /rsync error/.match(shell.as_root('rsync'))
|
640
|
+
package_manager = shell.as_root('which {yum,apt-get} 2>/dev/null')
|
641
|
+
raise NoRsyncAvailable if package_manager.empty?
|
642
|
+
shell.as_root("#{package_manager} install rsync -y", 300)
|
643
|
+
end
|
461
644
|
end
|
645
|
+
rescue Timeout::Error
|
646
|
+
retry_exit('Host is slow to respond while preparing the destination.')
|
647
|
+
retry
|
648
|
+
end
|
462
649
|
|
463
|
-
|
464
|
-
|
465
|
-
|
650
|
+
# Public: Verify that rsync is installed on the destination host,
|
651
|
+
# installing needed.
|
652
|
+
#
|
653
|
+
# shell - SSH object logged in to the destination host.
|
654
|
+
#
|
655
|
+
# TODO: Better distro support
|
656
|
+
# Only Debian- and RedHat-based Unix hosts support automatic rsync
|
657
|
+
# installation at this time. This will be fixed in a future release.
|
658
|
+
#
|
659
|
+
# Raises NoRsyncAvailable if rsync doesn't exist on the destination host.
|
660
|
+
#
|
661
|
+
# Returns nothing.
|
662
|
+
def prepare_destination_pubkey(shell, pubkey)
|
663
|
+
UI.spinner('Installing source host public key') do
|
664
|
+
ssh_key = "mkdir $HOME/.ssh; chmod 0700 $HOME/.ssh; printf " +
|
665
|
+
"'#{pubkey}\\n' >> $HOME/.ssh/authorized_keys"
|
666
|
+
shell.as_root(ssh_key)
|
667
|
+
end
|
668
|
+
rescue Timeout::Error
|
669
|
+
retry_exit('Host is slow to respond while preparing the destination.')
|
670
|
+
retry
|
466
671
|
end
|
467
672
|
|
468
673
|
# Public: Determine what address should be used when connecting from source
|
@@ -659,14 +864,27 @@ module CloudFlock; module App
|
|
659
864
|
end
|
660
865
|
end
|
661
866
|
|
867
|
+
# Public: Wrap retry_prompt, exiting the application if the prompt is
|
868
|
+
# declined.
|
869
|
+
#
|
870
|
+
# message - String containing a failure message.
|
871
|
+
# prompt - Prompt to present to the user (default: 'Try again? (Y/N)').
|
872
|
+
#
|
873
|
+
# Returns false, or exits.
|
874
|
+
def retry_exit(message, prompt = 'Try again? (Y/N)')
|
875
|
+
error = UI.red { "#{message} #{prompt}" }
|
876
|
+
exit unless UI.prompt_yn(error, default_answer: 'Y')
|
877
|
+
end
|
878
|
+
|
662
879
|
# Public: Display a failure message to the user and prompt whether to
|
663
880
|
# retry.
|
664
881
|
#
|
665
882
|
# message - String containing a failure message.
|
883
|
+
# prompt - Prompt to present to the user (default: 'Try again? (Y/N)').
|
666
884
|
#
|
667
885
|
# Returns true or false indicating whether the user wishes to retry.
|
668
|
-
def retry_prompt(message)
|
669
|
-
error = UI.red { "#{message}
|
886
|
+
def retry_prompt(message, prompt = 'Try again? (Y/N)')
|
887
|
+
error = UI.red { "#{message} #{prompt}".strip }
|
670
888
|
UI.prompt_yn(error, default_answer: 'Y')
|
671
889
|
end
|
672
890
|
end
|
@@ -25,7 +25,6 @@ module CloudFlock; module App
|
|
25
25
|
exclusions = build_exclusions(profile.cpe)
|
26
26
|
migrate_server(source_host, dest_host, exclusions)
|
27
27
|
|
28
|
-
source_host.logout!
|
29
28
|
cleanup_destination(dest_host, profile.cpe)
|
30
29
|
configure_ips(dest_host, profile)
|
31
30
|
|
@@ -263,6 +262,11 @@ module CloudFlock; module App
|
|
263
262
|
'SSH identity to use for the source host') do |key|
|
264
263
|
options[:ssh_key] = key
|
265
264
|
end
|
265
|
+
|
266
|
+
opts.on('--src-identity-password PASSWORD',
|
267
|
+
"Password to unlock the source host's SSH key") do |pass|
|
268
|
+
options[:passphrase] = key
|
269
|
+
end
|
266
270
|
end
|
267
271
|
|
268
272
|
opts.separator ''
|
@@ -306,6 +310,11 @@ module CloudFlock; module App
|
|
306
310
|
'SSH identity to use for the destination host') do |key|
|
307
311
|
options[:dest_ssh_key] = key
|
308
312
|
end
|
313
|
+
|
314
|
+
opts.on('--dest-identity-password PASSWORD',
|
315
|
+
"Password to unlock the destination host's SSH key") do |pass|
|
316
|
+
options[:dest_passphrase] = key
|
317
|
+
end
|
309
318
|
end
|
310
319
|
|
311
320
|
opts.separator ''
|
@@ -12,12 +12,11 @@ module CloudFlock; module App
|
|
12
12
|
# Public: Connect to and profile a remote host, then display the gathered
|
13
13
|
# information.
|
14
14
|
def initialize
|
15
|
-
options
|
15
|
+
options = parse_options
|
16
|
+
source_host = options.dup
|
16
17
|
|
17
18
|
source_host = define_source(options)
|
18
|
-
source_ssh
|
19
|
-
SSH.new(source_host)
|
20
|
-
end
|
19
|
+
source_ssh = connect_source(source_host)
|
21
20
|
|
22
21
|
profile = UI.spinner("Checking source host") do
|
23
22
|
CloudFlock::Task::ServerProfile.new(source_ssh)
|
data/lib/cloudflock/app.rb
CHANGED
@@ -95,9 +95,10 @@ module CloudFlock
|
|
95
95
|
opts.separator ''
|
96
96
|
opts.separator 'Global Options:'
|
97
97
|
|
98
|
-
|
99
|
-
|
100
|
-
|
98
|
+
# TODO: Add config file support.
|
99
|
+
# opts.on('-c', '--config FILE', 'Specify configuration file') do |file|
|
100
|
+
# options[:config_file] = File.expand_path(file)
|
101
|
+
# end
|
101
102
|
|
102
103
|
opts.on_tail('--version', 'Show Version Information') do
|
103
104
|
puts "CloudFlock v#{CloudFlock::VERSION}"
|
data/lib/cloudflock/errstr.rb
CHANGED
@@ -2,7 +2,7 @@ module CloudFlock
|
|
2
2
|
module App
|
3
3
|
module Common
|
4
4
|
module Errstr
|
5
|
-
NO_RSYNC = 'Cannot find rsync on the destination host'
|
5
|
+
NO_RSYNC = 'Cannot find rsync on the destination host.'
|
6
6
|
end
|
7
7
|
end
|
8
8
|
end
|
@@ -10,9 +10,9 @@ module CloudFlock
|
|
10
10
|
module Remote
|
11
11
|
class SSH
|
12
12
|
module Errstr
|
13
|
-
NOHOST = 'No host specified'
|
14
|
-
INVALID_HOST = 'Unable to look up host: %s'
|
15
|
-
CANNOT_CONNECT = 'Unable to connect to host: %s'
|
13
|
+
NOHOST = 'No host specified.'
|
14
|
+
INVALID_HOST = 'Unable to look up host: %s.'
|
15
|
+
CANNOT_CONNECT = 'Unable to connect to host: %s.'
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -20,12 +20,12 @@ module CloudFlock
|
|
20
20
|
module Target
|
21
21
|
module Servers
|
22
22
|
class Profile
|
23
|
-
NOT_SSH = 'SSH session expected'
|
23
|
+
NOT_SSH = 'SSH session expected.'
|
24
24
|
end
|
25
25
|
|
26
26
|
class Platform
|
27
|
-
NOT_CPE = 'Expected a CPE object'
|
28
|
-
CPE_INCOMPLETE = 'CPE must contain at least vendor and version'
|
27
|
+
NOT_CPE = 'Expected a CPE object.'
|
28
|
+
CPE_INCOMPLETE = 'CPE must contain at least vendor and version.'
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -42,18 +42,17 @@ module CloudFlock; module Remote
|
|
42
42
|
#
|
43
43
|
# args - Hash containing arguments relating to the SSH session. (default
|
44
44
|
# defined in DEFAULT_ARGS):
|
45
|
-
# :hostname
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
# (default: '')
|
45
|
+
# :hostname - String containing the address of the remote host.
|
46
|
+
# :username - String containing the remote user with which to log
|
47
|
+
# in. (default: '')
|
48
|
+
# :password - String containing the password with which to log in.
|
49
|
+
# (default: '')
|
50
|
+
# :port - Fixnum specifying the port to which to connect.
|
51
|
+
# (default: 22)
|
52
|
+
# :ssh_key - String containing the path to an ssh private key.
|
53
|
+
# (default: '')
|
54
|
+
# :passphrase - The passphrase for the ssh key if applicable.
|
55
|
+
# (default: '')
|
57
56
|
#
|
58
57
|
# Raises InvalidHostname if host lookup fails.
|
59
58
|
# Raises LoginFailed if logging into the host fails.
|
@@ -192,18 +191,17 @@ module CloudFlock; module Remote
|
|
192
191
|
# Internal: Sanitize arguments to be used
|
193
192
|
#
|
194
193
|
# args - Hash containing arguments relating to the SSH session:
|
195
|
-
# :host
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
# (default: '')
|
194
|
+
# :host - String containing the address of the remote host.
|
195
|
+
# :username - String containing the remote user with which to log
|
196
|
+
# in. (default: '')
|
197
|
+
# :password - String containing the password with which to log in.
|
198
|
+
# (default: '')
|
199
|
+
# :port - Fixnum specifying the port to which to connect.
|
200
|
+
# (default: 22)
|
201
|
+
# :ssh_key - String containing the path to an ssh private key.
|
202
|
+
# (default: '')
|
203
|
+
# :passphrase - The passphrase for the ssh key if applicable.
|
204
|
+
# (default: '')
|
207
205
|
#
|
208
206
|
# Returns a Hash containing sanitized arguments suitable for passing to
|
209
207
|
# Net::SSH. Not that #filter_ssh_arguments should be called to guarantee
|
@@ -237,9 +235,9 @@ module CloudFlock; module Remote
|
|
237
235
|
# args - Hash containing arguments to filter.
|
238
236
|
#
|
239
237
|
# Returns a Hash with only the keys :port, :password, :key_data and
|
240
|
-
# :
|
238
|
+
# :passphrase defined.
|
241
239
|
def filter_ssh_options(args)
|
242
|
-
valid_arguments = [:port, :password, :key_data, :
|
240
|
+
valid_arguments = [:port, :password, :key_data, :passphrase]
|
243
241
|
args.select { |opt| valid_arguments.include? opt }.merge(NET_SSH_OPTIONS)
|
244
242
|
end
|
245
243
|
|
data/lib/cloudflock.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudflock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Tina Wuest
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fog-json
|
@@ -85,7 +85,7 @@ dependencies:
|
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
version: 0.2.1
|
87
87
|
description: CloudFlock is a library and toolchain focused on migration
|
88
|
-
email:
|
88
|
+
email: tina@wuest.me
|
89
89
|
executables:
|
90
90
|
- cloudflock
|
91
91
|
- cloudflock-files
|