Class | BoxGrinder::EBSPlugin |
In: |
lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb
lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb |
Parent: | BasePlugin |
KERNELS | = | { 'eu-west-1' => { 'i386' => {:aki => 'aki-4deec439'}, 'x86_64' => {:aki => 'aki-4feec43b'} |
KERNELS | = | { 'eu-west-1' => { 'i386' => {:aki => 'aki-4deec439'}, 'x86_64' => {:aki => 'aki-4feec43b'} |
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 231 231: def adjust_fstab(ebs_mount_dir) 232: @exec_helper.execute("cat #{ebs_mount_dir}/etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > #{ebs_mount_dir}/etc/fstab.new") 233: @exec_helper.execute("mv #{ebs_mount_dir}/etc/fstab.new #{ebs_mount_dir}/etc/fstab") 234: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 231 231: def adjust_fstab(ebs_mount_dir) 232: @exec_helper.execute("cat #{ebs_mount_dir}/etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > #{ebs_mount_dir}/etc/fstab.new") 233: @exec_helper.execute("mv #{ebs_mount_dir}/etc/fstab.new #{ebs_mount_dir}/etc/fstab") 234: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 45 45: def after_init 46: begin 47: @current_avaibility_zone = open('http://169.254.169.254/latest/meta-data/placement/availability-zone').string 48: @region = @current_avaibility_zone.scan(/((\w+)-(\w+)-(\d+))/).flatten.first 49: rescue 50: @current_avaibility_zone = nil 51: @region = nil 52: end 53: 54: set_default_config_value('availability_zone', @current_avaibility_zone) 55: set_default_config_value('delete_on_termination', true) 56: 57: register_supported_os('fedora', ['13', '14', '15']) 58: register_supported_os('rhel', ['6']) 59: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 45 45: def after_init 46: begin 47: @current_avaibility_zone = open('http://169.254.169.254/latest/meta-data/placement/availability-zone').string 48: @region = @current_avaibility_zone.scan(/((\w+)-(\w+)-(\d+))/).flatten.first 49: rescue 50: @current_avaibility_zone = nil 51: @region = nil 52: end 53: 54: set_default_config_value('availability_zone', @current_avaibility_zone) 55: set_default_config_value('delete_on_termination', true) 56: 57: register_supported_os('fedora', ['13', '14', '15']) 58: register_supported_os('rhel', ['6']) 59: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 221 221: def already_registered?(name) 222: images = @ec2.describe_images(:owner_id => @plugin_config['account_number'].to_s.gsub(/-/, '')) 223: 224: return false if images.nil? or images['imagesSet'].nil? 225: 226: images['imagesSet']['item'].each { |image| return image['imageId'] if image['name'] == name } 227: 228: false 229: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 221 221: def already_registered?(name) 222: images = @ec2.describe_images(:owner_id => @plugin_config['account_number'].to_s.gsub(/-/, '')) 223: 224: return false if images.nil? or images['imagesSet'].nil? 225: 226: images['imagesSet']['item'].each { |image| return image['imageId'] if image['name'] == name } 227: 228: false 229: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 254 254: def device_for_suffix(suffix) 255: return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}") 256: return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}") 257: 258: raise "Not found device for suffix #{suffix}" 259: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 254 254: def device_for_suffix(suffix) 255: return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}") 256: return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}") 257: 258: raise "Not found device for suffix #{suffix}" 259: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 207 207: def ebs_appliance_name 208: base_path = "#{@appliance_config.name}/#{@appliance_config.os.name}/#{@appliance_config.os.version}/#{@appliance_config.version}.#{@appliance_config.release}" 209: 210: return "#{base_path}/#{@appliance_config.hardware.arch}" unless @plugin_config['snapshot'] 211: 212: snapshot = 1 213: 214: while already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}") 215: snapshot += 1 216: end 217: 218: "#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}" 219: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 207 207: def ebs_appliance_name 208: base_path = "#{@appliance_config.name}/#{@appliance_config.os.name}/#{@appliance_config.os.version}/#{@appliance_config.version}.#{@appliance_config.release}" 209: 210: return "#{base_path}/#{@appliance_config.hardware.arch}" unless @plugin_config['snapshot'] 211: 212: snapshot = 1 213: 214: while already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}") 215: snapshot += 1 216: end 217: 218: "#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}" 219: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 61 61: def execute(type = :ebs) 62: validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin') 63: 64: raise "You try to run this plugin on invalid platform. You can run EBS delivery plugin only on EC2." unless valid_platform? 65: raise "You can only convert to EBS type AMI appliances converted to EC2 format. Use '-p ec2' switch. For more info about EC2 plugin see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EC2_Platform_Plugin." unless @previous_plugin_info[:name] == :ec2 66: raise "You selected #{@plugin_config['availability_zone']} avaibility zone, but your instance is running in #{@current_avaibility_zone} zone. Please change avaibility zone in plugin configuration file to #{@current_avaibility_zone} (see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin) or use another instance in #{@plugin_config['availability_zone']} zone to create your EBS AMI." if @plugin_config['availability_zone'] != @current_avaibility_zone 67: 68: ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture" 69: 70: @ec2 = AWS::EC2::Base.new(:access_key_id => @plugin_config['access_key'], :secret_access_key => @plugin_config['secret_access_key']) 71: 72: @log.debug "Checking if appliance is already registered..." 73: 74: ami_id = already_registered?(ebs_appliance_name) 75: 76: if ami_id 77: @log.warn "EBS AMI '#{ebs_appliance_name}' is already registered as '#{ami_id}' (region: #{@region})." 78: return 79: end 80: 81: @log.info "Creating new EBS volume..." 82: 83: size = 0 84: 85: @appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] } 86: 87: # create_volume with 10GB size 88: volume_id = @ec2.create_volume(:size => size.to_s, :availability_zone => @plugin_config['availability_zone'])['volumeId'] 89: 90: @log.debug "Volume #{volume_id} created." 91: @log.debug "Waiting for EBS volume #{volume_id} to be available..." 92: 93: # wait fo volume to be created 94: wait_for_volume_status('available', volume_id) 95: 96: # get first free device to mount the volume 97: suffix = free_device_suffix 98: 99: @log.trace "Got free device suffix: '#{suffix}'" 100: @log.trace "Reading current instance id..." 101: 102: # read current instance id 103: instance_id = open('http://169.254.169.254/latest/meta-data/instance-id').string 104: 105: @log.trace "Got: #{instance_id}" 106: @log.info "Attaching created volume..." 107: 108: # attach the volume to current host 109: @ec2.attach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id) 110: 111: @log.debug "Waiting for EBS volume to be attached..." 112: 113: # wait for volume to be attached 114: wait_for_volume_status('in-use', volume_id) 115: 116: sleep 5 # let's wait to discover the attached volume by OS 117: 118: @log.info "Copying data to EBS volume..." 119: 120: ec2_disk_mount_dir = "#{@dir.tmp}/ec2-#{rand(9999999999).to_s.center(10, rand(9).to_s)}" 121: ebs_disk_mount_dir = "#{@dir.tmp}/ebs-#{rand(9999999999).to_s.center(10, rand(9).to_s)}" 122: 123: FileUtils.mkdir_p(ec2_disk_mount_dir) 124: FileUtils.mkdir_p(ebs_disk_mount_dir) 125: 126: begin 127: ec2_mounts = @image_helper.mount_image(@previous_deliverables.disk, ec2_disk_mount_dir) 128: rescue => e 129: @log.debug e 130: raise "Error while mounting image. See logs for more info" 131: end 132: 133: @log.debug "Creating filesystem on volume..." 134: 135: @image_helper.create_filesystem(device_for_suffix(suffix)) 136: @exec_helper.execute("mount #{device_for_suffix(suffix)} #{ebs_disk_mount_dir}") 137: 138: @log.debug "Syncing files..." 139: 140: @image_helper.sync_files(ec2_disk_mount_dir, ebs_disk_mount_dir) 141: 142: @log.debug "Adjusting /etc/fstab..." 143: 144: adjust_fstab(ebs_disk_mount_dir) 145: 146: @exec_helper.execute("umount #{ebs_disk_mount_dir}") 147: @image_helper.umount_image(@previous_deliverables.disk, ec2_disk_mount_dir, ec2_mounts) 148: 149: FileUtils.rm_rf(ebs_disk_mount_dir) 150: FileUtils.rm_rf(ec2_disk_mount_dir) 151: 152: @log.debug "Detaching EBS volume..." 153: 154: @ec2.detach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id) 155: 156: @log.debug "Waiting for EBS volume to be available..." 157: 158: wait_for_volume_status('available', volume_id) 159: 160: @log.info "Creating snapshot from EBS volume..." 161: 162: snapshot_id = @ec2.create_snapshot( 163: :volume_id => volume_id, 164: :description => ebs_appliance_description)['snapshotId'] 165: 166: @log.debug "Waiting for snapshot #{snapshot_id} to be completed..." 167: 168: wait_for_snapshot_status('completed', snapshot_id) 169: 170: @log.debug "Deleting temporary EBS volume..." 171: 172: @ec2.delete_volume(:volume_id => volume_id) 173: 174: @log.info "Registering image..." 175: 176: image_id = @ec2.register_image( 177: :block_device_mapping => [{ 178: :device_name => '/dev/sda1', 179: :ebs_snapshot_id => snapshot_id, 180: :ebs_delete_on_termination => @plugin_config['delete_on_termination'] 181: }, 182: { 183: :device_name => '/dev/sdb', 184: :virtual_name => 'ephemeral0' 185: }, 186: { 187: :device_name => '/dev/sdc', 188: :virtual_name => 'ephemeral1' 189: }, 190: { 191: :device_name => '/dev/sdd', 192: :virtual_name => 'ephemeral2' 193: }, 194: { 195: :device_name => '/dev/sde', 196: :virtual_name => 'ephemeral3' 197: }], 198: :root_device_name => '/dev/sda1', 199: :architecture => @appliance_config.hardware.base_arch, 200: :kernel_id => KERNELS[@region][@appliance_config.hardware.base_arch][:aki], 201: :name => ebs_appliance_name, 202: :description => ebs_appliance_description)['imageId'] 203: 204: @log.info "EBS AMI '#{ebs_appliance_name}' registered: #{image_id} (region: #{@region})" 205: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 61 61: def execute(type = :ebs) 62: validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin') 63: 64: raise "You try to run this plugin on invalid platform. You can run EBS delivery plugin only on EC2." unless valid_platform? 65: raise "You can only convert to EBS type AMI appliances converted to EC2 format. Use '-p ec2' switch. For more info about EC2 plugin see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EC2_Platform_Plugin." unless @previous_plugin_info[:name] == :ec2 66: raise "You selected #{@plugin_config['availability_zone']} avaibility zone, but your instance is running in #{@current_avaibility_zone} zone. Please change avaibility zone in plugin configuration file to #{@current_avaibility_zone} (see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin) or use another instance in #{@plugin_config['availability_zone']} zone to create your EBS AMI." if @plugin_config['availability_zone'] != @current_avaibility_zone 67: 68: ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture" 69: 70: @ec2 = AWS::EC2::Base.new(:access_key_id => @plugin_config['access_key'], :secret_access_key => @plugin_config['secret_access_key']) 71: 72: @log.debug "Checking if appliance is already registered..." 73: 74: ami_id = already_registered?(ebs_appliance_name) 75: 76: if ami_id 77: @log.warn "EBS AMI '#{ebs_appliance_name}' is already registered as '#{ami_id}' (region: #{@region})." 78: return 79: end 80: 81: @log.info "Creating new EBS volume..." 82: 83: size = 0 84: 85: @appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] } 86: 87: # create_volume with 10GB size 88: volume_id = @ec2.create_volume(:size => size.to_s, :availability_zone => @plugin_config['availability_zone'])['volumeId'] 89: 90: @log.debug "Volume #{volume_id} created." 91: @log.debug "Waiting for EBS volume #{volume_id} to be available..." 92: 93: # wait fo volume to be created 94: wait_for_volume_status('available', volume_id) 95: 96: # get first free device to mount the volume 97: suffix = free_device_suffix 98: 99: @log.trace "Got free device suffix: '#{suffix}'" 100: @log.trace "Reading current instance id..." 101: 102: # read current instance id 103: instance_id = open('http://169.254.169.254/latest/meta-data/instance-id').string 104: 105: @log.trace "Got: #{instance_id}" 106: @log.info "Attaching created volume..." 107: 108: # attach the volume to current host 109: @ec2.attach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id) 110: 111: @log.debug "Waiting for EBS volume to be attached..." 112: 113: # wait for volume to be attached 114: wait_for_volume_status('in-use', volume_id) 115: 116: sleep 5 # let's wait to discover the attached volume by OS 117: 118: @log.info "Copying data to EBS volume..." 119: 120: ec2_disk_mount_dir = "#{@dir.tmp}/ec2-#{rand(9999999999).to_s.center(10, rand(9).to_s)}" 121: ebs_disk_mount_dir = "#{@dir.tmp}/ebs-#{rand(9999999999).to_s.center(10, rand(9).to_s)}" 122: 123: FileUtils.mkdir_p(ec2_disk_mount_dir) 124: FileUtils.mkdir_p(ebs_disk_mount_dir) 125: 126: begin 127: ec2_mounts = @image_helper.mount_image(@previous_deliverables.disk, ec2_disk_mount_dir) 128: rescue => e 129: @log.debug e 130: raise "Error while mounting image. See logs for more info" 131: end 132: 133: @log.debug "Creating filesystem on volume..." 134: 135: @image_helper.create_filesystem(device_for_suffix(suffix)) 136: @exec_helper.execute("mount #{device_for_suffix(suffix)} #{ebs_disk_mount_dir}") 137: 138: @log.debug "Syncing files..." 139: 140: @image_helper.sync_files(ec2_disk_mount_dir, ebs_disk_mount_dir) 141: 142: @log.debug "Adjusting /etc/fstab..." 143: 144: adjust_fstab(ebs_disk_mount_dir) 145: 146: @exec_helper.execute("umount #{ebs_disk_mount_dir}") 147: @image_helper.umount_image(@previous_deliverables.disk, ec2_disk_mount_dir, ec2_mounts) 148: 149: FileUtils.rm_rf(ebs_disk_mount_dir) 150: FileUtils.rm_rf(ec2_disk_mount_dir) 151: 152: @log.debug "Detaching EBS volume..." 153: 154: @ec2.detach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id) 155: 156: @log.debug "Waiting for EBS volume to be available..." 157: 158: wait_for_volume_status('available', volume_id) 159: 160: @log.info "Creating snapshot from EBS volume..." 161: 162: snapshot_id = @ec2.create_snapshot( 163: :volume_id => volume_id, 164: :description => ebs_appliance_description)['snapshotId'] 165: 166: @log.debug "Waiting for snapshot #{snapshot_id} to be completed..." 167: 168: wait_for_snapshot_status('completed', snapshot_id) 169: 170: @log.debug "Deleting temporary EBS volume..." 171: 172: @ec2.delete_volume(:volume_id => volume_id) 173: 174: @log.info "Registering image..." 175: 176: image_id = @ec2.register_image( 177: :block_device_mapping => [{ 178: :device_name => '/dev/sda1', 179: :ebs_snapshot_id => snapshot_id, 180: :ebs_delete_on_termination => @plugin_config['delete_on_termination'] 181: }, 182: { 183: :device_name => '/dev/sdb', 184: :virtual_name => 'ephemeral0' 185: }, 186: { 187: :device_name => '/dev/sdc', 188: :virtual_name => 'ephemeral1' 189: }, 190: { 191: :device_name => '/dev/sdd', 192: :virtual_name => 'ephemeral2' 193: }, 194: { 195: :device_name => '/dev/sde', 196: :virtual_name => 'ephemeral3' 197: }], 198: :root_device_name => '/dev/sda1', 199: :architecture => @appliance_config.hardware.base_arch, 200: :kernel_id => KERNELS[@region][@appliance_config.hardware.base_arch][:aki], 201: :name => ebs_appliance_name, 202: :description => ebs_appliance_description)['imageId'] 203: 204: @log.info "EBS AMI '#{ebs_appliance_name}' registered: #{image_id} (region: #{@region})" 205: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 261 261: def free_device_suffix 262: ("f".."p").each do |suffix| 263: return suffix unless File.exists?("/dev/sd#{suffix}") or File.exists?("/dev/xvd#{suffix}") 264: end 265: 266: raise "Found too many attached devices. Cannot attach EBS volume." 267: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 261 261: def free_device_suffix 262: ("f".."p").each do |suffix| 263: return suffix unless File.exists?("/dev/sd#{suffix}") or File.exists?("/dev/xvd#{suffix}") 264: end 265: 266: raise "Found too many attached devices. Cannot attach EBS volume." 267: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 269 269: def valid_platform? 270: begin 271: open("http://169.254.169.254/1.0/meta-data/local-ipv4") 272: true 273: rescue 274: false 275: end 276: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 269 269: def valid_platform? 270: begin 271: open("http://169.254.169.254/1.0/meta-data/local-ipv4") 272: true 273: rescue 274: false 275: end 276: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 236 236: def wait_for_snapshot_status(status, snapshot_id) 237: snapshot = @ec2.describe_snapshots(:snapshot_id => snapshot_id)['snapshotSet']['item'].first 238: 239: unless snapshot['status'] == status 240: sleep 2 241: wait_for_snapshot_status(status, snapshot_id) 242: end 243: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 236 236: def wait_for_snapshot_status(status, snapshot_id) 237: snapshot = @ec2.describe_snapshots(:snapshot_id => snapshot_id)['snapshotSet']['item'].first 238: 239: unless snapshot['status'] == status 240: sleep 2 241: wait_for_snapshot_status(status, snapshot_id) 242: end 243: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 245 245: def wait_for_volume_status(status, volume_id) 246: volume = @ec2.describe_volumes(:volume_id => volume_id)['volumeSet']['item'].first 247: 248: unless volume['status'] == status 249: sleep 2 250: wait_for_volume_status(status, volume_id) 251: end 252: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 245 245: def wait_for_volume_status(status, volume_id) 246: volume = @ec2.describe_volumes(:volume_id => volume_id)['volumeSet']['item'].first 247: 248: unless volume['status'] == status 249: sleep 2 250: wait_for_volume_status(status, volume_id) 251: end 252: end