CloudyScripts 2.14.62 → 2.14.63
Sign up to get free protection for your applications and to get access to all the features.
- 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
|