CloudyScripts 2.14.63 → 2.14.64
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 +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
|