Module: StateTransitionHelper
- Included in:
- ScriptExecutionState
- Defined in:
- lib/help/state_transition_helper.rb
Instance Method Summary collapse
-
#attach_volume(volume_id, instance_id, temp_device_name) ⇒ Object
Attaches an EBS volume to an instance Input Parameters: * volume_id => EC2 ID for the EBS Volume to be attached * instance_id => EC2 ID for the instance to which the volume is supposed to be attached to * temp_device_name => device name to be used for attaching (e.g. /dev/sdj1).
-
#connect(dns_name, user_name, ssh_keyfile = nil, ssh_keydata = nil) ⇒ Object
Connects to the remote host via SSH.
-
#copy_distribution(destination_path) ⇒ Object
Copy all files of a running linux distribution via rsync to a mounted directory Input Parameters: * destination_path => where to copy to.
-
#create_fs(dns_name, device) ⇒ Object
Create a file-system on a given machine (assumes to be connected already).
-
#create_snapshot(volume_id, description = "") ⇒ Object
Creates a snapshot for an EBS volume.
-
#create_volume(availability_zone, size = "10") ⇒ Object
Creates a new EBS volume.
-
#create_volume_from_snapshot(snapshot_id, availability_zone) ⇒ Object
Creates a new EBS volume from a snapshot ID.
-
#delete_snapshot(snapshot_id) ⇒ Object
Deletes a snapshot.
-
#delete_volume(volume_id) ⇒ Object
Delete an EBS volume.
-
#detach_volume(volume_id, instance_id) ⇒ Object
Detach an EBS volume from an instance.
-
#disconnect ⇒ Object
If a remote command handler is connected, disconnect him silently.
- #ec2_handler ⇒ Object
- #ec2_handler=(ec2_handler) ⇒ Object
-
#launch_instance(ami_id, key_name, security_group_name, ec2_handler = nil) ⇒ Object
Launch an instance based on an AMI ID Input Parameters: * ami_id => ID of the AMI to be launched * key_name => name of the key to access the instance * security_group_name => name of the security group to be used Returned information: * instance_id => ID of the started instance * dns_name => DNS name of the started instance * availability_zone => Availability zone of the started instance * kernel_id => EC2 Kernel ID of the started instance * ramdisk_id => EC2 Ramdisk ID of the started instance * architecture => architecture (e.g. 386i, 64x) of the started instance.
-
#mount_fs(mount_point, device) ⇒ Object
Create a file-system on a given machine (assumes to be connected already).
-
#register_snapshot(snapshot_id, name, root_device_name, description, kernel_id, ramdisk_id, architecture) ⇒ Object
Registers a snapshot as EBS-booted AMI.
- #remote_copy(keyname, source_dir, dest_machine, dest_dir) ⇒ Object
-
#remote_handler ⇒ Object
setting/retrieving handlers.
- #remote_handler=(remote_handler) ⇒ Object
-
#shut_down_instance(instance_id) ⇒ Object
Shuts down an instance.
-
#unmount_fs(mount_point) ⇒ Object
Unmount a drive Input Parameters: * mount_point => directory to be unmounted.
- #upload_file(ip, user, key_data, file, target_file) ⇒ Object
-
#zip_volume(source_dir, zip_file_dest, zip_file_name) ⇒ Object
Zips all files on a mounted-directory into a file Input Parameters: * source_dir => where to copy from * zip_file_dest => path where the zip-file should be stored # zip_file_name => name of the zip file (without .zip suffix).
Instance Method Details
#attach_volume(volume_id, instance_id, temp_device_name) ⇒ Object
Attaches an EBS volume to an instance Input Parameters:
-
volume_id => EC2 ID for the EBS Volume to be attached
-
instance_id => EC2 ID for the instance to which the volume is supposed to be attached to
-
temp_device_name => device name to be used for attaching (e.g. /dev/sdj1)
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/help/state_transition_helper.rb', line 231 def attach_volume(volume_id, instance_id, temp_device_name) ("going to attach volume #{volume_id} to instance #{instance_id} on device #{temp_device_name}...") @logger.debug "attach volume #{volume_id} to instance #{instance_id} on device #{temp_device_name}" ec2_handler().attach_volume(:volume_id => volume_id, :instance_id => instance_id, :device => temp_device_name ) done = false while !done sleep(5) #TODO: check for timeout? res = ec2_handler().describe_volumes(:volume_id => volume_id) state = res['volumeSet']['item'][0]['status'] @logger.debug "storage attaching: #{state}" if state == 'in-use' done = true end end ("volume successfully attached") end |
#connect(dns_name, user_name, ssh_keyfile = nil, ssh_keydata = nil) ⇒ Object
Connects to the remote host via SSH. Params:
-
dns_name => machine to connect to
-
user_name => name to be used for connection
-
ssh_keyfile => key-file used for ssh
-
ssh_keydata => contents of key-file (either use ssh_keyfile or ssh_keydata)
Returns:
-
OS of the connected machine
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/help/state_transition_helper.rb', line 40 def connect(dns_name, user_name, ssh_keyfile = nil, ssh_keydata = nil) ("connecting '#{user_name}' to #{dns_name}...") connected = false last_connection_problem = "" remaining_trials = 5 while !connected && remaining_trials > 0 remaining_trials -= 1 if ssh_keyfile != nil begin @logger.info("connecting using keyfile") remote_handler().connect_with_keyfile(dns_name, ssh_keyfile) connected = true rescue Exception => e @logger.info("connection failed due to #{e}") last_connection_problem = "#{e}" @logger.debug(e.backtrace.select(){|line| line.include?("state_transition_helper")}.join("\n")) end elsif ssh_keydata != nil begin @logger.info("connecting using keydata") remote_handler().connect(dns_name, user_name, ssh_keydata) connected = true rescue Exception => e @logger.info("connection failed due to #{e}") last_connection_problem = "#{e}" @logger.debug(e.backtrace.select(){|line| line.include?("state_transition_helper")}.join("\n")) end else raise Exception.new("no key information specified") end if !connected sleep(20) #try again end end if !connected raise Exception.new("connection attempts stopped (#{last_connection_problem})") end os = remote_handler().retrieve_os() ("connected to #{dns_name}. OS installed is #{os}") @logger.info "connected to #{dns_name}" return os end |
#copy_distribution(destination_path) ⇒ Object
Copy all files of a running linux distribution via rsync to a mounted directory Input Parameters:
-
destination_path => where to copy to
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 |
# File 'lib/help/state_transition_helper.rb', line 405 def copy_distribution(destination_path) ("going to start copying files to #{destination_path}. This may take quite a time...") @logger.debug "start copying to #{destination_path}" start = Time.new.to_i if remote_handler().tools_installed?("rsync") @logger.debug "use rsync command line" status = remote_handler().local_rsync("/", "#{destination_path}", "#{destination_path}") status = remote_handler().local_rsync("/dev/", "#{destination_path}/dev/") if status == false raise Exception.new("failed to copy distribution remotely using rsync") end else @logger.debug "use cp command line" status = remote_handler().local_rcopy("/", "#{destination_path}", "/proc /sys /dev /mnt") if status == false raise Exception.new("failed to copy distribution remotely using cp") end status = remote_handler().mkdir("#{destination_path}/proc") status = remote_handler().mkdir("#{destination_path}/sys") status = remote_handler().mkdir("#{destination_path}/mnt") status = remote_handler().mkdir("#{destination_path}/dev") end endtime = Time.new.to_i @logger.info "copy took #{(endtime-start)}s" ("copying is done (took #{endtime-start})s") end |
#create_fs(dns_name, device) ⇒ Object
Create a file-system on a given machine (assumes to be connected already). Input Parameters:
-
dns_name => IP used
-
device => device to be used for file-system creation (e.g. /dev/sdj)
351 352 353 354 355 356 357 358 359 |
# File 'lib/help/state_transition_helper.rb', line 351 def create_fs(dns_name, device) ("going to create filesystem on #{dns_name} to #{device}...") @logger.debug "create filesystem on #{dns_name} to #{device}" status = remote_handler().create_filesystem("ext3", device) if status == false raise Exception.new("failed to create ext3 filesystem on #{device} device on #{dns_name}") end ("filesystem system successfully created") end |
#create_snapshot(volume_id, description = "") ⇒ Object
Creates a snapshot for an EBS volume.
- Input Parameters
-
volume_id => EC2 ID for the EBS volume to be snapshotted
Returns:
-
snapshot_id => EC2 ID for the snapshot created
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/help/state_transition_helper.rb', line 290 def create_snapshot(volume_id, description = "") ("going to create a snapshot for volume #{volume_id}...") @logger.debug "create snapshot for volume #{volume_id}" res = ec2_handler().create_snapshot(:volume_id => volume_id, :description => description) snapshot_id = res['snapshotId'] @logger.info "snapshot_id = #{snapshot_id}" done = false while !done sleep(5) #TODO: check for timeout? res = ec2_handler().describe_snapshots(:snapshot_id => snapshot_id) @logger.debug "snapshot creating: #{res.inspect}" if res['snapshotSet']['item'][0]['status'] == 'completed' done = true end end ("snapshot is done with ID=#{snapshot_id}") return snapshot_id end |
#create_volume(availability_zone, size = "10") ⇒ Object
Creates a new EBS volume. Input Parameters:
-
availability_zone => availability zone for the volume
-
size => size in Gigabytes
Returns
-
volume_id => EC2 EBS Volume ID
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/help/state_transition_helper.rb', line 179 def create_volume(availability_zone, size = "10") ("going to create a new EBS volume of size #{size}GB...") @logger.debug "create volume in zone #{availability_zone}" res = ec2_handler().create_volume(:availability_zone => availability_zone, :size => size.to_s) volume_id = res['volumeId'] started = false while !started sleep(5) #TODO: check for timeout? res = ec2_handler().describe_volumes(:volume_id => volume_id) state = res['volumeSet']['item'][0]['status'] @logger.debug "volume state #{state}" if state == 'available' started = true end end ("EBS volume #{volume_id} is ready") return volume_id end |
#create_volume_from_snapshot(snapshot_id, availability_zone) ⇒ Object
Creates a new EBS volume from a snapshot ID. Input Parameters:
-
availability_zone => availability zone for the volume
-
size => size of the volume to be created
-
snapshot_id => EC2 Snapshot ID used to create the volume
Returns
-
volume_id => EC2 EBS Volume ID created
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/help/state_transition_helper.rb', line 206 def create_volume_from_snapshot(snapshot_id, availability_zone) ("going to create a new EBS volume from the specified snapshot...") @logger.debug "create volume in zone #{availability_zone}" res = ec2_handler().create_volume(:snapshot_id => snapshot_id, :availability_zone => availability_zone) volume_id = res['volumeId'] started = false while !started sleep(5) #TODO: check for timeout? res = ec2_handler().describe_volumes(:volume_id => volume_id) state = res['volumeSet']['item'][0]['status'] @logger.debug "volume state #{state}" if state == 'available' started = true end end ("EBS volume #{volume_id} is ready") return volume_id end |
#delete_snapshot(snapshot_id) ⇒ Object
Deletes a snapshot.
312 313 314 315 316 317 318 |
# File 'lib/help/state_transition_helper.rb', line 312 def delete_snapshot(snapshot_id) ("going to delete snapshot #{snapshot_id}...") @logger.info("going to delete snapshot #{snapshot_id}...") ec2_handler().delete_snapshot(:snapshot_id => snapshot_id) @logger.info("snapshot #{snapshot_id} deleted") ("snapshot #{snapshot_id} deleted") end |
#delete_volume(volume_id) ⇒ Object
Delete an EBS volume. Input Parameters:
-
volume_id => EC2 ID for the EBS Volume to be deleted
278 279 280 281 282 283 |
# File 'lib/help/state_transition_helper.rb', line 278 def delete_volume(volume_id) ("going to delete volume #{volume_id} (no longer needed)...") @logger.debug "delete volume #{volume_id}" ec2_handler().delete_volume(:volume_id => volume_id) ("volume #{volume_id} deleted") end |
#detach_volume(volume_id, instance_id) ⇒ Object
Detach an EBS volume from an instance. Input Parameters:
-
volume_id => EC2 ID for the EBS Volume to be detached
-
instance_id => EC2 ID for the instance to detach from
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/help/state_transition_helper.rb', line 256 def detach_volume(volume_id, instance_id) ("going to detach volume #{volume_id}...") @logger.debug "detach volume #{volume_id}" ec2_handler().detach_volume(:volume_id => volume_id, :instance_id => instance_id ) done = false while !done sleep(3) #TODO: check for timeout? res = ec2_handler().describe_volumes(:volume_id => volume_id) @logger.debug "volume detaching: #{res.inspect}" if res['volumeSet']['item'][0]['status'] == 'available' done = true end end ("volume #{volume_id} detached.") end |
#disconnect ⇒ Object
If a remote command handler is connected, disconnect him silently.
84 85 86 87 88 89 90 |
# File 'lib/help/state_transition_helper.rb', line 84 def disconnect begin remote_handler().disconnect() rescue end self.remote_handler= nil end |
#ec2_handler ⇒ Object
481 482 483 484 485 486 |
# File 'lib/help/state_transition_helper.rb', line 481 def ec2_handler() if @ec2_handler == nil @ec2_handler = @context[:ec2_api_handler] end @ec2_handler end |
#ec2_handler=(ec2_handler) ⇒ Object
488 489 490 |
# File 'lib/help/state_transition_helper.rb', line 488 def ec2_handler=(ec2_handler) @ec2_handler = ec2_handler end |
#launch_instance(ami_id, key_name, security_group_name, ec2_handler = nil) ⇒ Object
Launch an instance based on an AMI ID Input Parameters:
-
ami_id => ID of the AMI to be launched
-
key_name => name of the key to access the instance
-
security_group_name => name of the security group to be used
Returned information:
-
instance_id => ID of the started instance
-
dns_name => DNS name of the started instance
-
availability_zone => Availability zone of the started instance
-
kernel_id => EC2 Kernel ID of the started instance
-
ramdisk_id => EC2 Ramdisk ID of the started instance
-
architecture => architecture (e.g. 386i, 64x) of the started instance
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/help/state_transition_helper.rb', line 104 def launch_instance(ami_id, key_name, security_group_name, ec2_handler = nil) ec2_handler = ec2_handler() if ec2_handler == nil ("starting up instance to execute the script (AMI = #{ami_id}) ...") @logger.debug "start up AMI #{ami_id}" # find out the image architecture first image_props = ec2_handler.describe_images(:image_id => ami_id) architecture = image_props['imagesSet']['item'][0]['architecture'] instance_type = "m1.small" if architecture != "i386" instance_type = "m1.large" end arch_log_msg = "Architecture of image #{ami_id} is #{architecture}. Use instance_type #{instance_type}." @logger.info arch_log_msg (arch_log_msg) # now start it res = ec2_handler.run_instances(:image_id => ami_id, :security_group => security_group_name, :key_name => key_name, :instance_type => instance_type ) instance_id = res['instancesSet']['item'][0]['instanceId'] @logger.info "started instance #{instance_id}" ("Started instance #{instance_id}. wait until it is ready...") #availability_zone , key_name/group_name started = false while started == false sleep(5) res = ec2_handler.describe_instances(:instance_id => instance_id) state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState'] @logger.info "instance is in state #{state['name']} (#{state['code']})" if state['code'].to_i == 16 started = true ("instance is up and running") dns_name = res['reservationSet']['item'][0]['instancesSet']['item'][0]['dnsName'] availability_zone = res['reservationSet']['item'][0]['instancesSet']['item'][0]['placement']['availabilityZone'] kernel_id = res['reservationSet']['item'][0]['instancesSet']['item'][0]['kernelId'] ramdisk_id = res['reservationSet']['item'][0]['instancesSet']['item'][0]['ramdiskId'] architecture = res['reservationSet']['item'][0]['instancesSet']['item'][0]['architecture'] elsif state['code'].to_i != 0 ("instance in state #{state['name']}") raise Exception.new('instance failed to start up') else ("instance still starting up...") end end return instance_id, dns_name, availability_zone, kernel_id, ramdisk_id, architecture end |
#mount_fs(mount_point, device) ⇒ Object
Create a file-system on a given machine (assumes to be connected already). Input Parameters:
-
mount_point => directory to be mounted on the device
-
device => device used for mounting
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/help/state_transition_helper.rb', line 365 def mount_fs(mount_point, device) ("going to mount #{device} on #{mount_point}...") @logger.debug "mount #{device} on #{mount_point}" if !remote_handler.file_exists?(mount_point) remote_handler().mkdir(mount_point) end remote_handler().mount(device, mount_point) trials = 3 mounted = false while trials > 0 sleep(5) #give mount some time if remote_handler().drive_mounted?(mount_point) mounted = true break end trials -= trials end if !mounted raise Exception.new("drive #{mount_point} not mounted") end ("mount successful") end |
#register_snapshot(snapshot_id, name, root_device_name, description, kernel_id, ramdisk_id, architecture) ⇒ Object
Registers a snapshot as EBS-booted AMI. Input Parameters:
-
snapshot_id => EC2 Snapshot ID used to be used
-
name => name of the AMI to be created
-
root_device_name => Root device name (e.g. /dev/sdj) to be used for AMI registration
-
description => description of the AMI to be created
-
kernel_id => EC2 Kernel ID to be used for AMI registration
-
ramdisk_id => EC2 Ramdisk ID to be used for AMI registration
-
architecture => architecture (e.g. 386i, 64x) to be used for AMI registration
Returns:
-
image_id => ID of the AMI created and registered
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/help/state_transition_helper.rb', line 331 def register_snapshot(snapshot_id, name, root_device_name, description, kernel_id, ramdisk_id, architecture) ("going to register snapshot #{snapshot_id}...") @logger.debug "register snapshot #{snapshot_id} as #{name}" res = ec2_handler().register_image_updated(:snapshot_id => snapshot_id, :kernel_id => kernel_id, :architecture => architecture, :root_device_name => root_device_name, :description => description, :name => name, :ramdisk_id => ramdisk_id ) @logger.debug "result of registration = #{res.inspect}" image_id = res['imageId'] @logger.info "resulting image_id = #{image_id}" ("snapshot #{snapshot_id} successfully registered as AMI #{image_id} ") return image_id end |
#remote_copy(keyname, source_dir, dest_machine, dest_dir) ⇒ Object
447 448 449 450 451 452 453 454 455 456 457 |
# File 'lib/help/state_transition_helper.rb', line 447 def remote_copy(keyname, source_dir, dest_machine, dest_dir) ("going to remote copy all files from volume. This may take some time...") if remote_handler().tools_installed?("rsync") @logger.debug "use rsync command" remote_handler().remote_rsync("/root/.ssh/#{keyname}.pem", source_dir, dest_machine, dest_dir) else @logger.debug "use scp command" remote_handler().scp("/root/.ssh/#{keyname}.pem", source_dir, dest_machine, dest_dir) end ("remote copy operation done") end |
#remote_handler ⇒ Object
setting/retrieving handlers
466 467 468 469 470 471 472 473 474 475 |
# File 'lib/help/state_transition_helper.rb', line 466 def remote_handler() if @remote_handler == nil if @context[:remote_command_handler] == nil @context[:remote_command_handler] = RemoteCommandHandler.new else @remote_handler = @context[:remote_command_handler] end end @remote_handler end |
#remote_handler=(remote_handler) ⇒ Object
477 478 479 |
# File 'lib/help/state_transition_helper.rb', line 477 def remote_handler=(remote_handler) @remote_handler = remote_handler end |
#shut_down_instance(instance_id) ⇒ Object
Shuts down an instance. Input Parameters:
-
instance_id => ID of the instance to be shut down
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/help/state_transition_helper.rb', line 154 def shut_down_instance(instance_id) ("going to shut down the temporary instance #{instance_id}...") @logger.debug "shutdown instance #{instance_id}" res = ec2_handler().terminate_instances(:instance_id => instance_id) done = false while done == false sleep(5) res = ec2_handler().describe_instances(:instance_id => instance_id) state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState'] @logger.debug "instance in state #{state['name']} (#{state['code']})" if state['code'].to_i == 48 done = true elsif state['code'].to_i != 32 raise Exception.new('instance failed to shut down') end end ("instance #{instance_id} is terminated") end |
#unmount_fs(mount_point) ⇒ Object
Unmount a drive Input Parameters:
-
mount_point => directory to be unmounted
391 392 393 394 395 396 397 398 399 400 |
# File 'lib/help/state_transition_helper.rb', line 391 def unmount_fs(mount_point) ("Going to unmount ...") @logger.debug "unmount #{mount_point}" remote_handler().umount(mount_point) sleep(2) #give umount some time if remote_handler().drive_mounted?(mount_point) raise Exception.new("drive #{mount_point} not unmounted") end ("device unmounted") end |
#upload_file(ip, user, key_data, file, target_file) ⇒ Object
459 460 461 462 |
# File 'lib/help/state_transition_helper.rb', line 459 def upload_file(ip, user, key_data, file, target_file) ("going to upload #{file} to #{ip}:/#{target_file}") remote_handler().upload(ip, user, key_data, file, target_file) end |
#zip_volume(source_dir, zip_file_dest, zip_file_name) ⇒ Object
Zips all files on a mounted-directory into a file Input Parameters:
-
source_dir => where to copy from
-
zip_file_dest => path where the zip-file should be stored
# zip_file_name => name of the zip file (without .zip suffix)
437 438 439 440 441 442 443 444 445 |
# File 'lib/help/state_transition_helper.rb', line 437 def zip_volume(source_dir, zip_file_dest, zip_file_name) ("going to zip the EBS volume") stderr = remote_handler().zip(source_dir, zip_file_dest+"/"+zip_file_name) if stderr.size > 0 @logger.info("zip operation generated error and might not be complete. output: #{stderr.join("\n")}") ("zip operation generated error and might not be complete. output: #{stderr.join("\n")}") end ("EBS volume successfully zipped") end |