CloudyScripts 0.0.13 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/Rakefile +1 -1
- data/lib/help/ec2_helper.rb +8 -0
- data/lib/help/remote_command_handler.rb +5 -5
- data/lib/help/state_transition_helper.rb +227 -204
- data/lib/scripts/ec2/ami2_ebs_conversion.rb +19 -13
- data/lib/scripts/ec2/dm_encrypt.rb +2 -1
- data/lib/scripts/ec2/download_snapshot.rb +40 -29
- metadata +24 -13
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2010
|
1
|
+
Copyright (c) 2010 SecludIT (http://secludit.com)
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
4
|
|
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 = '0.0.
|
15
|
+
s.version = '0.0.14'
|
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.'
|
data/lib/help/ec2_helper.rb
CHANGED
@@ -51,4 +51,12 @@ class Ec2Helper
|
|
51
51
|
}
|
52
52
|
return false
|
53
53
|
end
|
54
|
+
|
55
|
+
def volume_prop(volume_id, prop)
|
56
|
+
vols = @ec2_api.describe_volumes(:volume_id => volume_id)
|
57
|
+
if vols['volumeSet']['item'].size == 0
|
58
|
+
raise Exception.new("volume #{volume_id} not found")
|
59
|
+
end
|
60
|
+
return vols['volumeSet']['item'][0][prop.to_s]
|
61
|
+
end
|
54
62
|
end
|
@@ -12,8 +12,8 @@ class RemoteCommandHandler
|
|
12
12
|
# Params:
|
13
13
|
# * ip: ip address of the machine to connect to
|
14
14
|
# * keyfile: path of the keyfile to be used for authentication
|
15
|
-
def connect_with_keyfile(ip, keyfile)
|
16
|
-
@ssh_session = Net::SSH.start(ip, 'root', :keys => [keyfile])
|
15
|
+
def connect_with_keyfile(ip, keyfile, timeout = 30)
|
16
|
+
@ssh_session = Net::SSH.start(ip, 'root', {:keys => [keyfile], :timeout => timeout})
|
17
17
|
end
|
18
18
|
|
19
19
|
# Connect to the machine as root using keydata from a keyfile.
|
@@ -21,8 +21,8 @@ class RemoteCommandHandler
|
|
21
21
|
# * ip: ip address of the machine to connect to
|
22
22
|
# * user: user name
|
23
23
|
# * key_data: key_data to be used for authentication
|
24
|
-
def connect(ip, user, key_data)
|
25
|
-
@ssh_session = Net::SSH.start(ip, user, :key_data => [key_data])
|
24
|
+
def connect(ip, user, key_data, timeout = 30)
|
25
|
+
@ssh_session = Net::SSH.start(ip, user, {:key_data => [key_data], :timeout => timeout})
|
26
26
|
end
|
27
27
|
|
28
28
|
# Disconnect the current handler
|
@@ -115,7 +115,7 @@ class RemoteCommandHandler
|
|
115
115
|
# Zip the complete contents of the source path into the destination file.
|
116
116
|
def zip(source_path, destination_file)
|
117
117
|
begin
|
118
|
-
exec = "cd #{source_path}; zip #{destination_file} *"
|
118
|
+
exec = "cd #{source_path}; zip -ry #{destination_file} *"
|
119
119
|
remote_execute(exec, nil, true)
|
120
120
|
rescue Exception => e
|
121
121
|
raise Exception.new("zip failed due to #{e.message}")
|
@@ -1,37 +1,39 @@
|
|
1
1
|
# Contains methods that are used by the scripts in the state-machines. Since
|
2
2
|
# they are reused by different scripts, they are factored into this module.
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# to listeners
|
3
|
+
#
|
4
|
+
# Note: it is supposed that a hash named @context exists @context[:script]
|
5
|
+
# must be set to a script object to pass information and messages
|
6
|
+
# to listeners.
|
7
|
+
# Some other information is expected to be provided in the @context object:
|
8
|
+
# * :remote_command_handler => ssh wrapper object
|
9
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
7
10
|
|
8
11
|
module StateTransitionHelper
|
9
12
|
|
10
13
|
# Connects to the remote host via SSH.
|
11
|
-
# Params
|
12
|
-
# *
|
13
|
-
# *
|
14
|
-
# *
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
14
|
+
# Params:
|
15
|
+
# * dns_name => machine to connect to
|
16
|
+
# * ssh_keyfile => key-file used for ssh
|
17
|
+
# * ssh_keydata => contents of key-file (either use ssh_keyfile or ssh_keydata)
|
18
|
+
# Returns:
|
19
|
+
# * OS of the connected machine
|
20
|
+
def connect(dns_name, ssh_keyfile = nil, ssh_keydata = nil)
|
21
|
+
post_message("connecting to #{dns_name}...")
|
20
22
|
connected = false
|
21
23
|
remaining_trials = 3
|
22
24
|
while !connected && remaining_trials > 0
|
23
25
|
remaining_trials -= 1
|
24
|
-
if
|
26
|
+
if ssh_keyfile != nil
|
25
27
|
begin
|
26
|
-
|
28
|
+
remote_handler().connect_with_keyfile(dns_name, ssh_keyfile)
|
27
29
|
connected = true
|
28
30
|
rescue Exception => e
|
29
31
|
@logger.info("connection failed due to #{e}")
|
30
32
|
@logger.debug(e.backtrace.join("\n"))
|
31
33
|
end
|
32
|
-
elsif
|
34
|
+
elsif ssh_keydata != nil
|
33
35
|
begin
|
34
|
-
|
36
|
+
remote_handler().connect(dns_name, "root", ssh_keydata)
|
35
37
|
connected = true
|
36
38
|
rescue Exception => e
|
37
39
|
@logger.info("connection failed due to #{e}")
|
@@ -47,82 +49,81 @@ module StateTransitionHelper
|
|
47
49
|
if !connected
|
48
50
|
raise Exception.new("connection attempts stopped")
|
49
51
|
end
|
50
|
-
|
51
|
-
|
52
|
-
@logger.info "connected to #{
|
52
|
+
os = remote_handler().retrieve_os()
|
53
|
+
post_message("connected to #{dns_name}. OS installed is #{os}")
|
54
|
+
@logger.info "connected to #{dns_name}"
|
55
|
+
return os
|
53
56
|
end
|
54
57
|
|
55
58
|
# Launch an instance based on an AMI ID
|
56
|
-
# Input Parameters
|
57
|
-
# *
|
58
|
-
# *
|
59
|
-
# *
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# *
|
63
|
-
# *
|
64
|
-
# *
|
65
|
-
# *
|
66
|
-
# *
|
67
|
-
|
68
|
-
|
69
|
-
@
|
70
|
-
@logger.debug "start up AMI #{@context[:ami_id]}"
|
59
|
+
# Input Parameters:
|
60
|
+
# * ami_id => ID of the AMI to be launched
|
61
|
+
# * key_name => name of the key to access the instance
|
62
|
+
# * security_group_name => name of the security group to be used
|
63
|
+
# Returned information:
|
64
|
+
# * instance_id => ID of the started instance
|
65
|
+
# * dns_name => DNS name of the started instance
|
66
|
+
# * availability_zone => Availability zone of the started instance
|
67
|
+
# * kernel_id => EC2 Kernel ID of the started instance
|
68
|
+
# * ramdisk_id => EC2 Ramdisk ID of the started instance
|
69
|
+
# * architecture => architecture (e.g. 386i, 64x) of the started instance
|
70
|
+
def launch_instance(ami_id, key_name, security_group_name)
|
71
|
+
post_message("starting up instance to execute the script (AMI = #{ami_id}) ...")
|
72
|
+
@logger.debug "start up AMI #{ami_id}"
|
71
73
|
# find out the image architecture first
|
72
|
-
image_props =
|
74
|
+
image_props = ec2_handler().describe_images(:image_id => ami_id)
|
73
75
|
architecture = image_props['imagesSet']['item'][0]['architecture']
|
74
76
|
instance_type = "m1.small"
|
75
77
|
if architecture != "i386"
|
76
78
|
instance_type = "m1.large"
|
77
79
|
end
|
78
|
-
arch_log_msg = "Architecture of image #{
|
80
|
+
arch_log_msg = "Architecture of image #{ami_id} is #{architecture}. Use instance_type #{instance_type}."
|
79
81
|
@logger.info arch_log_msg
|
80
|
-
|
82
|
+
post_message(arch_log_msg)
|
81
83
|
# now start it
|
82
|
-
res =
|
83
|
-
:security_group =>
|
84
|
+
res = ec2_handler().run_instances(:image_id => ami_id,
|
85
|
+
:security_group => security_group_name, :key_name => key_name,
|
84
86
|
:instance_type => instance_type
|
85
87
|
)
|
86
88
|
instance_id = res['instancesSet']['item'][0]['instanceId']
|
87
|
-
@context[:instance_id] = instance_id
|
88
89
|
@logger.info "started instance #{instance_id}"
|
89
|
-
|
90
|
+
post_message("Started instance #{instance_id}. wait until it is ready...")
|
90
91
|
#availability_zone , key_name/group_name
|
91
92
|
started = false
|
92
93
|
while started == false
|
93
94
|
sleep(5)
|
94
|
-
res =
|
95
|
+
res = ec2_handler().describe_instances(:instance_id => instance_id)
|
95
96
|
state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
|
96
97
|
@logger.info "instance is in state #{state['name']} (#{state['code']})"
|
97
98
|
if state['code'].to_i == 16
|
98
99
|
started = true
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
100
|
+
post_message("instance is up and running")
|
101
|
+
dns_name = res['reservationSet']['item'][0]['instancesSet']['item'][0]['dnsName']
|
102
|
+
availability_zone = res['reservationSet']['item'][0]['instancesSet']['item'][0]['placement']['availabilityZone']
|
103
|
+
kernel_id = res['reservationSet']['item'][0]['instancesSet']['item'][0]['kernelId']
|
104
|
+
ramdisk_id = res['reservationSet']['item'][0]['instancesSet']['item'][0]['ramdiskId']
|
105
|
+
architecture = res['reservationSet']['item'][0]['instancesSet']['item'][0]['architecture']
|
105
106
|
elsif state['code'].to_i != 0
|
106
|
-
|
107
|
+
post_message("instance in state #{state['name']}")
|
107
108
|
raise Exception.new('instance failed to start up')
|
108
109
|
else
|
109
|
-
|
110
|
+
post_message("instance still starting up...")
|
110
111
|
end
|
111
112
|
end
|
113
|
+
return instance_id, dns_name, availability_zone, kernel_id, ramdisk_id, architecture
|
112
114
|
end
|
113
115
|
|
114
116
|
# Shuts down an instance.
|
115
|
-
# Input Parameters
|
116
|
-
# *
|
117
|
-
|
118
|
-
|
119
|
-
@
|
120
|
-
|
121
|
-
res = @context[:ec2_api_handler].terminate_instances(:instance_id => @context[:instance_id])
|
117
|
+
# Input Parameters:
|
118
|
+
# * instance_id => ID of the instance to be shut down
|
119
|
+
def shut_down_instance(instance_id)
|
120
|
+
post_message("going to shut down the temporary instance #{instance_id}...")
|
121
|
+
@logger.debug "shutdown instance #{instance_id}"
|
122
|
+
res = ec2_handler().terminate_instances(:instance_id => instance_id)
|
122
123
|
done = false
|
123
124
|
while done == false
|
124
125
|
sleep(5)
|
125
|
-
res =
|
126
|
+
res = ec2_handler().describe_instances(:instance_id => instance_id)
|
126
127
|
state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
|
127
128
|
@logger.debug "instance in state #{state['name']} (#{state['code']})"
|
128
129
|
if state['code'].to_i == 48
|
@@ -131,245 +132,267 @@ module StateTransitionHelper
|
|
131
132
|
raise Exception.new('instance failed to shut down')
|
132
133
|
end
|
133
134
|
end
|
134
|
-
|
135
|
+
post_message("instance #{instance_id} is terminated")
|
135
136
|
end
|
136
137
|
|
137
138
|
# Creates a new EBS volume.
|
138
|
-
# Input Parameters
|
139
|
-
# *
|
140
|
-
# *
|
141
|
-
#
|
142
|
-
# *
|
143
|
-
def create_volume()
|
144
|
-
|
145
|
-
@logger.debug "create volume in zone #{
|
146
|
-
res =
|
147
|
-
|
139
|
+
# Input Parameters:
|
140
|
+
# * availability_zone => availability zone for the volume
|
141
|
+
# * size => size in Gigabytes
|
142
|
+
# Returns
|
143
|
+
# * volume_id => EC2 EBS Volume ID
|
144
|
+
def create_volume(availability_zone, size = "10")
|
145
|
+
post_message("going to create a new EBS volume of size #{size}GB...")
|
146
|
+
@logger.debug "create volume in zone #{availability_zone}"
|
147
|
+
res = ec2_handler().create_volume(:availability_zone => availability_zone, :size => size.to_s)
|
148
|
+
volume_id = res['volumeId']
|
148
149
|
started = false
|
149
150
|
while !started
|
150
151
|
sleep(5)
|
151
152
|
#TODO: check for timeout?
|
152
|
-
res =
|
153
|
+
res = ec2_handler().describe_volumes(:volume_id => volume_id)
|
153
154
|
state = res['volumeSet']['item'][0]['status']
|
154
155
|
@logger.debug "volume state #{state}"
|
155
156
|
if state == 'available'
|
156
157
|
started = true
|
157
158
|
end
|
158
159
|
end
|
159
|
-
|
160
|
+
post_message("EBS volume #{volume_id} is ready")
|
161
|
+
return volume_id
|
160
162
|
end
|
161
163
|
|
162
164
|
# Creates a new EBS volume from a snapshot ID.
|
163
|
-
# Input Parameters
|
164
|
-
# *
|
165
|
-
# *
|
166
|
-
# *
|
167
|
-
#
|
168
|
-
# *
|
169
|
-
def create_volume_from_snapshot
|
170
|
-
|
171
|
-
@logger.debug "create volume in zone #{
|
172
|
-
res =
|
173
|
-
|
165
|
+
# Input Parameters:
|
166
|
+
# * availability_zone => availability zone for the volume
|
167
|
+
# * size => size of the volume to be created
|
168
|
+
# * snapshot_id => EC2 Snapshot ID used to create the volume
|
169
|
+
# Returns
|
170
|
+
# * volume_id => EC2 EBS Volume ID created
|
171
|
+
def create_volume_from_snapshot(snapshot_id, availability_zone)
|
172
|
+
post_message("going to create a new EBS volume from the specified snapshot...")
|
173
|
+
@logger.debug "create volume in zone #{availability_zone}"
|
174
|
+
res = ec2_handler().create_volume(:snapshot_id => snapshot_id, :availability_zone => availability_zone)
|
175
|
+
volume_id = res['volumeId']
|
174
176
|
started = false
|
175
177
|
while !started
|
176
178
|
sleep(5)
|
177
179
|
#TODO: check for timeout?
|
178
|
-
res =
|
180
|
+
res = ec2_handler().describe_volumes(:volume_id => volume_id)
|
179
181
|
state = res['volumeSet']['item'][0]['status']
|
180
182
|
@logger.debug "volume state #{state}"
|
181
183
|
if state == 'available'
|
182
184
|
started = true
|
183
185
|
end
|
184
186
|
end
|
185
|
-
|
187
|
+
post_message("EBS volume #{volume_id} is ready")
|
188
|
+
return volume_id
|
186
189
|
end
|
187
190
|
|
188
191
|
# Attaches an EBS volume to an instance
|
189
|
-
# Input Parameters
|
190
|
-
# *
|
191
|
-
# *
|
192
|
-
# *
|
193
|
-
|
194
|
-
|
195
|
-
@
|
196
|
-
|
197
|
-
|
198
|
-
:
|
199
|
-
:device => @context[:temp_device_name]
|
192
|
+
# Input Parameters:
|
193
|
+
# * volume_id => EC2 ID for the EBS Volume to be attached
|
194
|
+
# * instance_id => EC2 ID for the instance to which the volume is supposed to be attached to
|
195
|
+
# * temp_device_name => device name to be used for attaching (e.g. /dev/sdj1)
|
196
|
+
def attach_volume(volume_id, instance_id, temp_device_name)
|
197
|
+
post_message("going to attach volume #{volume_id} to instance #{instance_id} on device #{temp_device_name}...")
|
198
|
+
@logger.debug "attach volume #{volume_id} to instance #{instance_id} on device #{temp_device_name}"
|
199
|
+
ec2_handler().attach_volume(:volume_id => volume_id,
|
200
|
+
:instance_id => instance_id,
|
201
|
+
:device => temp_device_name
|
200
202
|
)
|
201
203
|
done = false
|
202
204
|
while !done
|
203
205
|
sleep(5)
|
204
206
|
#TODO: check for timeout?
|
205
|
-
res =
|
207
|
+
res = ec2_handler().describe_volumes(:volume_id => volume_id)
|
206
208
|
state = res['volumeSet']['item'][0]['status']
|
207
209
|
@logger.debug "storage attaching: #{state}"
|
208
210
|
if state == 'in-use'
|
209
211
|
done = true
|
210
212
|
end
|
211
213
|
end
|
212
|
-
|
214
|
+
post_message("volume successfully attached")
|
213
215
|
end
|
214
216
|
|
215
217
|
# Detach an EBS volume from an instance.
|
216
|
-
# Input Parameters
|
217
|
-
# *
|
218
|
-
# *
|
219
|
-
|
220
|
-
|
221
|
-
@
|
222
|
-
|
223
|
-
|
224
|
-
:instance_id => @context[:instance_id]
|
218
|
+
# Input Parameters:
|
219
|
+
# * volume_id => EC2 ID for the EBS Volume to be detached
|
220
|
+
# * instance_id => EC2 ID for the instance to detach from
|
221
|
+
def detach_volume(volume_id, instance_id)
|
222
|
+
post_message("going to detach volume #{volume_id}...")
|
223
|
+
@logger.debug "detach volume #{volume_id}"
|
224
|
+
ec2_handler().detach_volume(:volume_id => volume_id,
|
225
|
+
:instance_id => instance_id
|
225
226
|
)
|
226
227
|
done = false
|
227
228
|
while !done
|
228
229
|
sleep(3)
|
229
230
|
#TODO: check for timeout?
|
230
|
-
res =
|
231
|
+
res = ec2_handler().describe_volumes(:volume_id => volume_id)
|
231
232
|
@logger.debug "volume detaching: #{res.inspect}"
|
232
233
|
if res['volumeSet']['item'][0]['status'] == 'available'
|
233
234
|
done = true
|
234
235
|
end
|
235
236
|
end
|
236
|
-
|
237
|
+
post_message("volume #{volume_id} detached.")
|
237
238
|
end
|
238
239
|
|
239
240
|
# Delete an EBS volume.
|
240
|
-
# Input Parameters
|
241
|
-
# *
|
242
|
-
|
243
|
-
|
244
|
-
@
|
245
|
-
|
246
|
-
|
247
|
-
@context[:script].post_message("volume #{@context[:volume_id]} deleted")
|
241
|
+
# Input Parameters:
|
242
|
+
# * volume_id => EC2 ID for the EBS Volume to be deleted
|
243
|
+
def delete_volume(volume_id)
|
244
|
+
post_message("going to delete volume #{volume_id} (no longer needed)...")
|
245
|
+
@logger.debug "delete volume #{volume_id}"
|
246
|
+
ec2_handler().delete_volume(:volume_id => volume_id)
|
247
|
+
post_message("volume #{volume_id} deleted")
|
248
248
|
end
|
249
249
|
|
250
250
|
# Creates a snapshot for an EBS volume.
|
251
|
-
# Input Parameters
|
252
|
-
# *
|
253
|
-
#
|
254
|
-
# *
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
@context[:snapshot_id] = res['snapshotId']
|
262
|
-
@logger.info "snapshot_id = #{@context[:snapshot_id]}"
|
251
|
+
# Input Parameters::
|
252
|
+
# * volume_id => EC2 ID for the EBS volume to be snapshotted
|
253
|
+
# Returns:
|
254
|
+
# * snapshot_id => EC2 ID for the snapshot created
|
255
|
+
def create_snapshot(volume_id)
|
256
|
+
post_message("going to create a snapshot for volume #{volume_id}...")
|
257
|
+
@logger.debug "create snapshot for volume #{volume_id}"
|
258
|
+
res = ec2_handler().create_snapshot(:volume_id => volume_id)
|
259
|
+
snapshot_id = res['snapshotId']
|
260
|
+
@logger.info "snapshot_id = #{snapshot_id}"
|
263
261
|
done = false
|
264
262
|
while !done
|
265
263
|
sleep(5)
|
266
264
|
#TODO: check for timeout?
|
267
|
-
res =
|
265
|
+
res = ec2_handler().describe_snapshots(:snapshot_id => snapshot_id)
|
268
266
|
@logger.debug "snapshot creating: #{res.inspect}"
|
269
267
|
if res['snapshotSet']['item'][0]['status'] == 'completed'
|
270
268
|
done = true
|
271
269
|
end
|
272
270
|
end
|
273
|
-
|
271
|
+
post_message("snapshot is done with ID=#{snapshot_id}")
|
272
|
+
return snapshot_id
|
274
273
|
end
|
275
274
|
|
276
275
|
# Registers a snapshot as EBS-booted AMI.
|
277
|
-
# Input Parameters
|
278
|
-
# *
|
279
|
-
# *
|
280
|
-
# *
|
281
|
-
# *
|
282
|
-
# *
|
283
|
-
# *
|
284
|
-
# *
|
285
|
-
#
|
286
|
-
#
|
287
|
-
|
288
|
-
|
289
|
-
@
|
290
|
-
|
291
|
-
|
292
|
-
:
|
293
|
-
:
|
294
|
-
:
|
295
|
-
:ramdisk_id => @context[:ramdisk_id]
|
276
|
+
# Input Parameters:
|
277
|
+
# * snapshot_id => EC2 Snapshot ID used to be used
|
278
|
+
# * name => name of the AMI to be created
|
279
|
+
# * root_device_name => Root device name (e.g. /dev/sdj) to be used for AMI registration
|
280
|
+
# * description => description of the AMI to be created
|
281
|
+
# * kernel_id => EC2 Kernel ID to be used for AMI registration
|
282
|
+
# * ramdisk_id => EC2 Ramdisk ID to be used for AMI registration
|
283
|
+
# * architecture => architecture (e.g. 386i, 64x) to be used for AMI registration
|
284
|
+
# Returns:
|
285
|
+
# * image_id => ID of the AMI created and registered
|
286
|
+
def register_snapshot(snapshot_id, name, root_device_name, description, kernel_id, ramdisk_id, architecture)
|
287
|
+
post_message("going to register snapshot #{snapshot_id}...")
|
288
|
+
@logger.debug "register snapshot #{snapshot_id} as #{name}"
|
289
|
+
res = ec2_handler().register_image_updated(:snapshot_id => snapshot_id,
|
290
|
+
:kernel_id => kernel_id, :architecture => architecture,
|
291
|
+
:root_device_name => root_device_name,
|
292
|
+
:description => description, :name => name,
|
293
|
+
:ramdisk_id => ramdisk_id
|
296
294
|
)
|
297
295
|
@logger.debug "result of registration = #{res.inspect}"
|
298
|
-
|
299
|
-
@logger.info "resulting image_id = #{
|
300
|
-
|
296
|
+
image_id = res['imageId']
|
297
|
+
@logger.info "resulting image_id = #{image_id}"
|
298
|
+
post_message("snapshot #{snapshot_id} successfully registered as AMI #{image_id} ")
|
299
|
+
return image_id
|
301
300
|
end
|
302
301
|
|
303
302
|
# Create a file-system on a given machine (assumes to be connected already).
|
304
|
-
# Input Parameters
|
305
|
-
# *
|
306
|
-
# *
|
307
|
-
|
308
|
-
|
309
|
-
@
|
310
|
-
|
311
|
-
|
312
|
-
@context[:script].post_message("filesystem system successfully created")
|
303
|
+
# Input Parameters:
|
304
|
+
# * dns_name => IP used
|
305
|
+
# * device => device to be used for file-system creation (e.g. /dev/sdj)
|
306
|
+
def create_fs(dns_name, device)
|
307
|
+
post_message("going to create filesystem on #{dns_name} to #{device}...")
|
308
|
+
@logger.debug "create filesystem on #{dns_name} to #{device}"
|
309
|
+
remote_handler().create_filesystem("ext3", device)
|
310
|
+
post_message("filesystem system successfully created")
|
313
311
|
end
|
314
312
|
|
315
313
|
# Create a file-system on a given machine (assumes to be connected already).
|
316
|
-
# Input Parameters
|
317
|
-
# *
|
318
|
-
# *
|
319
|
-
|
320
|
-
|
321
|
-
@
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
314
|
+
# Input Parameters:
|
315
|
+
# * mount_point => directory to be mounted on the device
|
316
|
+
# * device => device used for mounting
|
317
|
+
def mount_fs(mount_point, device)
|
318
|
+
post_message("going to mount #{device} on #{mount_point}...")
|
319
|
+
@logger.debug "mount #{device} on #{mount_point}"
|
320
|
+
if !remote_handler.file_exists?(mount_point)
|
321
|
+
remote_handler().mkdir(mount_point)
|
322
|
+
end
|
323
|
+
remote_handler().mount(device, mount_point)
|
324
|
+
trials = 3
|
325
|
+
mounted = false
|
326
|
+
while trials > 0
|
327
|
+
sleep(5) #give mount some time
|
328
|
+
if remote_handler().drive_mounted?(mount_point)
|
329
|
+
mounted = true
|
330
|
+
break
|
331
|
+
end
|
332
|
+
trials -= trials
|
329
333
|
end
|
330
|
-
|
334
|
+
if !mounted
|
335
|
+
raise Exception.new("drive #{mount_point} not mounted")
|
336
|
+
end
|
337
|
+
post_message("mount successful")
|
331
338
|
end
|
332
339
|
|
333
340
|
# Unmount a drive
|
334
|
-
# Input Parameters
|
335
|
-
# *
|
336
|
-
|
337
|
-
|
338
|
-
@
|
339
|
-
|
340
|
-
@context[:remote_command_handler].umount(@context[:path])
|
341
|
+
# Input Parameters:
|
342
|
+
# * mount_point => directory to be unmounted
|
343
|
+
def unmount_fs(mount_point)
|
344
|
+
post_message("Going to unmount ...")
|
345
|
+
@logger.debug "unmount #{mount_point}"
|
346
|
+
remote_handler().umount(mount_point)
|
341
347
|
sleep(2) #give umount some time
|
342
|
-
if
|
343
|
-
raise Exception.new("drive #{
|
348
|
+
if remote_handler().drive_mounted?(mount_point)
|
349
|
+
raise Exception.new("drive #{mount_point} not unmounted")
|
344
350
|
end
|
345
|
-
|
351
|
+
post_message("device unmounted")
|
346
352
|
end
|
347
353
|
|
348
354
|
# Copy all files of a running linux distribution via rsync to a mounted directory
|
349
|
-
# Input Parameters
|
350
|
-
# *
|
351
|
-
|
352
|
-
|
353
|
-
@
|
354
|
-
@logger.debug "start copying to #{@context[:path]}"
|
355
|
+
# Input Parameters:
|
356
|
+
# * destination_path => where to copy to
|
357
|
+
def copy_distribution(destination_path)
|
358
|
+
post_message("going to start copying files to #{destination_path}. This may take quite a time...")
|
359
|
+
@logger.debug "start copying to #{destination_path}"
|
355
360
|
start = Time.new.to_i
|
356
|
-
|
357
|
-
|
361
|
+
remote_handler().rsync("/", "#{destination_path}", "#{destination_path}")
|
362
|
+
remote_handler().rsync("/dev/", "#{destination_path}/dev/")
|
358
363
|
endtime = Time.new.to_i
|
359
364
|
@logger.info "copy took #{(endtime-start)}s"
|
360
|
-
|
365
|
+
post_message("copying is done (took #{endtime-start})s")
|
361
366
|
end
|
362
367
|
|
363
368
|
# Zips all files on a mounted-directory into a file
|
364
|
-
# Input Parameters
|
365
|
-
# *
|
366
|
-
# *
|
367
|
-
# #
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
369
|
+
# Input Parameters:
|
370
|
+
# * source_dir => where to copy from
|
371
|
+
# * zip_file_dest => path where the zip-file should be stored
|
372
|
+
# # zip_file_name => name of the zip file (without .zip suffix)
|
373
|
+
def zip_volume(source_dir, zip_file_dest, zip_file_name)
|
374
|
+
post_message("going to zip the EBS volume")
|
375
|
+
remote_handler().zip(source_dir, zip_file_dest+"/"+zip_file_name)
|
376
|
+
post_message("EBS volume successfully zipped")
|
377
|
+
end
|
378
|
+
|
379
|
+
protected
|
380
|
+
|
381
|
+
def post_message(msg)
|
382
|
+
if @context[:script] != nil
|
383
|
+
@context[:script].post_message(msg)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def remote_handler()
|
388
|
+
if @context[:remote_command_handler] == nil
|
389
|
+
@context[:remote_command_handler] = RemoteCommandHandler.new
|
390
|
+
end
|
391
|
+
@context[:remote_command_handler]
|
392
|
+
end
|
393
|
+
|
394
|
+
def ec2_handler()
|
395
|
+
@context[:ec2_api_handler]
|
373
396
|
end
|
374
397
|
|
375
398
|
end
|
@@ -76,7 +76,9 @@ class Ami2EbsConversion < Ec2Script
|
|
76
76
|
# which serves to create
|
77
77
|
class InitialState < Ami2EbsConversionState
|
78
78
|
def enter
|
79
|
-
|
79
|
+
@context[:instance_id], @context[:dns_name], @context[:availability_zone],
|
80
|
+
@context[:kernel_id], @context[:ramdisk_id], @context[:architecture] =
|
81
|
+
launch_instance(@context[:ami_id], @context[:key_name], @context[:security_group_name])
|
80
82
|
AmiStarted.new(@context)
|
81
83
|
end
|
82
84
|
end
|
@@ -84,7 +86,7 @@ class Ami2EbsConversion < Ec2Script
|
|
84
86
|
# Ami started. Create a storage
|
85
87
|
class AmiStarted < Ami2EbsConversionState
|
86
88
|
def enter
|
87
|
-
create_volume()
|
89
|
+
@context[:volume_id] = create_volume(@context[:availability_zone], "10")
|
88
90
|
StorageCreated.new(@context)
|
89
91
|
end
|
90
92
|
end
|
@@ -92,7 +94,7 @@ class Ami2EbsConversion < Ec2Script
|
|
92
94
|
# Storage created. Attach it.
|
93
95
|
class StorageCreated < Ami2EbsConversionState
|
94
96
|
def enter
|
95
|
-
attach_volume()
|
97
|
+
attach_volume(@context[:volume_id], @context[:instance_id], @context[:temp_device_name])
|
96
98
|
StorageAttached.new(@context)
|
97
99
|
end
|
98
100
|
end
|
@@ -100,8 +102,9 @@ class Ami2EbsConversion < Ec2Script
|
|
100
102
|
# Storage attached. Create a file-system and moun it
|
101
103
|
class StorageAttached < Ami2EbsConversionState
|
102
104
|
def enter
|
103
|
-
|
104
|
-
|
105
|
+
@context[:result][:os] =
|
106
|
+
connect(@context[:dns_name], @context[:ssh_keyfile], @context[:ssh_keydata])
|
107
|
+
create_fs(@context[:dns_name], @context[:temp_device_name])
|
105
108
|
FileSystemCreated.new(@context)
|
106
109
|
end
|
107
110
|
end
|
@@ -109,7 +112,8 @@ class Ami2EbsConversion < Ec2Script
|
|
109
112
|
# File system created. Mount it.
|
110
113
|
class FileSystemCreated < Ami2EbsConversionState
|
111
114
|
def enter
|
112
|
-
|
115
|
+
@context[:mount_dir] = "/mnt/tmp_#{@context[:volume_id]}"
|
116
|
+
mount_fs(@context[:mount_dir], @context[:temp_device_name])
|
113
117
|
FileSystemMounted.new(@context)
|
114
118
|
end
|
115
119
|
end
|
@@ -117,7 +121,7 @@ class Ami2EbsConversion < Ec2Script
|
|
117
121
|
# File system created and mounted. Copy the root partition.
|
118
122
|
class FileSystemMounted < Ami2EbsConversionState
|
119
123
|
def enter
|
120
|
-
|
124
|
+
copy_distribution(@context[:mount_dir])
|
121
125
|
CopyDone.new(@context)
|
122
126
|
end
|
123
127
|
end
|
@@ -125,7 +129,7 @@ class Ami2EbsConversion < Ec2Script
|
|
125
129
|
# Copy operation done. Unmount volume.
|
126
130
|
class CopyDone < Ami2EbsConversionState
|
127
131
|
def enter
|
128
|
-
unmount_fs()
|
132
|
+
unmount_fs(@context[:mount_dir])
|
129
133
|
VolumeUnmounted.new(@context)
|
130
134
|
end
|
131
135
|
end
|
@@ -133,7 +137,7 @@ class Ami2EbsConversion < Ec2Script
|
|
133
137
|
# Volume unmounted. Detach it.
|
134
138
|
class VolumeUnmounted < Ami2EbsConversionState
|
135
139
|
def enter
|
136
|
-
detach_volume()
|
140
|
+
detach_volume(@context[:volume_id], @context[:instance_id])
|
137
141
|
VolumeDetached.new(@context)
|
138
142
|
end
|
139
143
|
end
|
@@ -141,7 +145,7 @@ class Ami2EbsConversion < Ec2Script
|
|
141
145
|
# VolumeDetached. Create snaphot
|
142
146
|
class VolumeDetached < Ami2EbsConversionState
|
143
147
|
def enter
|
144
|
-
create_snapshot()
|
148
|
+
@context[:snapshot_id] = create_snapshot(@context[:volume_id])
|
145
149
|
SnapshotCreated.new(@context)
|
146
150
|
end
|
147
151
|
end
|
@@ -149,7 +153,7 @@ class Ami2EbsConversion < Ec2Script
|
|
149
153
|
# Snapshot created. Delete volume.
|
150
154
|
class SnapshotCreated < Ami2EbsConversionState
|
151
155
|
def enter
|
152
|
-
delete_volume()
|
156
|
+
delete_volume(@context[:volume_id])
|
153
157
|
VolumeDeleted.new(@context)
|
154
158
|
end
|
155
159
|
end
|
@@ -157,7 +161,9 @@ class Ami2EbsConversion < Ec2Script
|
|
157
161
|
# Volume deleted. Register snapshot.
|
158
162
|
class VolumeDeleted < Ami2EbsConversionState
|
159
163
|
def enter
|
160
|
-
register_snapshot(
|
164
|
+
@context[:result][:image_id] = register_snapshot(@context[:snapshot_id], @context[:name],
|
165
|
+
@context[:root_device_name], @context[:description], @context[:kernel_id],
|
166
|
+
@context[:ramdisk_id], @context[:architecture])
|
161
167
|
SnapshotRegistered.new(@context)
|
162
168
|
end
|
163
169
|
end
|
@@ -165,7 +171,7 @@ class Ami2EbsConversion < Ec2Script
|
|
165
171
|
# Snapshot registered. Shutdown instance.
|
166
172
|
class SnapshotRegistered < Ami2EbsConversionState
|
167
173
|
def enter
|
168
|
-
shut_down_instance()
|
174
|
+
shut_down_instance(@context[:instance_id])
|
169
175
|
Done.new(@context)
|
170
176
|
end
|
171
177
|
end
|
@@ -61,7 +61,8 @@ class DmEncrypt < Ec2Script
|
|
61
61
|
# Starting state. Tries to connect via ssh.
|
62
62
|
class InitialState < DmEncryptState
|
63
63
|
def enter
|
64
|
-
|
64
|
+
@context[:result][:os] =
|
65
|
+
connect(@context[:dns_name], @context[:ssh_keyfile], @context[:ssh_keydata])
|
65
66
|
install_tools()
|
66
67
|
end
|
67
68
|
|
@@ -2,6 +2,7 @@ require "help/script_execution_state"
|
|
2
2
|
require "scripts/ec2/ec2_script"
|
3
3
|
require "help/remote_command_handler"
|
4
4
|
require "help/dm_crypt_helper"
|
5
|
+
require "help/ec2_helper"
|
5
6
|
require "AWS"
|
6
7
|
|
7
8
|
# Script to download a specific snapshot as ZIP
|
@@ -31,11 +32,14 @@ class DownloadSnapshot < Ec2Script
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def check_input_parameters()
|
34
|
-
if @input_params[:
|
35
|
-
@input_params[:
|
35
|
+
if @input_params[:source_device] == nil
|
36
|
+
@input_params[:source_device] = "/dev/sdj1"
|
37
|
+
end
|
38
|
+
if @input_params[:dest_device] == nil
|
39
|
+
@input_params[:dest_device] = "/dev/sdj2"
|
36
40
|
end
|
37
41
|
if @input_params[:zip_file_dest] == nil
|
38
|
-
@input_params[:zip_file_dest] = "/var/www/html
|
42
|
+
@input_params[:zip_file_dest] = "/var/www/html"
|
39
43
|
end
|
40
44
|
if @input_params[:zip_file_name] == nil
|
41
45
|
@input_params[:zip_file_name] = "download"
|
@@ -61,11 +65,13 @@ class DownloadSnapshot < Ec2Script
|
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
64
|
-
#Connected.
|
65
68
|
# Start state. First thing to do is to launch the instance.
|
66
69
|
class InitialState < DownloadSnapshotState
|
67
70
|
def enter
|
68
|
-
launch_instance()
|
71
|
+
result = launch_instance(@context[:ami_id], @context[:key_name], @context[:security_group_name])
|
72
|
+
@context[:instance_id] = result.first
|
73
|
+
@context[:dns_name] = result[1]
|
74
|
+
@context[:availability_zone] = result[2]
|
69
75
|
InstanceLaunchedState.new(context)
|
70
76
|
end
|
71
77
|
end
|
@@ -73,42 +79,46 @@ class DownloadSnapshot < Ec2Script
|
|
73
79
|
# Instance Launched. Create a volume based on the snapshot.
|
74
80
|
class InstanceLaunchedState < DownloadSnapshotState
|
75
81
|
def enter
|
76
|
-
create_volume_from_snapshot()
|
77
|
-
|
82
|
+
@context[:source_volume_id] = create_volume_from_snapshot(@context[:snapshot_id], @context[:availability_zone])
|
83
|
+
ec2_helper = Ec2Helper.new(@context[:ec2_api_handler])
|
84
|
+
size = ec2_helper.volume_prop(@context[:source_volume_id], :size).to_i
|
85
|
+
puts "retrieved volume size of #{size}"
|
86
|
+
@context[:dest_volume_id] = create_volume(@context[:availability_zone], size)
|
87
|
+
VolumesCreated.new(@context)
|
78
88
|
end
|
79
89
|
|
80
90
|
end
|
81
91
|
|
82
|
-
#
|
83
|
-
class
|
84
|
-
def enter
|
85
|
-
attach_volume()
|
86
|
-
VolumeAttached.new(@context)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# Volume attached. Create a file-system and mount it.
|
91
|
-
class VolumeAttached < DownloadSnapshotState
|
92
|
+
# Volumes created. Attach it.
|
93
|
+
class VolumesCreated < DownloadSnapshotState
|
92
94
|
def enter
|
93
|
-
|
94
|
-
|
95
|
-
|
95
|
+
@context[:script].post_message("Going to create two volumes. One with the snapshot data, one to store the zipped data for download.")
|
96
|
+
attach_volume(@context[:source_volume_id], @context[:instance_id], @context[:source_device])
|
97
|
+
attach_volume(@context[:dest_volume_id], @context[:instance_id], @context[:dest_device])
|
98
|
+
VolumesAttached.new(@context)
|
96
99
|
end
|
97
100
|
end
|
98
101
|
|
99
|
-
#
|
100
|
-
class
|
102
|
+
# Volumes attached. Create a file-system for the destination one, and mount both.
|
103
|
+
class VolumesAttached < DownloadSnapshotState
|
101
104
|
def enter
|
102
|
-
|
103
|
-
|
105
|
+
@context[:script].post_message("Going to prepare the two volumes for the zip-operation.")
|
106
|
+
@context[:result][:os] =
|
107
|
+
connect(@context[:dns_name], @context[:ssh_keyfile], @context[:ssh_keydata])
|
108
|
+
source_dir = "/mnt/tmp_#{@context[:source_volume_id]}"
|
109
|
+
dest_dir = @context[:zip_file_dest]
|
110
|
+
create_fs(@context[:dns_name], @context[:dest_device])
|
111
|
+
mount_fs(source_dir, @context[:source_device])
|
112
|
+
mount_fs(dest_dir, @context[:dest_device])
|
113
|
+
FileSystemsReady.new(@context)
|
104
114
|
end
|
105
|
-
|
106
115
|
end
|
107
116
|
|
108
117
|
# File System mounted. Zip the complete directory on the EBS.
|
109
|
-
class
|
118
|
+
class FileSystemsReady < DownloadSnapshotState
|
110
119
|
def enter
|
111
|
-
|
120
|
+
mount_point = "/mnt/tmp_#{@context[:source_volume_id]}"
|
121
|
+
zip_volume(mount_point, @context[:zip_file_dest], @context[:zip_file_name])
|
112
122
|
VolumeZippedAndDownloadableState.new(@context)
|
113
123
|
end
|
114
124
|
|
@@ -136,7 +146,7 @@ class DownloadSnapshot < Ec2Script
|
|
136
146
|
# Snapshot can no longer be downloaded. Shut down the instance.
|
137
147
|
class DownloadStoppedState < DownloadSnapshotState
|
138
148
|
def enter
|
139
|
-
shut_down_instance()
|
149
|
+
shut_down_instance(@context[:instance_id])
|
140
150
|
InstanceShutDown.new(@context)
|
141
151
|
end
|
142
152
|
|
@@ -145,7 +155,8 @@ class DownloadSnapshot < Ec2Script
|
|
145
155
|
# Instance is shut down. Delete the volume created.
|
146
156
|
class InstanceShutDown < DownloadSnapshotState
|
147
157
|
def enter
|
148
|
-
delete_volume()
|
158
|
+
delete_volume(@context[:source_volume_id])
|
159
|
+
delete_volume(@context[:dest_volume_id])
|
149
160
|
Done.new(@context)
|
150
161
|
end
|
151
162
|
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: CloudyScripts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 14
|
9
|
+
version: 0.0.14
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Matthias Jung
|
@@ -9,29 +14,33 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-25 00:00:00 +01:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: amazon-ec2
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
23
29
|
version: "0"
|
24
|
-
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
25
32
|
- !ruby/object:Gem::Dependency
|
26
33
|
name: net-ssh
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - ">="
|
32
38
|
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
33
41
|
version: "0"
|
34
|
-
|
42
|
+
type: :runtime
|
43
|
+
version_requirements: *id002
|
35
44
|
description: Scripts to facilitate programming for infrastructure clouds.
|
36
45
|
email: matthias.jung@gmail.com
|
37
46
|
executables: []
|
@@ -70,18 +79,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
70
79
|
requirements:
|
71
80
|
- - ">="
|
72
81
|
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
73
84
|
version: "0"
|
74
|
-
version:
|
75
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
86
|
requirements:
|
77
87
|
- - ">="
|
78
88
|
- !ruby/object:Gem::Version
|
89
|
+
segments:
|
90
|
+
- 0
|
79
91
|
version: "0"
|
80
|
-
version:
|
81
92
|
requirements: []
|
82
93
|
|
83
94
|
rubyforge_project: cloudyscripts
|
84
|
-
rubygems_version: 1.3.
|
95
|
+
rubygems_version: 1.3.6
|
85
96
|
signing_key:
|
86
97
|
specification_version: 3
|
87
98
|
summary: Scripts to facilitate programming for infrastructure clouds.
|