CloudyScripts 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,477 +1,180 @@
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 "AWS"
6
-
7
- class AWS::EC2::Base
8
- def register_image_updated(options)
9
- params = {}
10
- params["Name"] = options[:name].to_s
11
- params["BlockDeviceMapping.1.Ebs.SnapshotId"] = options[:snapshot_id].to_s
12
- params["BlockDeviceMapping.1.DeviceName"] = options[:root_device_name].to_s
13
- params["Description"] = options[:description].to_s
14
- params["KernelId"] = options[:kernel_id].to_s
15
- params["RamdiskId"] = options[:ramdisk_id].to_s
16
- params["Architecture"] = options[:architecture].to_s
17
- params["RootDeviceName"] = options[:root_device_name].to_s
18
- return response_generator(:action => "RegisterImage", :params => params)
19
- end
20
- end
21
-
22
- # Creates a bootable EBS storage from an existing AMI.
23
- #
24
-
25
- class Ami2EbsConversion < Ec2Script
26
- # Input parameters
27
- # * aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
28
- # * aws_secret_key => the Amazon AWS Secret Key
29
- # * ami_id => the ID of the AMI to be converted
30
- # * security_group_name => name of the security group to start
31
- # * ssh_key_data => Key information for the security group that starts the AMI [if not set, use ssh_key_files]
32
- # * ssh_key_files => Key information for the security group that starts the AMI
33
- # * remote_command_handler => object that allows to connect via ssh and execute commands (optional)
34
- # * ec2_api_handler => object that allows to access the EC2 API (optional)
35
- # * ec2_api_server => server to connect to (option, default is us-east-1.ec2.amazonaws.com)
36
- # * name => the name of the AMI to be created
37
- # * description => description on AMI to be created (optional)
38
- # * temp_device_name => [default /dev/sdj] device name used to attach the temporary storage; change this only if there's already a volume attacged as /dev/sdj (optional, default is /dev/sdj)
39
- # * root_device_name"=> [default /dev/sda1] device name used for the root device (optional)
40
- def initialize(input_params)
41
- super(input_params)
42
- end
43
-
44
- # Executes the script.
45
- def start_script()
46
- begin
47
- # optional parameters and initialization
48
- if @input_params[:name] == nil
49
- @input_params[:name] = "Boot EBS (for AMI #{@input_params[:ami_id]}) at #{Time.now.strftime('%d/%m/%Y %H.%M.%S')}"
50
- else
51
- end
52
- if @input_params[:description] == nil
53
- @input_params[:description] = @input_params[:name]
54
- end
55
- if @input_params[:temp_device_name] == nil
56
- @input_params[:temp_device_name] = "/dev/sdj"
57
- end
58
- if @input_params[:root_device_name] == nil
59
- @input_params[:root_device_name] = "/dev/sda1"
60
- end
61
- @input_params[:script] = self
62
- # start state machine
63
- current_state = Ami2EbsConversionState.load_state(@input_params)
64
- @state_change_listeners.each() {|listener|
65
- current_state.register_state_change_listener(listener)
66
- }
67
- end_state = current_state.start_state_machine()
68
- if end_state.failed?
69
- @result[:failed] = true
70
- @result[:failure_reason] = current_state.end_state.failure_reason
71
- @result[:end_state] = current_state.end_state
72
- else
73
- @result[:failed] = false
74
- end
75
- rescue Exception => e
76
- @logger.warn "exception during encryption: #{e}"
77
- @logger.warn e.backtrace.join("\n")
78
- err = e.to_s
79
- err += " (in #{current_state.end_state.to_s})" unless current_state == nil
80
- @result[:failed] = true
81
- @result[:failure_reason] = err
82
- @result[:end_state] = current_state.end_state unless current_state == nil
83
- ensure
84
- begin
85
- @input_params[:remote_command_handler].disconnect
86
- rescue Exception => e2
87
- end
88
- end
89
- #
90
- @result[:done] = true
91
- end
92
-
93
- # Returns a hash with the following information:
94
- # :done => if execution is done
95
- #
96
- def get_execution_result
97
- @result
98
- end
99
-
100
- private
101
-
102
- # Here begins the state machine implementation
103
- class Ami2EbsConversionState < ScriptExecutionState
104
- def self.load_state(context)
105
- state = context[:initial_state] == nil ? InitialState.new(context) : context[:initial_state]
106
- state
107
- end
108
-
109
- def connect
110
- @context[:script].post_message("connecting to #{@context[:dns_name]}...")
111
- if @context[:remote_command_handler] == nil
112
- @context[:remote_command_handler] = RemoteCommandHandler.new
113
- end
114
- connected = false
115
- remaining_trials = 3
116
- while !connected && remaining_trials > 0
117
- remaining_trials -= 1
118
- if @context[:ssh_keyfile] != nil
119
- begin
120
- @context[:remote_command_handler].connect_with_keyfile(@context[:dns_name], @context[:ssh_keyfile])
121
- connected = true
122
- rescue Exception => e
123
- @logger.info("connection failed due to #{e}")
124
- @logger.debug(e.backtrace.join("\n"))
125
- end
126
- elsif @context[:ssh_keydata] != nil
127
- begin
128
- @context[:remote_command_handler].connect(@context[:dns_name], "root", @context[:ssh_keydata])
129
- connected = true
130
- rescue Exception => e
131
- @logger.info("connection failed due to #{e}")
132
- @logger.debug(e.backtrace.join("\n"))
133
- end
134
- else
135
- raise Exception.new("no key information specified")
136
- end
137
- if !connected
138
- sleep(5) #try again
139
- end
140
- end
141
- if !connected
142
- raise Exception.new("connection attempts stopped")
143
- end
144
- @context[:result][:os] = @context[:remote_command_handler].retrieve_os()
145
- @context[:script].post_message("connected to #{@context[:dns_name]}. OS installed is #{@context[:result][:os]}")
146
- @logger.info "connected to #{@context[:dns_name]}"
147
- end
148
-
149
- end
150
-
151
- # Nothing done yet. Start by instantiating an AMI (in the right zone?)
152
- # which serves to create
153
- class InitialState < Ami2EbsConversionState
154
- def enter
155
- startup_ami()
156
- end
157
-
158
- private
159
-
160
- def startup_ami()
161
- @context[:script].post_message("starting up a new instance for AMI #{@context[:ami_id]}...")
162
- @logger.debug "start up AMI #{@context[:ami_id]}"
163
- res = @context[:ec2_api_handler].run_instances(:image_id => @context[:ami_id],
164
- :security_group => @context[:security_group_name], :key_name => @context[:key_name])
165
- instance_id = res['instancesSet']['item'][0]['instanceId']
166
- @context[:instance_id] = instance_id
167
- @logger.info "started instance #{instance_id}"
168
- @context[:script].post_message("started instance #{instance_id}. wait until it is ready...")
169
- #availability_zone , key_name/group_name
170
- started = false
171
- while started == false
172
- sleep(5)
173
- res = @context[:ec2_api_handler].describe_instances(:instance_id => @context[:instance_id])
174
- state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
175
- @logger.info "instance is in state #{state['name']} (#{state['code']})"
176
- if state['code'].to_i == 16
177
- started = true
178
- @context[:script].post_message("instance is up and running")
179
- @context[:dns_name] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['dnsName']
180
- @context[:availability_zone] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['placement']['availabilityZone']
181
- @context[:kernel_id] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['kernelId']
182
- @context[:ramdisk_id] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['ramdiskId']
183
- @context[:architecture] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['architecture']
184
- elsif state['code'].to_i != 0
185
- @context[:script].post_message("instance in state #{state['name']}")
186
- raise Exception.new('instance failed to start up')
187
- else
188
- @context[:script].post_message("instance still starting up...")
189
- end
190
- end
191
- AmiStarted.new(@context)
192
- end
193
- end
194
-
195
- # Ami started. Create a storage
196
- class AmiStarted < Ami2EbsConversionState
197
- def enter
198
- create_storage()
199
- end
200
-
201
- private
202
-
203
- def create_storage()
204
- @context[:script].post_message("going to create a new EBS volume...")
205
- @logger.debug "create volume in zone #{@context[:availability_zone]}"
206
- res = @context[:ec2_api_handler].create_volume(:availability_zone => @context[:availability_zone], :size => "10")
207
- @context[:volume_id] = res['volumeId']
208
- started = false
209
- while !started
210
- sleep(5)
211
- #TODO: check for timeout?
212
- res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
213
- state = res['volumeSet']['item'][0]['status']
214
- @logger.debug "volume state #{state}"
215
- if state == 'available'
216
- started = true
217
- end
218
- end
219
- @context[:script].post_message("EBS volume #{@context[:volume_id]} is ready")
220
- StorageCreated.new(@context)
221
- end
222
-
223
- end
224
-
225
- # Storage created. Attach it.
226
- class StorageCreated < Ami2EbsConversionState
227
- def enter
228
- attach_storage()
229
- end
230
-
231
- private
232
-
233
- def attach_storage()
234
- @context[:script].post_message("going to attach volume #{@context[:volume_id]} to instance #{@context[:instance_id]} on device #{@context[:temp_device_name]}...")
235
- @logger.debug "attach volume #{@context[:volume_id]} to instance #{@context[:instance_id]} on device #{@context[:temp_device_name]}"
236
- @context[:ec2_api_handler].attach_volume(:volume_id => @context[:volume_id],
237
- :instance_id => @context[:instance_id],
238
- :device => @context[:temp_device_name]
239
- )
240
- done = false
241
- while !done
242
- sleep(5)
243
- #TODO: check for timeout?
244
- res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
245
- state = res['volumeSet']['item'][0]['status']
246
- @logger.debug "storage attaching: #{state}"
247
- if state == 'in-use'
248
- done = true
249
- end
250
- end
251
- @context[:script].post_message("volume successfully attached")
252
- StorageAttached.new(@context)
253
- end
254
-
255
- end
256
-
257
- # Storage attached. Create a file-system and moun it
258
- class StorageAttached < Ami2EbsConversionState
259
- def enter
260
- create_fs()
261
- end
262
-
263
- private
264
-
265
- def create_fs()
266
- @context[:script].post_message("going to create filesystem on #{@context[:dns_name]} to #{@context[:temp_device_name]}...")
267
- @logger.debug "create filesystem on #{@context[:dns_name]} to #{@context[:temp_device_name]}"
268
- connect()
269
- @context[:remote_command_handler].create_filesystem("ext3", @context[:temp_device_name])
270
- @context[:script].post_message("filesystem system successfully created")
271
- FileSystemCreated.new(@context)
272
- end
273
- end
274
-
275
- # File system created. Mount it.
276
- class FileSystemCreated < Ami2EbsConversionState
277
- def enter
278
- mount_fs()
279
- end
280
-
281
- private
282
-
283
- def mount_fs()
284
- @context[:path] = "/mnt/tmp_#{@context[:volume_id]}"
285
- @context[:script].post_message("going to mount #{@context[:temp_device_name]} on #{@context[:path]}...")
286
- @logger.debug "mount #{@context[:temp_device_name]} on #{@context[:path]}"
287
- @context[:remote_command_handler].mkdir(@context[:path])
288
- @context[:remote_command_handler].mount(@context[:temp_device_name], @context[:path])
289
- sleep(2) #give mount some time
290
- if !@context[:remote_command_handler].drive_mounted?(@context[:path])
291
- raise Exception.new("drive #{@context[:path]} not mounted")
292
- end
293
- @context[:script].post_message("mount successful")
294
- FileSystemMounted.new(@context)
295
- end
296
- end
297
-
298
- # File system created and mounted. Copy the root partition.
299
- class FileSystemMounted < Ami2EbsConversionState
300
- def enter
301
- copy()
302
- end
303
-
304
- private
305
-
306
- def copy()
307
- @context[:script].post_message("going to start copying files to #{@context[:path]}. This may take quite a time...")
308
- @logger.debug "start copying to #{@context[:path]}"
309
- start = Time.new.to_i
310
- @context[:remote_command_handler].rsync("/", "#{@context[:path]}", "/mnt/")
311
- @context[:remote_command_handler].rsync("/dev/", "#{@context[:path]}/dev/")
312
- endtime = Time.new.to_i
313
- @logger.info "copy took #{(endtime-start)}s"
314
- @context[:script].post_message("copying is done (took #{endtime-start})s")
315
- CopyDone.new(@context)
316
- end
317
- end
318
-
319
- # Copy operation done. Unmount volume.
320
- class CopyDone < Ami2EbsConversionState
321
- def enter
322
- unmount()
323
- end
324
-
325
- private
326
-
327
- def unmount()
328
- @context[:script].post_message("going to clean things up. Start with unmounting ...")
329
- @logger.debug "unmount #{@context[:path]}"
330
- @context[:remote_command_handler].umount(@context[:path])
331
- sleep(2) #give umount some time
332
- if @context[:remote_command_handler].drive_mounted?(@context[:path])
333
- raise Exception.new("drive #{@context[:path]} not unmounted")
334
- end
335
- @context[:script].post_message("device unmounted")
336
- VolumeUnmounted.new(@context)
337
- end
338
- end
339
-
340
- # Volume unmounted. Detach it.
341
- class VolumeUnmounted < Ami2EbsConversionState
342
- def enter
343
- detach()
344
- end
345
-
346
- private
347
-
348
- def detach()
349
- @context[:script].post_message("going to detach volume #{@context[:volume_id]}...")
350
- @logger.debug "detach volume #{@context[:volume_id]}"
351
- @context[:ec2_api_handler].detach_volume(:volume_id => @context[:volume_id],
352
- :instance_id => @context[:instance_id]
353
- )
354
- done = false
355
- while !done
356
- sleep(3)
357
- #TODO: check for timeout?
358
- res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
359
- @logger.debug "volume detaching: #{res.inspect}"
360
- if res['volumeSet']['item'][0]['status'] == 'available'
361
- done = true
362
- end
363
- end
364
- @context[:script].post_message("volume #{@context[:volume_id]} detached.")
365
- VolumeDetached.new(@context)
366
- end
367
- end
368
-
369
-
370
- # VolumeDetached. Create snaphot
371
- class VolumeDetached < Ami2EbsConversionState
372
- def enter
373
- create_snapshot()
374
- end
375
-
376
- private
377
-
378
- def create_snapshot()
379
- @context[:script].post_message("going to create a snapshot...")
380
- @logger.debug "create snapshot for volume #{@context[:volume_id]}"
381
- res = @context[:ec2_api_handler].create_snapshot(:volume_id => @context[:volume_id])
382
- @context[:snapshot_id] = res['snapshotId']
383
- @logger.info "snapshot_id = #{@context[:snapshot_id]}"
384
- done = false
385
- while !done
386
- sleep(5)
387
- #TODO: check for timeout?
388
- res = @context[:ec2_api_handler].describe_snapshots(:snapshot_id => @context[:snapshot_id])
389
- @logger.debug "snapshot creating: #{res.inspect}"
390
- if res['snapshotSet']['item'][0]['status'] == 'completed'
391
- done = true
392
- end
393
- end
394
- @context[:script].post_message("snapshot is done with ID=#{@context[:snapshot_id]}")
395
- SnapshotCreated.new(@context)
396
- end
397
- end
398
-
399
- # Snapshot created. Delete volume.
400
- class SnapshotCreated < Ami2EbsConversionState
401
- def enter
402
- delete_volume()
403
- end
404
-
405
- private
406
-
407
- def delete_volume
408
- @context[:script].post_message("going to delete volume #{@context[:volume_id]} (no longer needed)...")
409
- @logger.debug "delete volume #{@context[:volume_id]}"
410
- res = @context[:ec2_api_handler].delete_volume(:volume_id => @context[:volume_id])
411
- @context[:script].post_message("volume #{@context[:volume_id]} deleted")
412
- VolumeDeleted.new(@context)
413
- end
414
- end
415
-
416
- # Volume deleted. Register snapshot.
417
- class VolumeDeleted < Ami2EbsConversionState
418
- def enter
419
- register()
420
- end
421
-
422
- private
423
-
424
- def register()
425
- @context[:script].post_message("going to register snapshot #{@context[:snapshot_id]}...")
426
- @logger.debug "register snapshot #{@context[:snapshot_id]} as #{@context[:name]}"
427
- res = @context[:ec2_api_handler].register_image_updated(:snapshot_id => @context[:snapshot_id],
428
- :kernel_id => @context[:kernel_id], :architecture => @context[:architecture],
429
- :root_device_name => @context[:root_device_name],
430
- :description => @context[:description], :name => @context[:name],
431
- :ramdisk_id => @context[:ramdisk_id]
432
- )
433
- @logger.debug "result of registration = #{res.inspect}"
434
- @context[:result][:image_id] = res['imageId']
435
- @logger.info "resulting image_id = #{@context[:result][:image_id]}"
436
- @context[:script].post_message("snapshot #{@context[:snapshot_id]} successfully registered as AMI #{@context[:result][:image_id]} ")
437
- SnapshotRegistered.new(@context)
438
- end
439
- end
440
-
441
- # Snapshot registered. Shutdown instance.
442
- class SnapshotRegistered < Ami2EbsConversionState
443
- def enter
444
- shut_down()
445
- end
446
-
447
- private
448
-
449
- def shut_down()
450
- @context[:script].post_message("going to shut down the temporary instance #{@context[:instance_id]}...")
451
- @logger.debug "shutdown instance #{@context[:instance_id]}"
452
- res = @context[:ec2_api_handler].terminate_instances(:instance_id => @context[:instance_id])
453
- done = false
454
- while done == false
455
- sleep(5)
456
- res = @context[:ec2_api_handler].describe_instances(:instance_id => @context[:instance_id])
457
- state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
458
- @logger.debug "instance in state #{state['name']} (#{state['code']})"
459
- if state['code'].to_i == 48
460
- done = true
461
- elsif state['code'].to_i != 32
462
- raise Exception.new('instance failed to shut down')
463
- end
464
- end
465
- @context[:script].post_message("instance #{@context[:instance_id]} is terminated")
466
- Done.new(@context)
467
- end
468
- end
469
-
470
- # Instance shutdown. Done.
471
- class Done < Ami2EbsConversionState
472
- def done?
473
- true
474
- end
475
- end
476
-
477
- end
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 "AWS"
6
+
7
+ class AWS::EC2::Base
8
+ def register_image_updated(options)
9
+ params = {}
10
+ params["Name"] = options[:name].to_s
11
+ params["BlockDeviceMapping.1.Ebs.SnapshotId"] = options[:snapshot_id].to_s
12
+ params["BlockDeviceMapping.1.DeviceName"] = options[:root_device_name].to_s
13
+ params["Description"] = options[:description].to_s
14
+ params["KernelId"] = options[:kernel_id].to_s
15
+ params["RamdiskId"] = options[:ramdisk_id].to_s
16
+ params["Architecture"] = options[:architecture].to_s
17
+ params["RootDeviceName"] = options[:root_device_name].to_s
18
+ return response_generator(:action => "RegisterImage", :params => params)
19
+ end
20
+ end
21
+
22
+ # Creates a bootable EBS storage from an existing AMI.
23
+ #
24
+
25
+ class Ami2EbsConversion < Ec2Script
26
+ # Input parameters
27
+ # * aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
28
+ # * aws_secret_key => the Amazon AWS Secret Key
29
+ # * ami_id => the ID of the AMI to be converted
30
+ # * security_group_name => name of the security group to start
31
+ # * ssh_key_data => Key information for the security group that starts the AMI [if not set, use ssh_key_files]
32
+ # * ssh_key_files => Key information for the security group that starts the AMI
33
+ # * remote_command_handler => object that allows to connect via ssh and execute commands (optional)
34
+ # * ec2_api_handler => object that allows to access the EC2 API (optional)
35
+ # * ec2_api_server => server to connect to (option, default is us-east-1.ec2.amazonaws.com)
36
+ # * name => the name of the AMI to be created
37
+ # * description => description on AMI to be created (optional)
38
+ # * temp_device_name => [default /dev/sdj] device name used to attach the temporary storage; change this only if there's already a volume attacged as /dev/sdj (optional, default is /dev/sdj)
39
+ # * root_device_name"=> [default /dev/sda1] device name used for the root device (optional)
40
+ def initialize(input_params)
41
+ super(input_params)
42
+ end
43
+
44
+ def check_input_parameters()
45
+ if @input_params[:name] == nil
46
+ @input_params[:name] = "Boot EBS (for AMI #{@input_params[:ami_id]}) at #{Time.now.strftime('%d/%m/%Y %H.%M.%S')}"
47
+ else
48
+ end
49
+ if @input_params[:description] == nil
50
+ @input_params[:description] = @input_params[:name]
51
+ end
52
+ if @input_params[:temp_device_name] == nil
53
+ @input_params[:temp_device_name] = "/dev/sdj"
54
+ end
55
+ if @input_params[:root_device_name] == nil
56
+ @input_params[:root_device_name] = "/dev/sda1"
57
+ end
58
+ end
59
+
60
+ def load_initial_state()
61
+ Ami2EbsConversionState.load_state(@input_params)
62
+ end
63
+
64
+ private
65
+
66
+ # Here begins the state machine implementation
67
+ class Ami2EbsConversionState < ScriptExecutionState
68
+ def self.load_state(context)
69
+ state = context[:initial_state] == nil ? InitialState.new(context) : context[:initial_state]
70
+ state
71
+ end
72
+
73
+ end
74
+
75
+ # Nothing done yet. Start by instantiating an AMI (in the right zone?)
76
+ # which serves to create
77
+ class InitialState < Ami2EbsConversionState
78
+ def enter
79
+ launch_instance()
80
+ AmiStarted.new(@context)
81
+ end
82
+ end
83
+
84
+ # Ami started. Create a storage
85
+ class AmiStarted < Ami2EbsConversionState
86
+ def enter
87
+ create_volume()
88
+ StorageCreated.new(@context)
89
+ end
90
+ end
91
+
92
+ # Storage created. Attach it.
93
+ class StorageCreated < Ami2EbsConversionState
94
+ def enter
95
+ attach_volume()
96
+ StorageAttached.new(@context)
97
+ end
98
+ end
99
+
100
+ # Storage attached. Create a file-system and moun it
101
+ class StorageAttached < Ami2EbsConversionState
102
+ def enter
103
+ connect()
104
+ create_fs()
105
+ FileSystemCreated.new(@context)
106
+ end
107
+ end
108
+
109
+ # File system created. Mount it.
110
+ class FileSystemCreated < Ami2EbsConversionState
111
+ def enter
112
+ mount_fs()
113
+ FileSystemMounted.new(@context)
114
+ end
115
+ end
116
+
117
+ # File system created and mounted. Copy the root partition.
118
+ class FileSystemMounted < Ami2EbsConversionState
119
+ def enter
120
+ copy()
121
+ CopyDone.new(@context)
122
+ end
123
+ end
124
+
125
+ # Copy operation done. Unmount volume.
126
+ class CopyDone < Ami2EbsConversionState
127
+ def enter
128
+ unmount_fs()
129
+ VolumeUnmounted.new(@context)
130
+ end
131
+ end
132
+
133
+ # Volume unmounted. Detach it.
134
+ class VolumeUnmounted < Ami2EbsConversionState
135
+ def enter
136
+ detach_volume()
137
+ VolumeDetached.new(@context)
138
+ end
139
+ end
140
+
141
+ # VolumeDetached. Create snaphot
142
+ class VolumeDetached < Ami2EbsConversionState
143
+ def enter
144
+ create_snapshot()
145
+ SnapshotCreated.new(@context)
146
+ end
147
+ end
148
+
149
+ # Snapshot created. Delete volume.
150
+ class SnapshotCreated < Ami2EbsConversionState
151
+ def enter
152
+ delete_volume()
153
+ VolumeDeleted.new(@context)
154
+ end
155
+ end
156
+
157
+ # Volume deleted. Register snapshot.
158
+ class VolumeDeleted < Ami2EbsConversionState
159
+ def enter
160
+ register_snapshot()
161
+ SnapshotRegistered.new(@context)
162
+ end
163
+ end
164
+
165
+ # Snapshot registered. Shutdown instance.
166
+ class SnapshotRegistered < Ami2EbsConversionState
167
+ def enter
168
+ shut_down_instance()
169
+ Done.new(@context)
170
+ end
171
+ end
172
+
173
+ # Instance shutdown. Done.
174
+ class Done < Ami2EbsConversionState
175
+ def done?
176
+ true
177
+ end
178
+ end
179
+
180
+ end