CloudyScripts 0.0.11 → 0.0.12

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.
@@ -1,97 +1,108 @@
1
- # Implements a little state-machine.
2
- # Usage: for every state you need, extend this class.
3
- # The method enter() must be implemented for every state you code and
4
- # return another state.
5
- class ScriptExecutionState
6
- # context information for the state (hash)
7
- attr_reader :context, :logger
8
-
9
- def initialize(context)
10
- @context = context
11
- @state_change_listeners = []
12
- @logger = context[:logger]
13
- if @logger == nil
14
- @logger = Logger.new(STDOUT)
15
- @logger.level = Logger::WARN
16
- end
17
- end
18
-
19
- # Listener should extend #StateChangeListener (or implement a
20
- # state_changed(state) method). Note: calls are synchronous.
21
- def register_state_change_listener(listener)
22
- @state_change_listeners << listener
23
- end
24
-
25
- # Start the state machine using this state as initial state.
26
- def start_state_machine
27
- @current_state = self
28
- @logger.info "start state machine with #{@current_state.to_s}"
29
- while !@current_state.done? && !@current_state.failed?
30
- begin
31
- @logger.info "state machine: current state = #{@current_state.to_s}"
32
- @current_state = @current_state.enter()
33
- notify_state_change_listeners(@current_state)
34
- rescue Exception => e
35
- if @context[:result] != nil
36
- @context[:result][:details] = e.backtrace().join("\n")
37
- end
38
- @current_state = FailedState.new(@context, e.to_s, @current_state)
39
- notify_state_change_listeners(@current_state)
40
- @logger.warn "StateMachine exception during execution: #{e}"
41
- @logger.warn "#{e.backtrace.join("\n")}"
42
- end
43
- end
44
- @current_state
45
- end
46
-
47
- # Returns the state that is reached after execution.
48
- def end_state
49
- @current_state
50
- end
51
-
52
- # To be implemented. Executes the code for this state.
53
- def enter
54
- raise Exception.new("TaskExecutionState is abstract")
55
- end
56
-
57
- # To be implemented. Indicates if the final state is reached.
58
- def done?
59
- false
60
- end
61
-
62
- # To be implemented. Indicates if the final state is a failure state.
63
- def failed?
64
- false
65
- end
66
-
67
- def to_s
68
- s = self.class.to_s
69
- s.sub(/.*\:\:/,'')
70
- end
71
-
72
- # Standard state reached when an exception occurs.
73
- class FailedState < ScriptExecutionState
74
- attr_accessor :failure_reason, :from_state
75
- def initialize(context, failure_reason, from_state)
76
- super(context)
77
- @failure_reason = failure_reason
78
- @from_state = from_state
79
- end
80
- def done?
81
- true
82
- end
83
- def failed?
84
- true
85
- end
86
- end
87
-
88
- private
89
-
90
- # Notifies all listeners of state changes
91
- def notify_state_change_listeners(state)
92
- @state_change_listeners.each() {|listener|
93
- listener.state_changed(state)
94
- }
95
- end
96
-
97
- end
1
+ require "help/state_transition_helper"
2
+
3
+ # Implements a little state-machine.
4
+ # Usage: for every state you need, extend this class.
5
+ # The method enter() must be implemented for every state you code and
6
+ # return another state.
7
+ class ScriptExecutionState
8
+ include StateTransitionHelper
9
+
10
+ # context information for the state (hash)
11
+ attr_reader :context, :logger
12
+
13
+ def initialize(context)
14
+ @context = context
15
+ @state_change_listeners = []
16
+ @logger = context[:logger]
17
+ if @logger == nil
18
+ @logger = Logger.new(STDOUT)
19
+ @logger.level = Logger::WARN
20
+ end
21
+ end
22
+
23
+ # Listener should extend #StateChangeListener (or implement a
24
+ # state_changed(state) method). Note: calls are synchronous.
25
+ def register_state_change_listener(listener)
26
+ @state_change_listeners << listener
27
+ end
28
+
29
+ # Start the state machine using this state as initial state.
30
+ def start_state_machine
31
+ @current_state = self
32
+ @logger.info "start state machine with #{@current_state.to_s}"
33
+ while !@current_state.done? && !@current_state.failed?
34
+ begin
35
+ @logger.info "state machine: current state = #{@current_state.to_s}"
36
+ @current_state = @current_state.enter()
37
+ notify_state_change_listeners(@current_state)
38
+ rescue Exception => e
39
+ if @context[:result] != nil
40
+ @context[:result][:details] = e.backtrace().join("\n")
41
+ end
42
+ @current_state = FailedState.new(@context, e.to_s, @current_state)
43
+ notify_state_change_listeners(@current_state)
44
+ @logger.warn "StateMachine exception during execution: #{e}"
45
+ @logger.warn "#{e.backtrace.join("\n")}"
46
+ end
47
+ end
48
+ @current_state
49
+ end
50
+
51
+ # Returns the state that is reached after execution.
52
+ def end_state
53
+ @current_state
54
+ end
55
+
56
+ # To be implemented. Executes the code for this state.
57
+ def enter
58
+ raise Exception.new("TaskExecutionState is abstract")
59
+ end
60
+
61
+ # To be implemented. Indicates if the final state is reached.
62
+ def done?
63
+ false
64
+ end
65
+
66
+ # To be implemented. Indicates if the final state is a failure state.
67
+ def failed?
68
+ false
69
+ end
70
+
71
+ def to_s
72
+ s = self.class.to_s
73
+ s.sub(/.*\:\:/,'')
74
+ end
75
+
76
+ private
77
+
78
+ # Notifies all listeners of state changes
79
+ def notify_state_change_listeners(state)
80
+ @state_change_listeners.each() {|listener|
81
+ listener.state_changed(state)
82
+ }
83
+ end
84
+
85
+ end
86
+
87
+ # Standard state reached when an exception occurs.
88
+ class FailedState < ScriptExecutionState
89
+ attr_accessor :failure_reason, :from_state
90
+ def initialize(context, failure_reason, from_state)
91
+ super(context)
92
+ @failure_reason = failure_reason
93
+ @from_state = from_state
94
+ end
95
+ def done?
96
+ true
97
+ end
98
+ def failed?
99
+ true
100
+ end
101
+ end
102
+
103
+ # Done.
104
+ class Done < ScriptExecutionState
105
+ def done?
106
+ true
107
+ end
108
+ end
@@ -1,13 +1,13 @@
1
- # Defines a template for a class that allows to be notified
2
- # on state-changes in #ScriptExecutionState. A listener must be
3
- # registered via #ScriptExecutionState::register_state_change_listener.
4
- # When a state changes the method #state_changed is called.
5
-
6
- class StateChangeListener
7
- # Method called when a state changes. Note: calls are synchronous, the
8
- # listener should return quickly and handle more complicated routines
9
- # in a different thread.
10
- def state_changed(state)
11
- raise Exception.new("StateChangeListener: state change notification not implemented")
12
- end
13
- end
1
+ # Defines a template for a class that allows to be notified
2
+ # on state-changes in #ScriptExecutionState. A listener must be
3
+ # registered via #ScriptExecutionState::register_state_change_listener.
4
+ # When a state changes the method #state_changed is called.
5
+
6
+ class StateChangeListener
7
+ # Method called when a state changes. Note: calls are synchronous, the
8
+ # listener should return quickly and handle more complicated routines
9
+ # in a different thread.
10
+ def state_changed(state)
11
+ raise Exception.new("StateChangeListener: state change notification not implemented")
12
+ end
13
+ end
@@ -0,0 +1,362 @@
1
+ # Contains methods that are used by the scripts in the state-machines. Since
2
+ # they are reused by different scripts, they are factored into this module.
3
+ # Parameters are read from the @context variable that must be defined. Results
4
+ # are written into @context[:result][...]
5
+ # Note: @context[:script] is set to a script object to pass information and messages
6
+ # to listeners
7
+
8
+ module StateTransitionHelper
9
+
10
+ # Connects to the remote host via SSH.
11
+ # Params in @context:
12
+ # * :dns_name => machine to connect to
13
+ # * :ssh_keyfile => key-file used for ssh OR :ssh_keydata => contents of key-file
14
+ # * :remote_command_handler => ssh wrapper object
15
+ def connect
16
+ @context[:script].post_message("connecting to #{@context[:dns_name]}...")
17
+ if @context[:remote_command_handler] == nil
18
+ @context[:remote_command_handler] = RemoteCommandHandler.new
19
+ end
20
+ connected = false
21
+ remaining_trials = 3
22
+ while !connected && remaining_trials > 0
23
+ remaining_trials -= 1
24
+ if @context[:ssh_keyfile] != nil
25
+ begin
26
+ @context[:remote_command_handler].connect_with_keyfile(@context[:dns_name], @context[:ssh_keyfile])
27
+ connected = true
28
+ rescue Exception => e
29
+ @logger.info("connection failed due to #{e}")
30
+ @logger.debug(e.backtrace.join("\n"))
31
+ end
32
+ elsif @context[:ssh_keydata] != nil
33
+ begin
34
+ @context[:remote_command_handler].connect(@context[:dns_name], "root", @context[:ssh_keydata])
35
+ connected = true
36
+ rescue Exception => e
37
+ @logger.info("connection failed due to #{e}")
38
+ @logger.debug(e.backtrace.join("\n"))
39
+ end
40
+ else
41
+ raise Exception.new("no key information specified")
42
+ end
43
+ if !connected
44
+ sleep(5) #try again
45
+ end
46
+ end
47
+ if !connected
48
+ raise Exception.new("connection attempts stopped")
49
+ end
50
+ @context[:result][:os] = @context[:remote_command_handler].retrieve_os()
51
+ @context[:script].post_message("connected to #{@context[:dns_name]}. OS installed is #{@context[:result][:os]}")
52
+ @logger.info "connected to #{@context[:dns_name]}"
53
+ end
54
+
55
+ # Launch an instance based on an AMI ID
56
+ # Input Parameters in @context:
57
+ # * :ami_id => ID of the AMI to be launched
58
+ # * :ec2_api_handler => wrapper object around EC2 API access
59
+ # * :key_name => name of the key to access the instance
60
+ # * :security_group_name => name of the security group to be used
61
+ # Output information set by this method in @context:
62
+ # * :instance_id => ID of the started instance
63
+ # * :dns_name => DNS name of the started instance
64
+ # * :availability_zone => Availability zone of the started instance
65
+ # * :kernel_id => EC2 Kernel ID of the started instance
66
+ # * :ramdisk_id => EC2 Ramdisk ID of the started instance
67
+ # * :architecture => architecture (e.g. 386i, 64x) of the started instance
68
+ def launch_instance
69
+ @context[:script].post_message("starting up instance to execute the script (AMI = #{@context[:ami_id]}) ...")
70
+ @logger.debug "start up AMI #{@context[:ami_id]}"
71
+ res = @context[:ec2_api_handler].run_instances(:image_id => @context[:ami_id],
72
+ :security_group => @context[:security_group_name], :key_name => @context[:key_name])
73
+ instance_id = res['instancesSet']['item'][0]['instanceId']
74
+ @context[:instance_id] = instance_id
75
+ @logger.info "started instance #{instance_id}"
76
+ @context[:script].post_message("started instance #{instance_id}. wait until it is ready...")
77
+ #availability_zone , key_name/group_name
78
+ started = false
79
+ while started == false
80
+ sleep(5)
81
+ res = @context[:ec2_api_handler].describe_instances(:instance_id => @context[:instance_id])
82
+ state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
83
+ @logger.info "instance is in state #{state['name']} (#{state['code']})"
84
+ if state['code'].to_i == 16
85
+ started = true
86
+ @context[:script].post_message("instance is up and running")
87
+ @context[:dns_name] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['dnsName']
88
+ @context[:availability_zone] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['placement']['availabilityZone']
89
+ @context[:kernel_id] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['kernelId']
90
+ @context[:ramdisk_id] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['ramdiskId']
91
+ @context[:architecture] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['architecture']
92
+ elsif state['code'].to_i != 0
93
+ @context[:script].post_message("instance in state #{state['name']}")
94
+ raise Exception.new('instance failed to start up')
95
+ else
96
+ @context[:script].post_message("instance still starting up...")
97
+ end
98
+ end
99
+ end
100
+
101
+ # Shuts down an instance.
102
+ # Input Parameters in @context:
103
+ # * :instance_id => ID of the instance to be shut down
104
+ # * :ec2_api_handler => wrapper object around EC2 API access
105
+ def shut_down_instance()
106
+ @context[:script].post_message("going to shut down the temporary instance #{@context[:instance_id]}...")
107
+ @logger.debug "shutdown instance #{@context[:instance_id]}"
108
+ res = @context[:ec2_api_handler].terminate_instances(:instance_id => @context[:instance_id])
109
+ done = false
110
+ while done == false
111
+ sleep(5)
112
+ res = @context[:ec2_api_handler].describe_instances(:instance_id => @context[:instance_id])
113
+ state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
114
+ @logger.debug "instance in state #{state['name']} (#{state['code']})"
115
+ if state['code'].to_i == 48
116
+ done = true
117
+ elsif state['code'].to_i != 32
118
+ raise Exception.new('instance failed to shut down')
119
+ end
120
+ end
121
+ @context[:script].post_message("instance #{@context[:instance_id]} is terminated")
122
+ end
123
+
124
+ # Creates a new EBS volume.
125
+ # Input Parameters in @context:
126
+ # * :availability_zone => availability zone for the volume
127
+ # * :ec2_api_handler => wrapper object around EC2 API access
128
+ # Output information set by this method in @context:
129
+ # * :volume_id => EC2 EBS Volume ID
130
+ def create_volume()
131
+ @context[:script].post_message("going to create a new EBS volume...")
132
+ @logger.debug "create volume in zone #{@context[:availability_zone]}"
133
+ res = @context[:ec2_api_handler].create_volume(:availability_zone => @context[:availability_zone], :size => "10")
134
+ @context[:volume_id] = res['volumeId']
135
+ started = false
136
+ while !started
137
+ sleep(5)
138
+ #TODO: check for timeout?
139
+ res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
140
+ state = res['volumeSet']['item'][0]['status']
141
+ @logger.debug "volume state #{state}"
142
+ if state == 'available'
143
+ started = true
144
+ end
145
+ end
146
+ @context[:script].post_message("EBS volume #{@context[:volume_id]} is ready")
147
+ end
148
+
149
+ # Creates a new EBS volume from a snapshot ID.
150
+ # Input Parameters in @context:
151
+ # * :availability_zone => availability zone for the volume
152
+ # * :snapshot_id => EC2 Snapshot ID used to create the volume
153
+ # * :ec2_api_handler => wrapper object around EC2 API access
154
+ # Output information set by this method in @context:
155
+ # * :volume_id => EC2 EBS Volume ID created
156
+ def create_volume_from_snapshot
157
+ @context[:script].post_message("going to create a new EBS volume from the specified snapshot...")
158
+ @logger.debug "create volume in zone #{@context[:availability_zone]}"
159
+ res = @context[:ec2_api_handler].create_volume(:snapshot_id => @context[:snapshot_id], :availability_zone => @context[:availability_zone], :size => "10")
160
+ @context[:volume_id] = res['volumeId']
161
+ started = false
162
+ while !started
163
+ sleep(5)
164
+ #TODO: check for timeout?
165
+ res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
166
+ state = res['volumeSet']['item'][0]['status']
167
+ @logger.debug "volume state #{state}"
168
+ if state == 'available'
169
+ started = true
170
+ end
171
+ end
172
+ @context[:script].post_message("EBS volume #{@context[:volume_id]} is ready")
173
+ end
174
+
175
+ # Attaches an EBS volume to an instance
176
+ # Input Parameters in @context:
177
+ # * :volume_id => EC2 ID for the EBS Volume to be attached
178
+ # * :instance_id => EC2 ID for the instance to which the volume is supposed to be attached to
179
+ # * :temp_device_name => device name to be used for attaching (e.g. /dev/sdj1)
180
+ # * :ec2_api_handler => wrapper object around EC2 API access
181
+ def attach_volume
182
+ @context[:script].post_message("going to attach volume #{@context[:volume_id]} to instance #{@context[:instance_id]} on device #{@context[:temp_device_name]}...")
183
+ @logger.debug "attach volume #{@context[:volume_id]} to instance #{@context[:instance_id]} on device #{@context[:temp_device_name]}"
184
+ @context[:ec2_api_handler].attach_volume(:volume_id => @context[:volume_id],
185
+ :instance_id => @context[:instance_id],
186
+ :device => @context[:temp_device_name]
187
+ )
188
+ done = false
189
+ while !done
190
+ sleep(5)
191
+ #TODO: check for timeout?
192
+ res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
193
+ state = res['volumeSet']['item'][0]['status']
194
+ @logger.debug "storage attaching: #{state}"
195
+ if state == 'in-use'
196
+ done = true
197
+ end
198
+ end
199
+ @context[:script].post_message("volume successfully attached")
200
+ end
201
+
202
+ # Detach an EBS volume from an instance.
203
+ # Input Parameters in @context:
204
+ # * :volume_id => EC2 ID for the EBS Volume to be detached
205
+ # * :instance_id => EC2 ID for the instance to detach from
206
+ # * :ec2_api_handler => wrapper object around EC2 API access
207
+ def detach_volume()
208
+ @context[:script].post_message("going to detach volume #{@context[:volume_id]}...")
209
+ @logger.debug "detach volume #{@context[:volume_id]}"
210
+ @context[:ec2_api_handler].detach_volume(:volume_id => @context[:volume_id],
211
+ :instance_id => @context[:instance_id]
212
+ )
213
+ done = false
214
+ while !done
215
+ sleep(3)
216
+ #TODO: check for timeout?
217
+ res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
218
+ @logger.debug "volume detaching: #{res.inspect}"
219
+ if res['volumeSet']['item'][0]['status'] == 'available'
220
+ done = true
221
+ end
222
+ end
223
+ @context[:script].post_message("volume #{@context[:volume_id]} detached.")
224
+ end
225
+
226
+ # Delete an EBS volume.
227
+ # Input Parameters in @context:
228
+ # * :volume_id => EC2 ID for the EBS Volume to be deleted
229
+ # * :ec2_api_handler => wrapper object around EC2 API access
230
+ def delete_volume
231
+ @context[:script].post_message("going to delete volume #{@context[:volume_id]} (no longer needed)...")
232
+ @logger.debug "delete volume #{@context[:volume_id]}"
233
+ @context[:ec2_api_handler].delete_volume(:volume_id => @context[:volume_id])
234
+ @context[:script].post_message("volume #{@context[:volume_id]} deleted")
235
+ end
236
+
237
+ # Creates a snapshot for an EBS volume.
238
+ # Input Parameters in @context:
239
+ # * :volume_id => EC2 ID for the EBS volume to be snapshotted
240
+ # * :snapshot_id => EC2 Snapshot ID used to create the volume
241
+ # * :ec2_api_handler => wrapper object around EC2 API access
242
+ # Output information set by this method in @context:
243
+ # * :snapshot_id => EC2 ID for the snapshot created
244
+ def create_snapshot()
245
+ @context[:script].post_message("going to create a snapshot...")
246
+ @logger.debug "create snapshot for volume #{@context[:volume_id]}"
247
+ res = @context[:ec2_api_handler].create_snapshot(:volume_id => @context[:volume_id])
248
+ @context[:snapshot_id] = res['snapshotId']
249
+ @logger.info "snapshot_id = #{@context[:snapshot_id]}"
250
+ done = false
251
+ while !done
252
+ sleep(5)
253
+ #TODO: check for timeout?
254
+ res = @context[:ec2_api_handler].describe_snapshots(:snapshot_id => @context[:snapshot_id])
255
+ @logger.debug "snapshot creating: #{res.inspect}"
256
+ if res['snapshotSet']['item'][0]['status'] == 'completed'
257
+ done = true
258
+ end
259
+ end
260
+ @context[:script].post_message("snapshot is done with ID=#{@context[:snapshot_id]}")
261
+ end
262
+
263
+ # Registers a snapshot as EBS-booted AMI.
264
+ # Input Parameters in @context:
265
+ # * :snapshot_id => EC2 Snapshot ID used to be used
266
+ # * :name => name of the AMI to be created
267
+ # * :root_device_name => Root device name (e.g. /dev/sdj) to be used for AMI registration
268
+ # * :description => description of the AMI to be created
269
+ # * :kernel_id => EC2 Kernel ID to be used for AMI registration
270
+ # * :ramdisk_id => EC2 Ramdisk ID to be used for AMI registration
271
+ # * :architecture => architecture (e.g. 386i, 64x) to be used for AMI registration
272
+ # * :ec2_api_handler => wrapper object around EC2 API access
273
+ # Output information set by this method in @context:
274
+ # * {:result => :image_id} => ID of the AMI created and registered
275
+ def register_snapshot()
276
+ @context[:script].post_message("going to register snapshot #{@context[:snapshot_id]}...")
277
+ @logger.debug "register snapshot #{@context[:snapshot_id]} as #{@context[:name]}"
278
+ res = @context[:ec2_api_handler].register_image_updated(:snapshot_id => @context[:snapshot_id],
279
+ :kernel_id => @context[:kernel_id], :architecture => @context[:architecture],
280
+ :root_device_name => @context[:root_device_name],
281
+ :description => @context[:description], :name => @context[:name],
282
+ :ramdisk_id => @context[:ramdisk_id]
283
+ )
284
+ @logger.debug "result of registration = #{res.inspect}"
285
+ @context[:result][:image_id] = res['imageId']
286
+ @logger.info "resulting image_id = #{@context[:result][:image_id]}"
287
+ @context[:script].post_message("snapshot #{@context[:snapshot_id]} successfully registered as AMI #{@context[:result][:image_id]} ")
288
+ end
289
+
290
+ # Create a file-system on a given machine (assumes to be connected already).
291
+ # Input Parameters in @context:
292
+ # * :dns_name => IP used
293
+ # * :temp_device_name => device name to be used #TODO: give it a better name
294
+ # * :remote_command_handler => ssh wrapper object
295
+ def create_fs()
296
+ @context[:script].post_message("going to create filesystem on #{@context[:dns_name]} to #{@context[:temp_device_name]}...")
297
+ @logger.debug "create filesystem on #{@context[:dns_name]} to #{@context[:temp_device_name]}"
298
+ @context[:remote_command_handler].create_filesystem("ext3", @context[:temp_device_name])
299
+ @context[:script].post_message("filesystem system successfully created")
300
+ end
301
+
302
+ # Create a file-system on a given machine (assumes to be connected already).
303
+ # Input Parameters in @context:
304
+ # * :path => mount point #TODO: give it a better name
305
+ # * :temp_device_name => device used for mounting #TODO: give it a better name
306
+ # * :remote_command_handler => ssh wrapper object
307
+ def mount_fs()
308
+ @context[:path] = "/mnt/tmp_#{@context[:volume_id]}"
309
+ @context[:script].post_message("going to mount #{@context[:temp_device_name]} on #{@context[:path]}...")
310
+ @logger.debug "mount #{@context[:temp_device_name]} on #{@context[:path]}"
311
+ @context[:remote_command_handler].mkdir(@context[:path])
312
+ @context[:remote_command_handler].mount(@context[:temp_device_name], @context[:path])
313
+ sleep(2) #give mount some time
314
+ if !@context[:remote_command_handler].drive_mounted?(@context[:path])
315
+ raise Exception.new("drive #{@context[:path]} not mounted")
316
+ end
317
+ @context[:script].post_message("mount successful")
318
+ end
319
+
320
+ # Unmount a drive
321
+ # Input Parameters in @context:
322
+ # * :path => mount point #TODO: give it a better name
323
+ # * :remote_command_handler => ssh wrapper object
324
+ def unmount_fs()
325
+ @context[:script].post_message("Going to unmount ...")
326
+ @logger.debug "unmount #{@context[:path]}"
327
+ @context[:remote_command_handler].umount(@context[:path])
328
+ sleep(2) #give umount some time
329
+ if @context[:remote_command_handler].drive_mounted?(@context[:path])
330
+ raise Exception.new("drive #{@context[:path]} not unmounted")
331
+ end
332
+ @context[:script].post_message("device unmounted")
333
+ end
334
+
335
+ # Copy all files of a running linux distribution via rsync to a mounted directory
336
+ # Input Parameters in @context:
337
+ # * :path => where to copy to
338
+ # * :remote_command_handler => ssh wrapper object
339
+ def copy()
340
+ @context[:script].post_message("going to start copying files to #{@context[:path]}. This may take quite a time...")
341
+ @logger.debug "start copying to #{@context[:path]}"
342
+ start = Time.new.to_i
343
+ @context[:remote_command_handler].rsync("/", "#{@context[:path]}", "/mnt/")
344
+ @context[:remote_command_handler].rsync("/dev/", "#{@context[:path]}/dev/")
345
+ endtime = Time.new.to_i
346
+ @logger.info "copy took #{(endtime-start)}s"
347
+ @context[:script].post_message("copying is done (took #{endtime-start})s")
348
+ end
349
+
350
+ # Zips all files on a mounted-directory into a file
351
+ # Input Parameters in @context:
352
+ # * :path => where to copy from
353
+ # * :zip_file_dest => path where the zip-file should be stored
354
+ # # :zip_file_name => name of the zip file (without .zip suffix)
355
+ # * :remote_command_handler => ssh wrapper object
356
+ def zip_volume
357
+ @context[:script].post_message("going to zip the EBS volume")
358
+ @context[:remote_command_handler].zip(@context[:path], @context[:zip_file_dest]+@context[:zip_file_name])
359
+ @context[:script].post_message("EBS volume successfully zipped")
360
+ end
361
+
362
+ end