CloudyScripts 2.14.62 → 2.14.63
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/Rakefile +6 -5
- data/lib/help/ec2_helper.rb +8 -2
- data/lib/help/helper.rb +9 -0
- data/lib/help/remote_command_handler.rb +11 -4
- data/lib/help/state_transition_helper.rb +29 -23
- data/lib/scripts/ec2/ami2_ebs_conversion.rb +29 -5
- data/lib/scripts/ec2/copy_ami.rb +55 -4
- data/lib/scripts/ec2/copy_ami_test.rb +449 -0
- data/lib/scripts/ec2/copy_mswindows_ami.rb +133 -26
- data/lib/scripts/ec2/copy_mswindows_snapshot.rb +28 -4
- data/lib/scripts/ec2/copy_snapshot.rb +26 -4
- data/lib/scripts/ec2/download_snapshot.rb +29 -8
- data/lib/scripts/ec2/ec2_script.rb +2 -0
- metadata +10 -6
data/Rakefile
CHANGED
@@ -12,16 +12,17 @@ require 'rake/testtask'
|
|
12
12
|
|
13
13
|
spec = Gem::Specification.new do |s|
|
14
14
|
s.name = 'CloudyScripts'
|
15
|
-
|
16
|
-
s.version = '2.14.62' #<number cloud-stacks supported>.<number cloud-scripts>.<counting releases>
|
15
|
+
s.version = '2.14.63' #<number cloud-stacks supported>.<number cloud-scripts>.<counting releases>
|
17
16
|
s.has_rdoc = true
|
18
17
|
s.extra_rdoc_files = ['README.rdoc', 'LICENSE']
|
19
18
|
s.summary = 'Scripts to facilitate programming for infrastructure clouds.'
|
20
19
|
s.description = s.summary
|
21
20
|
s.homepage = "http://elastic-security.com"
|
22
|
-
s.rubyforge_project = "
|
23
|
-
s.author = 'Matthias Jung'
|
24
|
-
s.
|
21
|
+
s.rubyforge_project = "CloudyScripts"
|
22
|
+
#s.author = 'Matthias Jung'
|
23
|
+
s.authors = ['Matthias Jung', 'Frederic Donnat']
|
24
|
+
#s.email = 'matthias.jung@gmail.com'
|
25
|
+
s.email = ['matthias.jung@gmail.com', 'fred@secludit.com']
|
25
26
|
# s.executables = ['your_executable_here']
|
26
27
|
s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
|
27
28
|
s.require_path = "lib"
|
data/lib/help/ec2_helper.rb
CHANGED
@@ -130,7 +130,7 @@ class Ec2Helper
|
|
130
130
|
end
|
131
131
|
return amis['imagesSet']['item'][0][prop.to_s]
|
132
132
|
rescue
|
133
|
-
|
133
|
+
raise Exception.new("image #{ami_id} not found")
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
@@ -146,7 +146,7 @@ class Ec2Helper
|
|
146
146
|
end
|
147
147
|
return amis['imagesSet']['item'][0]['blockDeviceMapping']['item'][0]['ebs'][prop.to_s]
|
148
148
|
rescue
|
149
|
-
|
149
|
+
raise Exception.new("image #{ami_id} not found")
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
@@ -264,4 +264,10 @@ class Ec2Helper
|
|
264
264
|
instance_info['instancesSet']['item'][0][prop.to_s]
|
265
265
|
end
|
266
266
|
|
267
|
+
# Check if keypair exist
|
268
|
+
def check_keypair(keypair_name)
|
269
|
+
puts "check if '#{keypair_name}' keypair exists"
|
270
|
+
@ec2_api.describe_keypairs(:key_name => keypair_name)
|
271
|
+
end
|
272
|
+
|
267
273
|
end
|
data/lib/help/helper.rb
CHANGED
@@ -35,3 +35,12 @@ def check_aws_desc(str)
|
|
35
35
|
return false
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
# Check SSH key for connecting AWS instance
|
40
|
+
# Constraints: A RSA Private Key
|
41
|
+
def check_ssh_key(key)
|
42
|
+
priv_key = OpenSSL::PKey::RSA.new(key)
|
43
|
+
if !priv_key.private?()
|
44
|
+
raise Exception.new("Invalid SSH Key")
|
45
|
+
end
|
46
|
+
end
|
@@ -69,6 +69,11 @@ class RemoteCommandHandler
|
|
69
69
|
get_output("uname -r").strip
|
70
70
|
end
|
71
71
|
|
72
|
+
# return teh result of mount
|
73
|
+
def mount_output()
|
74
|
+
get_output("mount").strip
|
75
|
+
end
|
76
|
+
|
72
77
|
# Return all the partitions of a device
|
73
78
|
def get_device_partition(device)
|
74
79
|
get_output("ls #{device}*").strip
|
@@ -79,16 +84,18 @@ class RemoteCommandHandler
|
|
79
84
|
get_output("sfdisk -d #{device}")
|
80
85
|
end
|
81
86
|
|
82
|
-
#
|
87
|
+
# Set the partition table of a device
|
83
88
|
def set_partition_table(device, partition_table)
|
84
89
|
push_data = "\"" + partition_table.gsub(/\/dev\/(s|xv)d[a-z]/, "#{device}") + "\""
|
85
90
|
remote_execute("sfdisk -f #{device}", push_data, nil)
|
86
91
|
end
|
87
92
|
|
88
93
|
# Get root partition label
|
94
|
+
# NB: add '/dev' detection in order to avoid entries containing 'rootfs'
|
89
95
|
def get_root_device()
|
90
96
|
#get_output("cat /etc/mtab | grep -E '[[:blank:]]+\/[[:blank:]]+' | cut -d ' ' -f 1").strip
|
91
|
-
get_output("mount | grep -E '[[:blank:]]+\/[[:blank:]]+' | cut -d ' ' -f 1").strip
|
97
|
+
#get_output("mount | grep -E '[[:blank:]]+\/[[:blank:]]+' | cut -d ' ' -f 1").strip
|
98
|
+
get_output("mount | grep -E '[[:blank:]]+\/[[:blank:]]+' | grep -E '\/dev' | cut -d ' ' -f 1").strip
|
92
99
|
end
|
93
100
|
|
94
101
|
# Get the device of a specific partition
|
@@ -385,9 +392,9 @@ class RemoteCommandHandler
|
|
385
392
|
if sudo
|
386
393
|
channel.request_pty do |ch, success|
|
387
394
|
if success
|
388
|
-
@logger.debug("pty successfully obtained")
|
395
|
+
@logger.debug("pty successfully obtained") if debug
|
389
396
|
else
|
390
|
-
@logger.debug("could not obtain pty")
|
397
|
+
@logger.debug("could not obtain pty") if debug
|
391
398
|
end
|
392
399
|
end
|
393
400
|
end
|
@@ -39,11 +39,11 @@ module StateTransitionHelper
|
|
39
39
|
# Returns:
|
40
40
|
# * OS of the connected machine
|
41
41
|
def connect(dns_name, user_name, ssh_keyfile = nil, ssh_keydata = nil,
|
42
|
-
trials = 10, wait_between_trials =
|
42
|
+
trials = 10, wait_between_trials = 30)
|
43
43
|
post_message("connecting '#{user_name}' to #{dns_name}...")
|
44
44
|
connected = false
|
45
45
|
last_connection_problem = ""
|
46
|
-
remaining_trials = trials
|
46
|
+
remaining_trials = trials
|
47
47
|
while !connected && remaining_trials > 0
|
48
48
|
remaining_trials -= 1
|
49
49
|
if ssh_keyfile != nil
|
@@ -409,8 +409,8 @@ module StateTransitionHelper
|
|
409
409
|
done = true
|
410
410
|
timeout = 0
|
411
411
|
end
|
412
|
-
sleep(
|
413
|
-
timeout -=
|
412
|
+
sleep(10)
|
413
|
+
timeout -= 10
|
414
414
|
end
|
415
415
|
msg = ""
|
416
416
|
if !done
|
@@ -444,8 +444,8 @@ module StateTransitionHelper
|
|
444
444
|
done = true
|
445
445
|
timeout = 0
|
446
446
|
end
|
447
|
-
sleep(
|
448
|
-
timeout -=
|
447
|
+
sleep(10)
|
448
|
+
timeout -= 10
|
449
449
|
end
|
450
450
|
msg = ""
|
451
451
|
if !done
|
@@ -563,23 +563,26 @@ module StateTransitionHelper
|
|
563
563
|
return image_id
|
564
564
|
end
|
565
565
|
|
566
|
+
# Create a Security group with specific rules
|
567
|
+
# NB: - if the Security Group already exists add teh required rules if not present
|
568
|
+
# - do not try to delete the Security Group because this will disallow multi-tasks and parallelization
|
566
569
|
def create_security_group_with_rules(name, desc, rules)
|
567
570
|
post_message("going to create '#{name}' Security Group...")
|
568
571
|
@logger.debug "create Security Group (name: #{name}, desc: #{desc})"
|
569
572
|
begin
|
570
|
-
res = ec2_handler().
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
573
|
+
res = ec2_handler().create_security_group(:group_name => name, :group_description => desc)
|
574
|
+
rescue AWS::InvalidGroupDuplicate => e
|
575
|
+
@logger.warn "'#{name}' Security Group already exists: #{e.to_s}"
|
576
|
+
ensure
|
577
|
+
rules.each(){ |rule|
|
578
|
+
begin
|
579
|
+
ec2_handler().authorize_security_group_ingress(:group_name => name,
|
580
|
+
:ip_protocol => rule[:ip_protocol], :from_port => rule[:from_port], :to_port => rule[:to_port], :cidr_ip => rule[:cidr_ip])
|
581
|
+
rescue AWS::InvalidPermissionDuplicate => e
|
582
|
+
@logger.warn "'#{rule[:proto].upcase()} (#{rule[:from_port]}-#{rule[:to_port]})' rule already exists: #{e.to_s}"
|
583
|
+
end
|
584
|
+
}
|
577
585
|
end
|
578
|
-
res = ec2_handler().create_security_group(:group_name => name, :group_description => desc)
|
579
|
-
rules.each(){ |rule|
|
580
|
-
ec2_handler().authorize_security_group_ingress(:group_name => name,
|
581
|
-
:ip_protocol => rule[:ip_protocol], :from_port => rule[:from_port], :to_port => rule[:to_port], :cidr_ip => rule[:cidr_ip])
|
582
|
-
}
|
583
586
|
return true
|
584
587
|
end
|
585
588
|
|
@@ -605,9 +608,9 @@ module StateTransitionHelper
|
|
605
608
|
@logger.debug "create filesystem on #{dns_name} to #{device}"
|
606
609
|
status = remote_handler().create_filesystem("ext3", device)
|
607
610
|
if status == false
|
608
|
-
raise Exception.new("
|
611
|
+
raise Exception.new("Failed to create ext3 filesystem on #{device} device on #{dns_name}")
|
609
612
|
end
|
610
|
-
post_message("filesystem system successfully created")
|
613
|
+
post_message("filesystem system successfully created on device #{device}")
|
611
614
|
end
|
612
615
|
|
613
616
|
# Create a file-system on a given machine (assumes to be connected already).
|
@@ -626,7 +629,7 @@ module StateTransitionHelper
|
|
626
629
|
@logger.debug "create '#{fs_type}' filesystem on device '#{device}'"
|
627
630
|
status = remote_handler().create_filesystem(fs_type, device)
|
628
631
|
if status == false
|
629
|
-
raise Exception.new("
|
632
|
+
raise Exception.new("Failed to create #{type} filesystem on #{device} device on #{dns_name}")
|
630
633
|
end
|
631
634
|
post_message("#{fs_type} filesystem system successfully created on device #{device}")
|
632
635
|
if !label.nil? && !label.empty?
|
@@ -635,7 +638,7 @@ module StateTransitionHelper
|
|
635
638
|
if remote_handler().set_device_label_ext(device, label, fs_type)
|
636
639
|
post_message("label #{label} added to device #{device}")
|
637
640
|
else
|
638
|
-
raise Exception.new("
|
641
|
+
raise Exception.new("Failed to add label #{label} to device #{device}")
|
639
642
|
end
|
640
643
|
end
|
641
644
|
end
|
@@ -810,6 +813,9 @@ module StateTransitionHelper
|
|
810
813
|
# Get root partition
|
811
814
|
def get_root_device_name()
|
812
815
|
post_message("Retrieving '/' root device name...")
|
816
|
+
@logger.debug "issuing very first mount..."
|
817
|
+
mount_output = remote_handler().mount_output()
|
818
|
+
@logger.debug "mount output:\n#{mount_output}"
|
813
819
|
@logger.debug "get root device name"
|
814
820
|
root_device = remote_handler().get_root_device()
|
815
821
|
@logger.debug "Found '#{root_device}' as root device"
|
@@ -994,7 +1000,7 @@ module StateTransitionHelper
|
|
994
1000
|
post_message("going to zip the EBS volume")
|
995
1001
|
stderr = remote_handler().zip(source_dir, zip_file_dest+"/"+zip_file_name)
|
996
1002
|
if stderr.size > 0
|
997
|
-
@logger.
|
1003
|
+
@logger.warn("zip operation generated error and might not be complete. output: #{stderr.join("\n")}")
|
998
1004
|
post_message("zip operation generated error and might not be complete. output: #{stderr.join("\n")}")
|
999
1005
|
end
|
1000
1006
|
post_message("EBS volume successfully zipped")
|
@@ -4,6 +4,7 @@ require "help/remote_command_handler"
|
|
4
4
|
#require "help/dm_crypt_helper"
|
5
5
|
require "help/ec2_helper"
|
6
6
|
require "AWS"
|
7
|
+
require "help/helper"
|
7
8
|
|
8
9
|
# Creates a bootable EBS storage from an existing AMI.
|
9
10
|
#
|
@@ -31,15 +32,23 @@ class Ami2EbsConversion < Ec2Script
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def check_input_parameters()
|
34
|
-
if @input_params[:security_group_name] == nil
|
35
|
-
@input_params[:security_group_name] = "default"
|
36
|
-
end
|
37
35
|
if @input_params[:ami_id] == nil && !(@input_params[:ami_id] =~ /^ami-.*$/)
|
38
36
|
raise Exception.new("Invalid AMI ID specified: #{@input_params[:ami_id]}")
|
39
37
|
end
|
40
38
|
ec2_helper = Ec2Helper.new(@input_params[:ec2_api_handler])
|
39
|
+
# AWS Security Group
|
40
|
+
if @input_params[:security_group_name] == nil
|
41
|
+
@input_params[:security_group_name] = "default"
|
42
|
+
end
|
41
43
|
if !ec2_helper.check_open_port(@input_params[:security_group_name], 22)
|
42
|
-
|
44
|
+
post_message("'#{@input_params[:security_group_name]}' Security Group not opened port 22 for connect via SSH in source region")
|
45
|
+
@input_params[:security_group_name] = nil
|
46
|
+
end
|
47
|
+
# AWS KeyPair
|
48
|
+
if @input_params[:key_name] == nil || @input_params[:key_name].empty?()
|
49
|
+
raise Exception.new("No KeyPair name specified")
|
50
|
+
else
|
51
|
+
ec2_helper.check_keypair(@input_params[:key_name])
|
43
52
|
end
|
44
53
|
if @input_params[:name] == nil
|
45
54
|
@input_params[:name] = "Boot EBS (for AMI #{@input_params[:ami_id]}) at #{Time.now.strftime('%d/%m/%Y %H.%M.%S')}"
|
@@ -53,9 +62,16 @@ class Ami2EbsConversion < Ec2Script
|
|
53
62
|
if @input_params[:root_device_name] == nil
|
54
63
|
@input_params[:root_device_name] = "/dev/sda1"
|
55
64
|
end
|
65
|
+
# SSH Parameters
|
56
66
|
if @input_params[:ssh_username] == nil
|
57
67
|
@input_params[:ssh_username] = "root"
|
58
68
|
end
|
69
|
+
if @input_params[:ssh_keydata] == nil
|
70
|
+
raise Exception.new("No Private Key for source region")
|
71
|
+
else
|
72
|
+
post_message("Checking SSH key for source region...")
|
73
|
+
check_ssh_key(@input_params[:ssh_keydata])
|
74
|
+
end
|
59
75
|
if @input_params[:connect_trials] == nil
|
60
76
|
@input_params[:connect_trials] = 6
|
61
77
|
end
|
@@ -84,6 +100,14 @@ class Ami2EbsConversion < Ec2Script
|
|
84
100
|
class InitialState < Ami2EbsConversionState
|
85
101
|
def enter
|
86
102
|
puts "DEBUG: params: #{@context[:ami_id]}, #{@context[:key_name]}, #{@context[:security_group_name]}"
|
103
|
+
#XXX: create a CloudyScripts Security Group with TCP port 22 publicly opened
|
104
|
+
if @context[:security_group_name] == nil
|
105
|
+
@context[:security_group_name] = Ec2Script::CS_SEC_GRP_NAME
|
106
|
+
create_security_group_with_rules(@context[:security_group_name], Ec2Script::CS_SEC_GRP_DESC,
|
107
|
+
[{:ip_protocol => "tcp", :from_port => 22, :to_port => 22, :cidr_ip => "0.0.0.0/0"}])
|
108
|
+
post_message("'#{@context[:security_group_name]}' Security Group created with TCP port 22 publicly opened.")
|
109
|
+
end
|
110
|
+
|
87
111
|
@context[:instance_id], @context[:dns_name], @context[:availability_zone],
|
88
112
|
@context[:kernel_id], @context[:ramdisk_id], @context[:architecture] =
|
89
113
|
launch_instance(@context[:ami_id], @context[:key_name], @context[:security_group_name])
|
@@ -102,7 +126,7 @@ class Ami2EbsConversion < Ec2Script
|
|
102
126
|
# Storage created. Attach it.
|
103
127
|
class StorageCreated < Ami2EbsConversionState
|
104
128
|
def enter
|
105
|
-
attach_volume(@context[:volume_id], @context[:instance_id], @context[:temp_device_name])
|
129
|
+
attach_volume(@context[:volume_id], @context[:instance_id], @context[:temp_device_name], Ec2Script::CS_AWS_TIMEOUT)
|
106
130
|
StorageAttached.new(@context)
|
107
131
|
end
|
108
132
|
end
|
data/lib/scripts/ec2/copy_ami.rb
CHANGED
@@ -40,7 +40,8 @@ class CopyAmi < Ec2Script
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def check_input_parameters()
|
43
|
-
|
43
|
+
post_message("Checking parameters...")
|
44
|
+
if @input_params[:ami_id] == nil || !(@input_params[:ami_id] =~ /^ami-.*$/)
|
44
45
|
raise Exception.new("Invalid AMI ID specified: #{@input_params[:ami_id]}")
|
45
46
|
end
|
46
47
|
ec2_helper = Ec2Helper.new(@input_params[:ec2_api_handler])
|
@@ -48,6 +49,29 @@ class CopyAmi < Ec2Script
|
|
48
49
|
raise Exception.new("must be an EBS type image")
|
49
50
|
end
|
50
51
|
local_ec2_helper = ec2_helper
|
52
|
+
remote_ec2_helper = Ec2Helper.new(@input_params[:target_ec2_handler])
|
53
|
+
# AWS KeyPair
|
54
|
+
if @input_params[:source_key_name] == nil || @input_params[:source_key_name].empty?()
|
55
|
+
raise Exception.new("No KeyPair name specified for source region")
|
56
|
+
else
|
57
|
+
begin
|
58
|
+
local_ec2_helper.check_keypair(@input_params[:source_key_name])
|
59
|
+
rescue Exception => e
|
60
|
+
post_message("'#{@input_params[:source_key_name]}' Key pair not found in source region")
|
61
|
+
raise Exception.new("source region: #{e.to_s}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
if @input_params[:target_key_name] == nil || @input_params[:target_key_name].empty?()
|
65
|
+
raise Exception.new("No KeyPair name specified for target region")
|
66
|
+
else
|
67
|
+
begin
|
68
|
+
remote_ec2_helper.check_keypair(@input_params[:target_key_name])
|
69
|
+
rescue Exception => e
|
70
|
+
post_message("'#{@input_params[:target_key_name]}' Key pair not found in target region")
|
71
|
+
raise Exception.new("target region: #{e.to_s}")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
# AWS SecurityGroup
|
51
75
|
if @input_params[:source_security_group] == nil
|
52
76
|
@input_params[:source_security_group] = "default"
|
53
77
|
end
|
@@ -57,7 +81,6 @@ class CopyAmi < Ec2Script
|
|
57
81
|
else
|
58
82
|
post_message("'#{@input_params[:source_security_group]}' Security Group opened port 22 for connect via SSH in source region")
|
59
83
|
end
|
60
|
-
remote_ec2_helper = Ec2Helper.new(@input_params[:target_ec2_handler])
|
61
84
|
if @input_params[:target_security_group] == nil
|
62
85
|
@input_params[:target_security_group] = "default"
|
63
86
|
end
|
@@ -67,18 +90,41 @@ class CopyAmi < Ec2Script
|
|
67
90
|
else
|
68
91
|
post_message("'#{@input_params[:target_security_group]}' Security Group opened port 22 for connect via SSH in target region")
|
69
92
|
end
|
93
|
+
# Device to use
|
70
94
|
if @input_params[:root_device_name] == nil
|
71
95
|
@input_params[:root_device_name] = "/dev/sda1"
|
72
96
|
end
|
73
97
|
if @input_params[:temp_device_name] == nil
|
74
98
|
@input_params[:temp_device_name] = "/dev/sdj"
|
75
99
|
end
|
100
|
+
# SSH Parameters
|
76
101
|
if @input_params[:source_ssh_username] == nil
|
77
102
|
@input_params[:source_ssh_username] = "root"
|
78
103
|
end
|
79
104
|
if @input_params[:target_ssh_username] == nil
|
80
105
|
@input_params[:target_ssh_username] = "root"
|
81
106
|
end
|
107
|
+
if @input_params[:source_ssh_keydata] == nil
|
108
|
+
raise Exception.new("No Private Key for source region")
|
109
|
+
else
|
110
|
+
begin
|
111
|
+
check_ssh_key(@input_params[:source_ssh_keydata])
|
112
|
+
rescue Exception => e
|
113
|
+
post_message("not a Private Key: #{e.to_s}")
|
114
|
+
raise Exception.new("Invalid Private Key for source region: #{e.to_s}")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
if @input_params[:target_ssh_keydata] == nil
|
118
|
+
raise Exception.new("No Private Key for target region")
|
119
|
+
else
|
120
|
+
begin
|
121
|
+
check_ssh_key(@input_params[:target_ssh_keydata])
|
122
|
+
rescue Exception => e
|
123
|
+
post_message("not a Private Key: #{e.to_s}")
|
124
|
+
raise Exception.new("Invalid Private Key for target region: #{e.to_s}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
# AWS Name and Description
|
82
128
|
if @input_params[:description] == nil || !check_aws_desc(@input_params[:description])
|
83
129
|
@input_params[:description] = "Created by CloudyScripts - #{self.class.name}"
|
84
130
|
end
|
@@ -150,7 +196,8 @@ class CopyAmi < Ec2Script
|
|
150
196
|
@context[:source_availability_zone])
|
151
197
|
device = @context[:temp_device_name]
|
152
198
|
mount_point = "/mnt/tmp_#{@context[:source_volume_id]}"
|
153
|
-
|
199
|
+
#XXX: attach volume after root partition detection
|
200
|
+
#attach_volume(@context[:source_volume_id], @context[:source_instance_id], device)
|
154
201
|
connect(@context[:source_dns_name], @context[:source_ssh_username], nil, @context[:source_ssh_keydata])
|
155
202
|
# detect if there is a shift for device mapping (between AWS and the operating system of the system)
|
156
203
|
root_device_name = get_root_device_name()
|
@@ -167,6 +214,9 @@ class CopyAmi < Ec2Script
|
|
167
214
|
aws_device_letter.succ!
|
168
215
|
end
|
169
216
|
|
217
|
+
# attach volume
|
218
|
+
attach_volume(@context[:source_volume_id], @context[:source_instance_id], device, Ec2Script::CS_AWS_TIMEOUT)
|
219
|
+
|
170
220
|
device = "/dev/sd#{aws_device_letter}"
|
171
221
|
# detect root partition vs root volume: simply check if we have several /dev/sdx* entries
|
172
222
|
parts_count = get_partition_count(device)
|
@@ -229,7 +279,7 @@ class CopyAmi < Ec2Script
|
|
229
279
|
@context[:target_volume_id] = create_volume(@context[:target_availability_zone], volume_size)
|
230
280
|
device = @context[:temp_device_name]
|
231
281
|
mount_point = "/mnt/tmp_#{@context[:target_volume_id]}"
|
232
|
-
attach_volume(@context[:target_volume_id], @context[:target_instance_id], device)
|
282
|
+
attach_volume(@context[:target_volume_id], @context[:target_instance_id], device, Ec2Script::CS_AWS_TIMEOUT)
|
233
283
|
connect(@context[:target_dns_name], @context[:target_ssh_username], nil, @context[:target_ssh_keydata])
|
234
284
|
# check if we need to create a partition table
|
235
285
|
if !(@context[:partition_table] == nil)
|
@@ -332,6 +382,7 @@ class CopyAmi < Ec2Script
|
|
332
382
|
# in the both regions.
|
333
383
|
class AmiRegisteredState < CopyAmiState
|
334
384
|
def enter()
|
385
|
+
post_message("Cleaning Source and Target Regions...")
|
335
386
|
error = []
|
336
387
|
local_region()
|
337
388
|
begin
|
@@ -0,0 +1,449 @@
|
|
1
|
+
require "help/script_execution_state"
|
2
|
+
require "scripts/ec2/ec2_script"
|
3
|
+
require "help/remote_command_handler"
|
4
|
+
require "help/dm_crypt_helper"
|
5
|
+
require "help/ec2_helper"
|
6
|
+
require "AWS"
|
7
|
+
require "help/helper"
|
8
|
+
|
9
|
+
|
10
|
+
# Copy a given snapshot to another region
|
11
|
+
# * start up instance in source-region, create a snapshot from the mounted EBS
|
12
|
+
# * then create volume from snapshot, attach volume, and mount it
|
13
|
+
# * start up instance in destination-region, create empty volume of same size, attache volume, and mount it
|
14
|
+
# * copy the destination key to the source instance
|
15
|
+
# * perform an rsynch
|
16
|
+
# sync -PHAXaz --rsh "ssh -i /home/${src_user}/.ssh/id_${dst_keypair}" --rsync-path "sudo rsync" ${src_dir}/ ${dst_user}@${dst_public_fqdn}:${dst_dir}/
|
17
|
+
# * create a snapshot of the volume
|
18
|
+
# * register the snapshot as AMI
|
19
|
+
# * clean-up everything
|
20
|
+
|
21
|
+
class CopyAmiTest < Ec2Script
|
22
|
+
# context information needed
|
23
|
+
# * the EC2 credentials (see #Ec2Script)
|
24
|
+
# * ami_id => the ID of the AMI to be copied in another region
|
25
|
+
# * target_ec2_handler => The EC2 handler connected to the region where the snapshot is being copied to
|
26
|
+
# * source_ssh_username => The username for ssh for source-instance (default = root)
|
27
|
+
# * source_key_name => Key name of the instance that manages the snaphot-volume in the source region
|
28
|
+
# * source_ssh_key_data => Key information for the security group that starts the AMI [if not set, use ssh_key_files]
|
29
|
+
# * source_ssh_key_files => Key information for the security group that starts the AMI
|
30
|
+
# * target_ssh_username => The username for ssh for target-instance (default = root)
|
31
|
+
# * target_key_name => Key name of the instance that manages the snaphot-volume in the target region
|
32
|
+
# * target_ssh_key_data => Key information for the security group that starts the AMI [if not set, use ssh_key_files]
|
33
|
+
# * target_ssh_key_files => Key information for the security group that starts the AMI
|
34
|
+
# * target_ami_id => ID of the AMI to start in the target region
|
35
|
+
# * name => name of new AMI to be created
|
36
|
+
# * description => description of new AMI to be created
|
37
|
+
|
38
|
+
def initialize(input_params)
|
39
|
+
super(input_params)
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_input_parameters()
|
43
|
+
post_message("Checking parameters...")
|
44
|
+
if @input_params[:ami_id] == nil || !(@input_params[:ami_id] =~ /^ami-.*$/)
|
45
|
+
raise Exception.new("Invalid AMI ID specified: #{@input_params[:ami_id]}")
|
46
|
+
end
|
47
|
+
ec2_helper = Ec2Helper.new(@input_params[:ec2_api_handler])
|
48
|
+
if ec2_helper.ami_prop(@input_params[:ami_id], 'rootDeviceType') != "ebs"
|
49
|
+
raise Exception.new("must be an EBS type image")
|
50
|
+
end
|
51
|
+
local_ec2_helper = ec2_helper
|
52
|
+
remote_ec2_helper = Ec2Helper.new(@input_params[:target_ec2_handler])
|
53
|
+
# AWS KeyPair
|
54
|
+
if @input_params[:source_key_name] == nil || @input_params[:source_key_name].empty?()
|
55
|
+
raise Exception.new("No KeyPair name specified for source region")
|
56
|
+
else
|
57
|
+
begin
|
58
|
+
local_ec2_helper.check_keypair(@input_params[:source_key_name])
|
59
|
+
rescue Exception => e
|
60
|
+
post_message("'#{@input_params[:source_key_name]}' Key pair not found in source region")
|
61
|
+
raise Exception.new("source region: #{e.to_s}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
if @input_params[:target_key_name] == nil || @input_params[:target_key_name].empty?()
|
65
|
+
raise Exception.new("No KeyPair name specified for target region")
|
66
|
+
else
|
67
|
+
begin
|
68
|
+
remote_ec2_helper.check_keypair(@input_params[:target_key_name])
|
69
|
+
rescue Exception => e
|
70
|
+
post_message("'#{@input_params[:target_key_name]}' Key pair not found in target region")
|
71
|
+
raise Exception.new("target region: #{e.to_s}")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
# AWS SecurityGroup
|
75
|
+
if @input_params[:source_security_group] == nil
|
76
|
+
@input_params[:source_security_group] = "default"
|
77
|
+
end
|
78
|
+
if !local_ec2_helper.check_open_port(@input_params[:source_security_group], 22)
|
79
|
+
post_message("'#{@input_params[:source_security_group]}' Security Group not opened port 22 for connect via SSH in source region")
|
80
|
+
@input_params[:source_security_group] = nil
|
81
|
+
else
|
82
|
+
post_message("'#{@input_params[:source_security_group]}' Security Group opened port 22 for connect via SSH in source region")
|
83
|
+
end
|
84
|
+
if @input_params[:target_security_group] == nil
|
85
|
+
@input_params[:target_security_group] = "default"
|
86
|
+
end
|
87
|
+
if !remote_ec2_helper.check_open_port(@input_params[:target_security_group], 22)
|
88
|
+
post_message("'#{@input_params[:target_security_group]}' Security Group not opened port 22 for connect via SSH in target region")
|
89
|
+
@input_params[:target_security_group] = nil
|
90
|
+
else
|
91
|
+
post_message("'#{@input_params[:target_security_group]}' Security Group opened port 22 for connect via SSH in target region")
|
92
|
+
end
|
93
|
+
# Device to use
|
94
|
+
if @input_params[:root_device_name] == nil
|
95
|
+
@input_params[:root_device_name] = "/dev/sda1"
|
96
|
+
end
|
97
|
+
if @input_params[:temp_device_name] == nil
|
98
|
+
@input_params[:temp_device_name] = "/dev/sdj"
|
99
|
+
end
|
100
|
+
# SSH Parameters
|
101
|
+
if @input_params[:source_ssh_username] == nil
|
102
|
+
@input_params[:source_ssh_username] = "root"
|
103
|
+
end
|
104
|
+
if @input_params[:target_ssh_username] == nil
|
105
|
+
@input_params[:target_ssh_username] = "root"
|
106
|
+
end
|
107
|
+
if @input_params[:source_ssh_keydata] == nil
|
108
|
+
raise Exception.new("No Private Key for source region")
|
109
|
+
else
|
110
|
+
begin
|
111
|
+
check_ssh_key(@input_params[:source_ssh_keydata])
|
112
|
+
rescue Exception => e
|
113
|
+
post_message("not a Private Key: #{e.to_s}")
|
114
|
+
raise Exception.new("Invalid Private Key for source region: #{e.to_s}")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
if @input_params[:target_ssh_keydata] == nil
|
118
|
+
raise Exception.new("No Private Key for target region")
|
119
|
+
else
|
120
|
+
begin
|
121
|
+
check_ssh_key(@input_params[:target_ssh_keydata])
|
122
|
+
rescue Exception => e
|
123
|
+
post_message("not a Private Key: #{e.to_s}")
|
124
|
+
raise Exception.new("Invalid Private Key for target region: #{e.to_s}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
# AWS Name and Description
|
128
|
+
if @input_params[:description] == nil || !check_aws_desc(@input_params[:description])
|
129
|
+
@input_params[:description] = "Created by CloudyScripts - #{self.class.name}"
|
130
|
+
end
|
131
|
+
if @input_params[:name] == nil || !check_aws_name(@input_params[:name])
|
132
|
+
@input_params[:name] = "Created_by_CloudyScripts/#{self.class.name}_from_#{@input_params[:ami_id]}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Load the initial state for the script.
|
137
|
+
# Abstract method to be implemented by extending classes.
|
138
|
+
def load_initial_state()
|
139
|
+
CopyAmiTestState.load_state(@input_params)
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
# Here begins the state machine implementation
|
145
|
+
class CopyAmiTestState < ScriptExecutionState
|
146
|
+
|
147
|
+
def self.load_state(context)
|
148
|
+
InitialState.new(context)
|
149
|
+
end
|
150
|
+
|
151
|
+
def local_region
|
152
|
+
self.ec2_handler=(@context[:ec2_api_handler])
|
153
|
+
end
|
154
|
+
|
155
|
+
def remote_region
|
156
|
+
self.ec2_handler=(@context[:target_ec2_handler])
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Initial state: start up AMI in source region
|
161
|
+
class InitialState < CopyAmiTestState
|
162
|
+
def enter()
|
163
|
+
local_region()
|
164
|
+
#XXX: create a CloudyScripts Security Group with TCP port 22 publicly opened
|
165
|
+
if @context[:source_security_group] == nil
|
166
|
+
@context[:source_security_group] = Ec2Script::CS_SEC_GRP_NAME
|
167
|
+
create_security_group_with_rules(@context[:source_security_group], Ec2Script::CS_SEC_GRP_DESC,
|
168
|
+
[{:ip_protocol => "tcp", :from_port => 22, :to_port => 22, :cidr_ip => "0.0.0.0/0"}])
|
169
|
+
post_message("'#{@context[:source_security_group]}' Security Group created with TCP port 22 publicly opened.")
|
170
|
+
end
|
171
|
+
|
172
|
+
@context[:source_instance_id], @context[:source_dns_name], @context[:source_availability_zone],
|
173
|
+
@context[:kernel_id], @context[:ramdisk_id], @context[:architecture], @context[:root_device_name] =
|
174
|
+
launch_instance(@context[:ami_id], @context[:source_key_name], @context[:source_security_group])
|
175
|
+
ec2_helper = Ec2Helper.new(@context[:ec2_api_handler])
|
176
|
+
@context[:ebs_volume_id] = ec2_helper.get_attached_volumes(@context[:source_instance_id])[0]['volumeId'] #TODO: what when more root devices?
|
177
|
+
|
178
|
+
SourceInstanceLaunchedState.new(@context)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Source is started. Create a snapshot on the volume that is linked to the instance.
|
183
|
+
class SourceInstanceLaunchedState < CopyAmiTestState
|
184
|
+
def enter()
|
185
|
+
@context[:snapshot_id] = create_snapshot(@context[:ebs_volume_id],
|
186
|
+
"Created by CloudyScripts - #{self.get_superclass_name()} from #{@context[:ebs_volume_id]}")
|
187
|
+
|
188
|
+
AmiSnapshotCreatedState.new(@context)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Snapshot is created from the AMI. Create a volume from the snapshot, attach and mount the volume as second device.
|
193
|
+
class AmiSnapshotCreatedState < CopyAmiTestState
|
194
|
+
def enter()
|
195
|
+
@context[:source_volume_id] = create_volume_from_snapshot(@context[:snapshot_id],
|
196
|
+
@context[:source_availability_zone])
|
197
|
+
device = @context[:temp_device_name]
|
198
|
+
mount_point = "/mnt/tmp_#{@context[:source_volume_id]}"
|
199
|
+
#XXX: attach volume after root partition detection
|
200
|
+
attach_volume(@context[:source_volume_id], @context[:source_instance_id], device)
|
201
|
+
connect(@context[:source_dns_name], @context[:source_ssh_username], nil, @context[:source_ssh_keydata])
|
202
|
+
# detect if there is a shift for device mapping (between AWS and the operating system of the system)
|
203
|
+
root_device_name = get_root_device_name()
|
204
|
+
# detect letters
|
205
|
+
aws_root_device = @context[:root_device_name]
|
206
|
+
aws_letter = aws_root_device.split('/')[2].gsub('sd', '').gsub('xvd', '').gsub(/[0-9]/, '')
|
207
|
+
os_letter = root_device_name.split('/')[2].gsub('sd', '').gsub('xvd', '').gsub(/[0-9]/, '')
|
208
|
+
aws_device_letter = device.split('/')[2].gsub('sd', '').gsub('xvd', '').gsub(/[0-9]/, '')
|
209
|
+
if !aws_letter.eql?(os_letter)
|
210
|
+
post_message("Detected specific kernel with shift between AWS and Kernel OS for device naming: '#{aws_root_device}' vs '#{root_device_name}'")
|
211
|
+
end
|
212
|
+
while !aws_letter.eql?(os_letter)
|
213
|
+
aws_letter.succ!
|
214
|
+
aws_device_letter.succ!
|
215
|
+
end
|
216
|
+
|
217
|
+
# attach volume
|
218
|
+
#attach_volume(@context[:source_volume_id], @context[:source_instance_id], device)
|
219
|
+
|
220
|
+
device = "/dev/sd#{aws_device_letter}"
|
221
|
+
# detect root partition vs root volume: simply check if we have several /dev/sdx* entries
|
222
|
+
parts_count = get_partition_count(device)
|
223
|
+
if parts_count >= 2
|
224
|
+
# retrieve partition table, in order to restore it in the target region
|
225
|
+
post_message("Detected specific volume with a valid partition table on device '#{device}'...")
|
226
|
+
partition_table = get_partition_table(device)
|
227
|
+
@context[:partition_table] = partition_table
|
228
|
+
#XXX: HANDLE at a LOWER LEVEL
|
229
|
+
# update partition table with device
|
230
|
+
# s/device/@context[:temp_device_name]/ on partition table
|
231
|
+
#@context[:partition_table] = partition_table.gsub("#{device}", "#{@context[:temp_device_name]}")
|
232
|
+
# retrieve the root partition number
|
233
|
+
os_nb = root_device_name.split('/')[2].gsub('sd', '').gsub('xvd', '').gsub(/[a-z]/, '')
|
234
|
+
device = device + os_nb
|
235
|
+
@context[:root_partition_nb] = os_nb
|
236
|
+
post_message("Using root partition: '#{device}'...")
|
237
|
+
end
|
238
|
+
post_message("Using AWS name '#{@context[:temp_device_name]}' and OS name '#{device}'")
|
239
|
+
mount_fs(mount_point, device)
|
240
|
+
# get root partition label and filesystem type
|
241
|
+
#@context[:label] = get_root_partition_label()
|
242
|
+
#@context[:fs_type] = get_root_partition_fs_type()
|
243
|
+
@context[:fs_type], @context[:label] = get_root_partition_fs_type_and_label()
|
244
|
+
disconnect()
|
245
|
+
|
246
|
+
#XXX: go to clean up state
|
247
|
+
#SourceVolumeReadyState.new(@context)
|
248
|
+
AmiRegisteredState.new(@context)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Source is ready. Now start instance in the target region
|
253
|
+
class SourceVolumeReadyState < CopyAmiTestState
|
254
|
+
def enter()
|
255
|
+
remote_region()
|
256
|
+
#XXX: create a CloudyScripts Security Group with TCP port 22 publicly opened
|
257
|
+
if @context[:target_security_group] == nil
|
258
|
+
@context[:target_security_group] = Ec2Script::CS_SEC_GRP_NAME
|
259
|
+
create_security_group_with_rules(@context[:target_security_group], Ec2Script::CS_SEC_GRP_DESC,
|
260
|
+
[{:ip_protocol => "tcp", :from_port => 22, :to_port => 22, :cidr_ip => "0.0.0.0/0"}])
|
261
|
+
post_message("'#{@context[:target_security_group]}' Security Group created with TCP port 22 publicly opened.")
|
262
|
+
end
|
263
|
+
|
264
|
+
result = launch_instance(@context[:target_ami_id], @context[:target_key_name], @context[:target_security_group])
|
265
|
+
@context[:target_instance_id] = result.first
|
266
|
+
@context[:target_dns_name] = result[1]
|
267
|
+
@context[:target_availability_zone] = result[2]
|
268
|
+
|
269
|
+
TargetInstanceLaunchedState.new(@context)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Destination instance is started. Now configure storage.
|
274
|
+
class TargetInstanceLaunchedState < CopyAmiTestState
|
275
|
+
def enter()
|
276
|
+
local_region()
|
277
|
+
ec2_helper = Ec2Helper.new(@context[:ec2_api_handler])
|
278
|
+
volume_size = ec2_helper.snapshot_prop(@context[:snapshot_id], :volumeSize).to_i
|
279
|
+
#
|
280
|
+
remote_region()
|
281
|
+
@context[:target_volume_id] = create_volume(@context[:target_availability_zone], volume_size)
|
282
|
+
device = @context[:temp_device_name]
|
283
|
+
mount_point = "/mnt/tmp_#{@context[:target_volume_id]}"
|
284
|
+
attach_volume(@context[:target_volume_id], @context[:target_instance_id], device)
|
285
|
+
connect(@context[:target_dns_name], @context[:target_ssh_username], nil, @context[:target_ssh_keydata])
|
286
|
+
# check if we need to create a partition table
|
287
|
+
if !(@context[:partition_table] == nil)
|
288
|
+
post_message("Creating a partition table on device '#{device}'...")
|
289
|
+
set_partition_table(device, @context[:partition_table])
|
290
|
+
#XXX: HANDLE at a LOWER LEVEL
|
291
|
+
# before adding partition table, adjust device name
|
292
|
+
#set_partition_table(device, @context[:partition_table].gsub(/\/dev\/(s|xv)d[a-z]/, "#{@context[:temp_device_name]}"))
|
293
|
+
# adjust partition to mount
|
294
|
+
device = device + @context[:root_partition_nb]
|
295
|
+
end
|
296
|
+
# make root partition
|
297
|
+
create_labeled_fs(@context[:target_dns_name], device, @context[:fs_type], @context[:label])
|
298
|
+
mount_fs(mount_point, device)
|
299
|
+
disconnect()
|
300
|
+
|
301
|
+
TargetVolumeReadyState.new(@context)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Storages are ready. Only thing missing: the key of the target region
|
306
|
+
# must be available on the instance in the source region to be able to perform
|
307
|
+
# a remote copy.
|
308
|
+
class TargetVolumeReadyState < CopyAmiTestState
|
309
|
+
def enter()
|
310
|
+
post_message("upload key of target-instance to source-instance...")
|
311
|
+
path_candidates = ["/#{@context[:source_ssh_username]}/.ssh/",
|
312
|
+
"/home/#{@context[:source_ssh_username]}/.ssh/"]
|
313
|
+
key_path = determine_file(@context[:source_dns_name], @context[:source_ssh_username], @context[:source_ssh_keydata], path_candidates)
|
314
|
+
#XXX: fix the problem fo key name with white space
|
315
|
+
#upload_file(@context[:source_dns_name], @context[:source_ssh_username], @context[:source_ssh_keydata],
|
316
|
+
# @context[:target_ssh_keyfile], "#{key_path}#{@context[:target_key_name]}.pem")
|
317
|
+
upload_file(@context[:source_dns_name], @context[:source_ssh_username], @context[:source_ssh_keydata],
|
318
|
+
@context[:target_ssh_keyfile], "#{key_path}#{@context[:target_key_name].gsub(/\s+/, '_')}.pem")
|
319
|
+
post_message("credentials are in place to connect source and target.")
|
320
|
+
|
321
|
+
KeyInPlaceState.new(@context)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Now we can copy.
|
326
|
+
class KeyInPlaceState < CopyAmiTestState
|
327
|
+
def enter()
|
328
|
+
connect(@context[:target_dns_name], @context[:target_ssh_username], nil, @context[:target_ssh_keydata])
|
329
|
+
disable_ssh_tty(@context[:target_dns_name])
|
330
|
+
disconnect()
|
331
|
+
#
|
332
|
+
connect(@context[:source_dns_name], @context[:source_ssh_username], nil, @context[:source_ssh_keydata])
|
333
|
+
source_dir = "/mnt/tmp_#{@context[:source_volume_id]}/"
|
334
|
+
dest_dir = "/mnt/tmp_#{@context[:target_volume_id]}"
|
335
|
+
#XXX: fix the problem fo key name with white space
|
336
|
+
#remote_copy(@context[:source_ssh_username], @context[:target_key_name], source_dir,
|
337
|
+
# @context[:target_dns_name], @context[:target_ssh_username], dest_dir)
|
338
|
+
remote_copy(@context[:source_ssh_username], @context[:target_key_name].gsub(/\s+/, '_'), source_dir,
|
339
|
+
@context[:target_dns_name], @context[:target_ssh_username], dest_dir)
|
340
|
+
disconnect()
|
341
|
+
#
|
342
|
+
connect(@context[:target_dns_name], @context[:target_ssh_username], nil, @context[:target_ssh_keydata])
|
343
|
+
enable_ssh_tty(@context[:target_dns_name])
|
344
|
+
unmount_fs(dest_dir)
|
345
|
+
disconnect()
|
346
|
+
|
347
|
+
DataCopiedState.new(@context)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Data of snapshot now copied to the new volume. Create a snapshot of the
|
352
|
+
# new volume.
|
353
|
+
class DataCopiedState < CopyAmiTestState
|
354
|
+
def enter()
|
355
|
+
remote_region()
|
356
|
+
@context[:new_snapshot_id] = create_snapshot(@context[:target_volume_id],
|
357
|
+
"Created by CloudyScripts - #{self.get_superclass_name()} from #{@context[:target_volume_id]}")
|
358
|
+
|
359
|
+
TargetSnapshotCreatedState.new(@context)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# Snapshot Operation done. Now this snapshot must be registered as AMI
|
364
|
+
class TargetSnapshotCreatedState < CopyAmiTestState
|
365
|
+
def enter()
|
366
|
+
remote_region()
|
367
|
+
# Get Amazon Kernel Image ID
|
368
|
+
aki = get_aws_kernel_image_aki(@context[:source_availability_zone], @context[:kernel_id],
|
369
|
+
@context[:target_availability_zone])
|
370
|
+
device = @context[:root_device_name]
|
371
|
+
if !(@context[:partition_table] == nil)
|
372
|
+
device.gsub!(/[0-9]/, '')
|
373
|
+
post_message("Using BlockDevice for snapshot registration rather than RootDevice '#{device}' due to a valid partition table on device...")
|
374
|
+
end
|
375
|
+
@context[:result][:image_id] = register_snapshot(@context[:new_snapshot_id], @context[:name],
|
376
|
+
device, @context[:description], aki, nil, @context[:architecture])
|
377
|
+
|
378
|
+
AmiRegisteredState.new(@context)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# AMI is registered. Now only cleanup is missing, i.e. shut down instances and
|
383
|
+
# remote the volumes that were created. Start with cleaning the ressources
|
384
|
+
# in the both regions.
|
385
|
+
class AmiRegisteredState < CopyAmiTestState
|
386
|
+
def enter()
|
387
|
+
post_message("Cleaning Source and Target Regions...")
|
388
|
+
error = []
|
389
|
+
local_region()
|
390
|
+
begin
|
391
|
+
shut_down_instance(@context[:source_instance_id])
|
392
|
+
rescue Exception => e
|
393
|
+
error << e
|
394
|
+
post_message("Unable to shutdown instance '#{@context[:source_instance_id]}' in source region: #{e.to_s}")
|
395
|
+
end
|
396
|
+
begin
|
397
|
+
delete_volume(@context[:source_volume_id])
|
398
|
+
rescue Exception => e
|
399
|
+
error << e
|
400
|
+
post_message("Unable to delete volume '#{@context[:source_volume_id]}' in source region: #{e.to_s}")
|
401
|
+
end
|
402
|
+
begin
|
403
|
+
delete_snapshot(@context[:snapshot_id])
|
404
|
+
rescue Exception => e
|
405
|
+
error << e
|
406
|
+
post_message("Unable to delete snapshot '#{@context[:snapshot_id]}' in source region: #{e.to_s}")
|
407
|
+
end
|
408
|
+
#XXX: delete Security Group according to its name
|
409
|
+
if @context[:source_security_group].eql?(Ec2Script::CS_SEC_GRP_NAME)
|
410
|
+
begin
|
411
|
+
delete_security_group(@context[:source_security_group])
|
412
|
+
rescue Exception => e
|
413
|
+
error << e
|
414
|
+
post_message("Unable to delete Security Group '#{@context[:source_security_group]}' in source region: #{e.to_s}")
|
415
|
+
end
|
416
|
+
end
|
417
|
+
#
|
418
|
+
remote_region()
|
419
|
+
begin
|
420
|
+
shut_down_instance(@context[:target_instance_id])
|
421
|
+
rescue Exception => e
|
422
|
+
error << e
|
423
|
+
post_message("Unable to shutdown instance '#{@context[:target_instance_id]}' in target region: #{e.to_s}")
|
424
|
+
end
|
425
|
+
begin
|
426
|
+
delete_volume(@context[:target_volume_id])
|
427
|
+
rescue Exception => e
|
428
|
+
error << e
|
429
|
+
post_message("Unable to delete volume '#{@context[:target_volume_id]}' in target region: #{e.to_s}")
|
430
|
+
end
|
431
|
+
#XXX: delete Security Group according to its name
|
432
|
+
if @context[:target_security_group].eql?(Ec2Script::CS_SEC_GRP_NAME)
|
433
|
+
begin
|
434
|
+
delete_security_group(@context[:target_security_group])
|
435
|
+
rescue
|
436
|
+
error << e
|
437
|
+
post_message("Unable to delete Security Group '#{@context[:target_security_group]}' in target region: #{e.to_s}")
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
if error.size() > 0
|
442
|
+
raise Exception.new("Cleanup error(s)")
|
443
|
+
end
|
444
|
+
|
445
|
+
Done.new(@context)
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
end
|