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.
-
#determine_file(ip, user_name, ssh_keydata, file_candidates) ⇒ Object
From a list of existing files, return the first that exists.
-
#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(user_name, 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)
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/help/state_transition_helper.rb', line 232 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 82 |
# 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() sudo = remote_handler().use_sudo ? " [sudo]" : "" ("connected to #{dns_name}#{sudo}. OS installed is #{os}") @logger.info "connected to #{dns_name}#{sudo}" 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
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 431 |
# File 'lib/help/state_transition_helper.rb', line 406 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)
352 353 354 355 356 357 358 359 360 |
# File 'lib/help/state_transition_helper.rb', line 352 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
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/help/state_transition_helper.rb', line 291 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
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/help/state_transition_helper.rb', line 180 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
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/help/state_transition_helper.rb', line 207 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.
313 314 315 316 317 318 319 |
# File 'lib/help/state_transition_helper.rb', line 313 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
279 280 281 282 283 284 |
# File 'lib/help/state_transition_helper.rb', line 279 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
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/help/state_transition_helper.rb', line 257 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 |
#determine_file(ip, user_name, ssh_keydata, file_candidates) ⇒ Object
From a list of existing files, return the first that exists
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/help/state_transition_helper.rb', line 473 def determine_file(ip, user_name, ssh_keydata, file_candidates) connect(ip, user_name, nil, ssh_keydata) begin file_candidates.each() {|file_path| if remote_handler().file_exists?(file_path) return file_path end } return nil rescue raise ensure disconnect() end end |
#disconnect ⇒ Object
If a remote command handler is connected, disconnect him silently.
85 86 87 88 89 90 91 |
# File 'lib/help/state_transition_helper.rb', line 85 def disconnect begin remote_handler().disconnect() rescue end self.remote_handler= nil end |
#ec2_handler ⇒ Object
506 507 508 509 510 511 |
# File 'lib/help/state_transition_helper.rb', line 506 def ec2_handler() if @ec2_handler == nil @ec2_handler = @context[:ec2_api_handler] end @ec2_handler end |
#ec2_handler=(ec2_handler) ⇒ Object
513 514 515 |
# File 'lib/help/state_transition_helper.rb', line 513 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
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 150 |
# File 'lib/help/state_transition_helper.rb', line 105 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
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/help/state_transition_helper.rb', line 366 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
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/help/state_transition_helper.rb', line 332 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(user_name, keyname, source_dir, dest_machine, dest_dir) ⇒ Object
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
# File 'lib/help/state_transition_helper.rb', line 448 def remote_copy(user_name, keyname, source_dir, dest_machine, dest_dir) ("going to remote copy all files from volume. This may take some time...") key_path_candidates = ["/#{user_name}/.ssh/", "/home/#{user_name}/.ssh/"] key_path_candidates.each() {|key_path| key_file = "#{key_path}#{keyname}.pem" if remote_handler().file_exists?(key_path) if remote_handler().tools_installed?("rsync") @logger.debug "use rsync command on #{key_file}" remote_handler().remote_rsync(key_file, source_dir, dest_machine, dest_dir) else @logger.debug "use scp command #{key_file}" remote_handler().scp(key_file, source_dir, dest_machine, dest_dir) end break end } ("remote copy operation done") end |
#remote_handler ⇒ Object
setting/retrieving handlers
491 492 493 494 495 496 497 498 499 500 |
# File 'lib/help/state_transition_helper.rb', line 491 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
502 503 504 |
# File 'lib/help/state_transition_helper.rb', line 502 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
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/help/state_transition_helper.rb', line 155 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
392 393 394 395 396 397 398 399 400 401 |
# File 'lib/help/state_transition_helper.rb', line 392 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
467 468 469 470 |
# File 'lib/help/state_transition_helper.rb', line 467 def upload_file(ip, user, key_data, file, target_file) ("going to upload #{file} to #{user}@#{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)
438 439 440 441 442 443 444 445 446 |
# File 'lib/help/state_transition_helper.rb', line 438 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 |