CloudyScripts 2.14.63 → 2.14.64
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -1
- data/lib/help/state_transition_helper.rb +46 -8
- metadata +34 -10
- data/lib/scripts/ec2/check_cloudyscripts.rb +0 -142
- data/lib/scripts/ec2/copy_ami_test.rb +0 -449
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ require 'rake/testtask'
|
|
12
12
|
|
13
13
|
spec = Gem::Specification.new do |s|
|
14
14
|
s.name = 'CloudyScripts'
|
15
|
-
s.version = '2.14.
|
15
|
+
s.version = '2.14.64' #<number cloud-stacks supported>.<number cloud-scripts>.<counting releases>
|
16
16
|
s.has_rdoc = true
|
17
17
|
s.extra_rdoc_files = ['README.rdoc', 'LICENSE']
|
18
18
|
s.summary = 'Scripts to facilitate programming for infrastructure clouds.'
|
@@ -31,6 +31,8 @@ spec = Gem::Specification.new do |s|
|
|
31
31
|
s.add_dependency("amazon-ec2")
|
32
32
|
s.add_dependency("net-ssh")
|
33
33
|
s.add_dependency("net-scp")
|
34
|
+
s.add_dependency("net-sftp")
|
35
|
+
s.add_dependency("zip")
|
34
36
|
end
|
35
37
|
|
36
38
|
Rake::GemPackageTask.new(spec) do |p|
|
@@ -579,7 +579,7 @@ module StateTransitionHelper
|
|
579
579
|
ec2_handler().authorize_security_group_ingress(:group_name => name,
|
580
580
|
:ip_protocol => rule[:ip_protocol], :from_port => rule[:from_port], :to_port => rule[:to_port], :cidr_ip => rule[:cidr_ip])
|
581
581
|
rescue AWS::InvalidPermissionDuplicate => e
|
582
|
-
@logger.warn "'#{rule[:
|
582
|
+
@logger.warn "'#{rule[:ip_protocol].upcase()} (#{rule[:from_port]}-#{rule[:to_port]})' rule already exists: #{e.to_s}"
|
583
583
|
end
|
584
584
|
}
|
585
585
|
end
|
@@ -626,6 +626,21 @@ module StateTransitionHelper
|
|
626
626
|
if !type.nil? && !type.empty?
|
627
627
|
fs_type = type
|
628
628
|
end
|
629
|
+
#XXX: detect new kernel that have /dev/xvdX device node instead of /dev/sdX
|
630
|
+
if device =~ /\/dev\/sd[a-z]/
|
631
|
+
if !remote_handler().file_exists?(device)
|
632
|
+
post_message("'#{device}' device node not found, checking for new kernel support...")
|
633
|
+
@logger.debug "'#{device}' device node not found, checking for new kernel support"
|
634
|
+
new_device = device.gsub('sd', 'xvd')
|
635
|
+
if remote_handler().file_exists?(new_device)
|
636
|
+
post_message("'#{new_device}' device node found")
|
637
|
+
@logger.debug "'#{new_device}' device node found"
|
638
|
+
device = new_device
|
639
|
+
end
|
640
|
+
end
|
641
|
+
#elsif device =~/\/dev\/xvd[a-z]/
|
642
|
+
end
|
643
|
+
|
629
644
|
@logger.debug "create '#{fs_type}' filesystem on device '#{device}'"
|
630
645
|
status = remote_handler().create_filesystem(fs_type, device)
|
631
646
|
if status == false
|
@@ -1334,6 +1349,27 @@ module StateTransitionHelper
|
|
1334
1349
|
#Ubuntu kernel Amazon Kernel ID
|
1335
1350
|
'aki-87f38cd5' => 'aki-ubuntu-karmic-v2.6.31-302.i386'
|
1336
1351
|
},
|
1352
|
+
'ap-southeast-2' => {'' => 'pv-grub-hd00-V1.01-i386',
|
1353
|
+
'' => 'pv-grub-hd00-V1.01-x86_64',
|
1354
|
+
'' => 'pv-grub-hd0-V1.01-i386',
|
1355
|
+
'' => 'pv-grub-hd0-V1.01-x86_64',
|
1356
|
+
'' => 'pv-grub-hd00_1.02-i386',
|
1357
|
+
'' => 'pv-grub-hd00_1.02-x86_64',
|
1358
|
+
'' => 'pv-grub-hd0_1.02-i386',
|
1359
|
+
'' => 'pv-grub-hd0_1.02-x86_64',
|
1360
|
+
'aki-33990e09' => 'pv-grub-hd0_1.03-i386',
|
1361
|
+
'aki-31990e0b' => 'pv-grub-hd0_1.03-x86_64',
|
1362
|
+
'aki-3f990e05' => 'pv-grub-hd00_1.03-i386',
|
1363
|
+
'aki-3d990e07' => 'pv-grub-hd00_1.03-x86_64',
|
1364
|
+
|
1365
|
+
#RHEL kernel Amazon Kernel ID
|
1366
|
+
'aki-9b8413a1' => 'aki-rhel-i386', # RH-pv-grub-hd0-V1.01-i386
|
1367
|
+
'aki-998413a3' => 'aki-rhel-x86_64', # RH-pv-grub-hd0-V1.01-x86_64
|
1368
|
+
|
1369
|
+
#Ubuntu kernel Amazon Kernel ID
|
1370
|
+
'' => 'aki-ubuntu-karmic-v2.6.31-302.i386'
|
1371
|
+
},
|
1372
|
+
|
1337
1373
|
'ap-northeast-1' => {'aki-d209a2d3' => 'pv-grub-hd00-V1.01-i386',
|
1338
1374
|
'aki-d409a2d5' => 'pv-grub-hd00-V1.01-x86_64',
|
1339
1375
|
'aki-d609a2d7' => 'pv-grub-hd0-V1.01-i386',
|
@@ -1358,10 +1394,10 @@ module StateTransitionHelper
|
|
1358
1394
|
'aki-d03ce3cd' => 'pv-grub-hd00-V1.01-x86_64',
|
1359
1395
|
'aki-863ce39b' => 'pv-grub-hd0-V1.01-i386',
|
1360
1396
|
'aki-d63ce3cb' => 'pv-grub-hd0-V1.01-x86_64',
|
1361
|
-
'aki-
|
1362
|
-
'aki-
|
1363
|
-
'aki-
|
1364
|
-
'aki-
|
1397
|
+
'aki-823ce39f' => 'pv-grub-hd00_1.02-i386',
|
1398
|
+
'aki-d23ce3cf' => 'pv-grub-hd00_1.02-x86_64',
|
1399
|
+
'aki-bc3ce3a1' => 'pv-grub-hd0_1.02-i386',
|
1400
|
+
'aki-cc3ce3d1' => 'pv-grub-hd0_1.02-x86_64',
|
1365
1401
|
'aki-ca8f51d7' => 'pv-grub-hd0_1.03-i386',
|
1366
1402
|
'aki-c48f51d9' => 'pv-grub-hd0_1.03-x86_64',
|
1367
1403
|
'aki-ce8f51d3' => 'pv-grub-hd00_1.03-i386',
|
@@ -1407,14 +1443,16 @@ module StateTransitionHelper
|
|
1407
1443
|
case endpoint
|
1408
1444
|
when /us-east/
|
1409
1445
|
region = "us-east-1"
|
1410
|
-
when /us-west-2/
|
1411
|
-
region = "us-west-2"
|
1412
1446
|
when /us-west-1/
|
1413
1447
|
region = "us-west-1"
|
1448
|
+
when /us-west-2/
|
1449
|
+
region = "us-west-2"
|
1414
1450
|
when /eu-west/
|
1415
1451
|
region = "eu-west-1"
|
1416
|
-
when /ap-southeast/
|
1452
|
+
when /ap-southeast-1/
|
1417
1453
|
region = "ap-southeast-1"
|
1454
|
+
when /ap-southeast-2/
|
1455
|
+
region = "ap-southeast-2"
|
1418
1456
|
when /ap-northeast/
|
1419
1457
|
region = "ap-northeast-1"
|
1420
1458
|
when /sa-east-1/
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: CloudyScripts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 183
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 14
|
9
|
-
-
|
10
|
-
version: 2.14.
|
9
|
+
- 64
|
10
|
+
version: 2.14.64
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matthias Jung
|
@@ -16,8 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date:
|
20
|
-
default_executable:
|
19
|
+
date: 2013-01-03 00:00:00 Z
|
21
20
|
dependencies:
|
22
21
|
- !ruby/object:Gem::Dependency
|
23
22
|
name: amazon-ec2
|
@@ -61,6 +60,34 @@ dependencies:
|
|
61
60
|
version: "0"
|
62
61
|
type: :runtime
|
63
62
|
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: net-sftp
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :runtime
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: zip
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :runtime
|
90
|
+
version_requirements: *id005
|
64
91
|
description: Scripts to facilitate programming for infrastructure clouds.
|
65
92
|
email:
|
66
93
|
- matthias.jung@gmail.com
|
@@ -255,7 +282,6 @@ files:
|
|
255
282
|
- lib/scripts/ec2/port_range_detector.rb
|
256
283
|
- lib/scripts/ec2/ec2_script.rb
|
257
284
|
- lib/scripts/ec2/vpc_critical_ports_audit.rb
|
258
|
-
- lib/scripts/ec2/copy_ami_test.rb
|
259
285
|
- lib/scripts/ec2/copy_snapshot.rb
|
260
286
|
- lib/scripts/ec2/copy_mswindows_ami.rb
|
261
287
|
- lib/scripts/ec2/copy_mswindows_snapshot.rb
|
@@ -267,7 +293,6 @@ files:
|
|
267
293
|
- lib/scripts/ec2/snapshot_optimization.rb
|
268
294
|
- lib/scripts/ec2/audit_via_ssh.rb
|
269
295
|
- lib/scripts/ec2/download_snapshot.rb
|
270
|
-
- lib/scripts/ec2/check_cloudyscripts.rb
|
271
296
|
- lib/scripts/vCloud/v_cloud_script.rb
|
272
297
|
- lib/scripts/vCloud/open_port_checker_vm.rb
|
273
298
|
- lib/cloudyscripts.rb
|
@@ -281,7 +306,6 @@ files:
|
|
281
306
|
- lib/help/progress_message_listener.rb
|
282
307
|
- lib/help/remote_command_handler.rb
|
283
308
|
- lib/help/helper.rb
|
284
|
-
has_rdoc: true
|
285
309
|
homepage: http://elastic-security.com
|
286
310
|
licenses: []
|
287
311
|
|
@@ -311,7 +335,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
311
335
|
requirements: []
|
312
336
|
|
313
337
|
rubyforge_project: CloudyScripts
|
314
|
-
rubygems_version: 1.
|
338
|
+
rubygems_version: 1.7.2
|
315
339
|
signing_key:
|
316
340
|
specification_version: 3
|
317
341
|
summary: Scripts to facilitate programming for infrastructure clouds.
|
@@ -1,142 +0,0 @@
|
|
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
|
-
|
8
|
-
# Goal: check internal piece of CloudyScripts without launching the full scripts
|
9
|
-
#
|
10
|
-
|
11
|
-
class CheckCloudyScripts < Ec2Script
|
12
|
-
|
13
|
-
def initialize(input_params)
|
14
|
-
super(input_params)
|
15
|
-
end
|
16
|
-
|
17
|
-
def check_input_parameters()
|
18
|
-
if @input_params[:ami_id] == nil && !(@input_params[:ami_id] =~ /^ami-.*$/)
|
19
|
-
raise Exception.new("Invalid AMI ID specified: #{@input_params[:ami_id]}")
|
20
|
-
end
|
21
|
-
ec2_helper = Ec2Helper.new(@input_params[:ec2_api_handler])
|
22
|
-
if ec2_helper.ami_prop(@input_params[:ami_id], 'rootDeviceType') != "ebs"
|
23
|
-
raise Exception.new("must be an EBS type image")
|
24
|
-
end
|
25
|
-
local_ec2_helper = ec2_helper
|
26
|
-
if !local_ec2_helper.check_open_port('default', 22)
|
27
|
-
raise Exception.new("Port 22 must be opened for security group 'default' to connect via SSH in source-region")
|
28
|
-
end
|
29
|
-
remote_ec2_helper = Ec2Helper.new(@input_params[:target_ec2_handler])
|
30
|
-
if !remote_ec2_helper.check_open_port('default', 22)
|
31
|
-
raise Exception.new("Port 22 must be opened for security group 'default' to connect via SSH in target-region")
|
32
|
-
end
|
33
|
-
if @input_params[:root_device_name] == nil
|
34
|
-
@input_params[:root_device_name] = "/dev/sda1"
|
35
|
-
end
|
36
|
-
if @input_params[:temp_device_name] == nil
|
37
|
-
@input_params[:temp_device_name] = "/dev/sdj"
|
38
|
-
end
|
39
|
-
if @input_params[:source_ssh_username] == nil
|
40
|
-
@input_params[:source_ssh_username] = "root"
|
41
|
-
end
|
42
|
-
if @input_params[:target_ssh_username] == nil
|
43
|
-
@input_params[:target_ssh_username] = "root"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Load the initial state for the script.
|
48
|
-
# Abstract method to be implemented by extending classes.
|
49
|
-
def load_initial_state()
|
50
|
-
CheckCloudyScriptsState.load_state(@input_params)
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
# Here begins the state machine implementation
|
56
|
-
class CheckCloudyScriptsState < ScriptExecutionState
|
57
|
-
|
58
|
-
def self.load_state(context)
|
59
|
-
InitialState.new(context)
|
60
|
-
end
|
61
|
-
|
62
|
-
def local_region
|
63
|
-
self.ec2_handler=(@context[:ec2_api_handler])
|
64
|
-
end
|
65
|
-
|
66
|
-
def remote_region
|
67
|
-
self.ec2_handler=(@context[:target_ec2_handler])
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# Initial state: start up AMI in source region
|
72
|
-
class InitialState < CheckCloudyScriptsState
|
73
|
-
def enter()
|
74
|
-
post_message("INFO: Entering InitialState...")
|
75
|
-
|
76
|
-
#@context[:source_instance_id], @context[:source_dns_name], @context[:source_availability_zone],
|
77
|
-
# @context[:kernel_id], @context[:ramdisk_id], @context[:architecture], @context[:root_device_name] =
|
78
|
-
# launch_instance(@context[:ami_id], @context[:source_key_name], "default")
|
79
|
-
start_instance(@context[:source_instance_id])
|
80
|
-
res = describe_instance(@context[:source_instance_id])
|
81
|
-
puts "DEBUG: instance: #{res.inspect}"
|
82
|
-
#@context[:source_instance_id] = "i-0a663643"
|
83
|
-
@context[:source_dns_name] = res[1]
|
84
|
-
@context[:source_availability_zone] = res[2]
|
85
|
-
@context[:kernel_id] = res[3]
|
86
|
-
@context[:ramdisk_id] = res[4]
|
87
|
-
@context[:architecture] = res[5]
|
88
|
-
@context[:root_device_name] = res[6]
|
89
|
-
|
90
|
-
ec2_helper = Ec2Helper.new(@context[:ec2_api_handler])
|
91
|
-
puts "DEBUG: get_attached returns: #{ec2_helper.get_attached_volumes(@context[:source_instance_id]).inspect}"
|
92
|
-
@context[:ebs_volume_id] = ec2_helper.get_attached_volumes(@context[:source_instance_id])[0]['volumeId']#TODO: what when more root devices?
|
93
|
-
|
94
|
-
CSTestingState.new(@context)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Snapshot is created from the AMI. Create a volume from the snapshot, attach and mount the volume as second device.
|
99
|
-
class CSTestingState < CheckCloudyScriptsState
|
100
|
-
def enter()
|
101
|
-
post_message("INFO: Entering CSTestingState...")
|
102
|
-
|
103
|
-
#@context[:source_volume_id] = create_volume_from_snapshot(@context[:snapshot_id],
|
104
|
-
# @context[:source_availability_zone])
|
105
|
-
@context[:source_volume_id] = "vol-6eb34b06"
|
106
|
-
|
107
|
-
device = @context[:temp_device_name]
|
108
|
-
mount_point = "/mnt/tmp_#{@context[:source_volume_id]}"
|
109
|
-
attach_volume(@context[:source_volume_id], @context[:source_instance_id], device)
|
110
|
-
connect(@context[:source_dns_name], @context[:source_ssh_username], nil, @context[:source_ssh_keydata])
|
111
|
-
# detect if there is a shift for device mapping (between AWS and the operating system of the system)
|
112
|
-
root_device_name = get_root_device_name()
|
113
|
-
# detect letters
|
114
|
-
aws_root_device = @context[:root_device_name]
|
115
|
-
aws_letter = aws_root_device.split('/')[2].gsub('sd', '').gsub('xvd', '').gsub(/[0-9]/, '')
|
116
|
-
os_letter = root_device_name.split('/')[2].gsub('sd', '').gsub('xvd', '').gsub(/[0-9]/, '')
|
117
|
-
aws_device_letter = device.split('/')[2].gsub('sd', '').gsub('xvd', '').gsub(/[0-9]/, '')
|
118
|
-
puts "DEBUG: AWS info: #{aws_root_device}, #{aws_letter}"
|
119
|
-
puts "DEBUG: OS info: #{root_device_name}, #{os_letter}"
|
120
|
-
if !aws_letter.eql?(os_letter)
|
121
|
-
post_message("Detected specific kernel with shift between AWS and Kernel OS for device naming")
|
122
|
-
puts "Detected specific kernel with shift between AWS and Kernel OS for device naming (#{aws_root_device} vs #{root_device_name})"
|
123
|
-
end
|
124
|
-
while !aws_letter.eql?(os_letter)
|
125
|
-
aws_letter.succ!
|
126
|
-
aws_device_letter.succ!
|
127
|
-
end
|
128
|
-
device = "/dev/sd#{aws_device_letter}"
|
129
|
-
post_message("Using AWS name '#{@context[:temp_device_name]}' and OS name '#{device}'")
|
130
|
-
puts "Using AWS name '#{@context[:temp_device_name]}' and OS name '#{device}'"
|
131
|
-
mount_fs(mount_point, device)
|
132
|
-
# get root partition label and filesystem type
|
133
|
-
#@context[:label] = get_root_partition_label()
|
134
|
-
#@context[:fs_type] = get_root_partition_fs_type()
|
135
|
-
@context[:fs_type], @context[:label] = get_root_partition_fs_type_and_label()
|
136
|
-
disconnect()
|
137
|
-
|
138
|
-
Done.new()
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
end
|
@@ -1,449 +0,0 @@
|
|
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
|