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 203 203: def adjust_fstab(guestfs) 204: guestfs.sh("cat /etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > /etc/fstab.new") 205: guestfs.mv("/etc/fstab.new", "/etc/fstab") 206: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 203 203: def adjust_fstab(guestfs) 204: guestfs.sh("cat /etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > /etc/fstab.new") 205: guestfs.mv("/etc/fstab.new", "/etc/fstab") 206: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 45 45: def after_init 46: if valid_platform? 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: end 50: 51: set_default_config_value('availability_zone', @current_avaibility_zone) 52: set_default_config_value('delete_on_termination', true) 53: 54: register_supported_os('fedora', ['13', '14', '15']) 55: register_supported_os('rhel', ['6']) 56: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 45 45: def after_init 46: if valid_platform? 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: end 50: 51: set_default_config_value('availability_zone', @current_avaibility_zone) 52: set_default_config_value('delete_on_termination', true) 53: 54: register_supported_os('fedora', ['13', '14', '15']) 55: register_supported_os('rhel', ['6']) 56: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 193 193: def already_registered?(name) 194: images = @ec2.describe_images(:owner_id => @plugin_config['account_number'].to_s.gsub(/-/, '')) 195: 196: return false if images.nil? or images['imagesSet'].nil? 197: 198: images['imagesSet']['item'].each { |image| return image['imageId'] if image['name'] == name } 199: 200: false 201: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 193 193: def already_registered?(name) 194: images = @ec2.describe_images(:owner_id => @plugin_config['account_number'].to_s.gsub(/-/, '')) 195: 196: return false if images.nil? or images['imagesSet'].nil? 197: 198: images['imagesSet']['item'].each { |image| return image['imageId'] if image['name'] == name } 199: 200: false 201: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 226 226: def device_for_suffix(suffix) 227: return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}") 228: return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}") 229: 230: raise "Device for suffix '#{suffix}' not found!" 231: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 226 226: def device_for_suffix(suffix) 227: return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}") 228: return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}") 229: 230: raise "Device for suffix '#{suffix}' not found!" 231: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 179 179: def ebs_appliance_name 180: base_path = "#{@appliance_config.name}/#{@appliance_config.os.name}/#{@appliance_config.os.version}/#{@appliance_config.version}.#{@appliance_config.release}" 181: 182: return "#{base_path}/#{@appliance_config.hardware.arch}" unless @plugin_config['snapshot'] 183: 184: snapshot = 1 185: 186: while already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}") 187: snapshot += 1 188: end 189: 190: "#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}" 191: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 179 179: def ebs_appliance_name 180: base_path = "#{@appliance_config.name}/#{@appliance_config.os.name}/#{@appliance_config.os.version}/#{@appliance_config.version}.#{@appliance_config.release}" 181: 182: return "#{base_path}/#{@appliance_config.hardware.arch}" unless @plugin_config['snapshot'] 183: 184: snapshot = 1 185: 186: while already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}") 187: snapshot += 1 188: end 189: 190: "#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}" 191: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 58 58: def execute(type = :ebs) 59: validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin') 60: 61: raise "You try to run this plugin on invalid platform. You can run EBS delivery plugin only on EC2." unless valid_platform? 62: 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 63: 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 64: 65: ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture" 66: 67: @ec2 = AWS::EC2::Base.new(:access_key_id => @plugin_config['access_key'], :secret_access_key => @plugin_config['secret_access_key']) 68: 69: @log.debug "Checking if appliance is already registered..." 70: 71: ami_id = already_registered?(ebs_appliance_name) 72: 73: if ami_id 74: @log.warn "EBS AMI '#{ebs_appliance_name}' is already registered as '#{ami_id}' (region: #{@region})." 75: return 76: end 77: 78: @log.info "Creating new EBS volume..." 79: 80: size = 0 81: 82: @appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] } 83: 84: # create_volume with 10GB size 85: volume_id = @ec2.create_volume(:size => size.to_s, :availability_zone => @plugin_config['availability_zone'])['volumeId'] 86: 87: @log.debug "Volume #{volume_id} created." 88: @log.debug "Waiting for EBS volume #{volume_id} to be available..." 89: 90: # wait fo volume to be created 91: wait_for_volume_status('available', volume_id) 92: 93: # get first free device to mount the volume 94: suffix = free_device_suffix 95: 96: @log.trace "Got free device suffix: '#{suffix}'" 97: @log.trace "Reading current instance id..." 98: 99: # read current instance id 100: instance_id = open('http://169.254.169.254/latest/meta-data/instance-id').string 101: 102: @log.trace "Got: #{instance_id}" 103: @log.info "Attaching created volume..." 104: 105: # attach the volume to current host 106: @ec2.attach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id) 107: 108: @log.debug "Waiting for EBS volume to be attached..." 109: 110: # wait for volume to be attached 111: wait_for_volume_status('in-use', volume_id) 112: 113: sleep 10 # let's wait to discover the attached volume by OS 114: 115: @log.info "Copying data to EBS volume..." 116: 117: @image_helper.customize([@previous_deliverables.disk, device_for_suffix(suffix)], :automount => false) do |guestfs, guestfs_helper| 118: @image_helper.sync_filesystem(guestfs, guestfs_helper) 119: 120: @log.debug "Adjusting /etc/fstab..." 121: adjust_fstab(guestfs) 122: end 123: 124: @log.debug "Detaching EBS volume..." 125: 126: @ec2.detach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id) 127: 128: @log.debug "Waiting for EBS volume to be available..." 129: 130: wait_for_volume_status('available', volume_id) 131: 132: @log.info "Creating snapshot from EBS volume..." 133: 134: snapshot_id = @ec2.create_snapshot( 135: :volume_id => volume_id, 136: :description => ebs_appliance_description)['snapshotId'] 137: 138: @log.debug "Waiting for snapshot #{snapshot_id} to be completed..." 139: 140: wait_for_snapshot_status('completed', snapshot_id) 141: 142: @log.debug "Deleting temporary EBS volume..." 143: 144: @ec2.delete_volume(:volume_id => volume_id) 145: 146: @log.info "Registering image..." 147: 148: image_id = @ec2.register_image( 149: :block_device_mapping => [{ 150: :device_name => '/dev/sda1', 151: :ebs_snapshot_id => snapshot_id, 152: :ebs_delete_on_termination => @plugin_config['delete_on_termination'] 153: }, 154: { 155: :device_name => '/dev/sdb', 156: :virtual_name => 'ephemeral0' 157: }, 158: { 159: :device_name => '/dev/sdc', 160: :virtual_name => 'ephemeral1' 161: }, 162: { 163: :device_name => '/dev/sdd', 164: :virtual_name => 'ephemeral2' 165: }, 166: { 167: :device_name => '/dev/sde', 168: :virtual_name => 'ephemeral3' 169: }], 170: :root_device_name => '/dev/sda1', 171: :architecture => @appliance_config.hardware.base_arch, 172: :kernel_id => KERNELS[@region][@appliance_config.hardware.base_arch][:aki], 173: :name => ebs_appliance_name, 174: :description => ebs_appliance_description)['imageId'] 175: 176: @log.info "EBS AMI '#{ebs_appliance_name}' registered: #{image_id} (region: #{@region})" 177: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 58 58: def execute(type = :ebs) 59: validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin') 60: 61: raise "You try to run this plugin on invalid platform. You can run EBS delivery plugin only on EC2." unless valid_platform? 62: 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 63: 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 64: 65: ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture" 66: 67: @ec2 = AWS::EC2::Base.new(:access_key_id => @plugin_config['access_key'], :secret_access_key => @plugin_config['secret_access_key']) 68: 69: @log.debug "Checking if appliance is already registered..." 70: 71: ami_id = already_registered?(ebs_appliance_name) 72: 73: if ami_id 74: @log.warn "EBS AMI '#{ebs_appliance_name}' is already registered as '#{ami_id}' (region: #{@region})." 75: return 76: end 77: 78: @log.info "Creating new EBS volume..." 79: 80: size = 0 81: 82: @appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] } 83: 84: # create_volume with 10GB size 85: volume_id = @ec2.create_volume(:size => size.to_s, :availability_zone => @plugin_config['availability_zone'])['volumeId'] 86: 87: @log.debug "Volume #{volume_id} created." 88: @log.debug "Waiting for EBS volume #{volume_id} to be available..." 89: 90: # wait fo volume to be created 91: wait_for_volume_status('available', volume_id) 92: 93: # get first free device to mount the volume 94: suffix = free_device_suffix 95: 96: @log.trace "Got free device suffix: '#{suffix}'" 97: @log.trace "Reading current instance id..." 98: 99: # read current instance id 100: instance_id = open('http://169.254.169.254/latest/meta-data/instance-id').string 101: 102: @log.trace "Got: #{instance_id}" 103: @log.info "Attaching created volume..." 104: 105: # attach the volume to current host 106: @ec2.attach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id) 107: 108: @log.debug "Waiting for EBS volume to be attached..." 109: 110: # wait for volume to be attached 111: wait_for_volume_status('in-use', volume_id) 112: 113: sleep 10 # let's wait to discover the attached volume by OS 114: 115: @log.info "Copying data to EBS volume..." 116: 117: @image_helper.customize([@previous_deliverables.disk, device_for_suffix(suffix)], :automount => false) do |guestfs, guestfs_helper| 118: @image_helper.sync_filesystem(guestfs, guestfs_helper) 119: 120: @log.debug "Adjusting /etc/fstab..." 121: adjust_fstab(guestfs) 122: end 123: 124: @log.debug "Detaching EBS volume..." 125: 126: @ec2.detach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id) 127: 128: @log.debug "Waiting for EBS volume to be available..." 129: 130: wait_for_volume_status('available', volume_id) 131: 132: @log.info "Creating snapshot from EBS volume..." 133: 134: snapshot_id = @ec2.create_snapshot( 135: :volume_id => volume_id, 136: :description => ebs_appliance_description)['snapshotId'] 137: 138: @log.debug "Waiting for snapshot #{snapshot_id} to be completed..." 139: 140: wait_for_snapshot_status('completed', snapshot_id) 141: 142: @log.debug "Deleting temporary EBS volume..." 143: 144: @ec2.delete_volume(:volume_id => volume_id) 145: 146: @log.info "Registering image..." 147: 148: image_id = @ec2.register_image( 149: :block_device_mapping => [{ 150: :device_name => '/dev/sda1', 151: :ebs_snapshot_id => snapshot_id, 152: :ebs_delete_on_termination => @plugin_config['delete_on_termination'] 153: }, 154: { 155: :device_name => '/dev/sdb', 156: :virtual_name => 'ephemeral0' 157: }, 158: { 159: :device_name => '/dev/sdc', 160: :virtual_name => 'ephemeral1' 161: }, 162: { 163: :device_name => '/dev/sdd', 164: :virtual_name => 'ephemeral2' 165: }, 166: { 167: :device_name => '/dev/sde', 168: :virtual_name => 'ephemeral3' 169: }], 170: :root_device_name => '/dev/sda1', 171: :architecture => @appliance_config.hardware.base_arch, 172: :kernel_id => KERNELS[@region][@appliance_config.hardware.base_arch][:aki], 173: :name => ebs_appliance_name, 174: :description => ebs_appliance_description)['imageId'] 175: 176: @log.info "EBS AMI '#{ebs_appliance_name}' registered: #{image_id} (region: #{@region})" 177: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 233 233: def free_device_suffix 234: ("f".."p").each do |suffix| 235: return suffix unless File.exists?("/dev/sd#{suffix}") or File.exists?("/dev/xvd#{suffix}") 236: end 237: 238: raise "Found too many attached devices. Cannot attach EBS volume." 239: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 233 233: def free_device_suffix 234: ("f".."p").each do |suffix| 235: return suffix unless File.exists?("/dev/sd#{suffix}") or File.exists?("/dev/xvd#{suffix}") 236: end 237: 238: raise "Found too many attached devices. Cannot attach EBS volume." 239: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 241 241: def valid_platform? 242: begin 243: return Resolv.getname("169.254.169.254").include?(".ec2.internal") 244: rescue Resolv::ResolvError 245: false 246: end 247: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 241 241: def valid_platform? 242: begin 243: return Resolv.getname("169.254.169.254").include?(".ec2.internal") 244: rescue Resolv::ResolvError 245: false 246: end 247: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 208 208: def wait_for_snapshot_status(status, snapshot_id) 209: snapshot = @ec2.describe_snapshots(:snapshot_id => snapshot_id)['snapshotSet']['item'].first 210: 211: unless snapshot['status'] == status 212: sleep 2 213: wait_for_snapshot_status(status, snapshot_id) 214: end 215: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 208 208: def wait_for_snapshot_status(status, snapshot_id) 209: snapshot = @ec2.describe_snapshots(:snapshot_id => snapshot_id)['snapshotSet']['item'].first 210: 211: unless snapshot['status'] == status 212: sleep 2 213: wait_for_snapshot_status(status, snapshot_id) 214: end 215: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 217 217: def wait_for_volume_status(status, volume_id) 218: volume = @ec2.describe_volumes(:volume_id => volume_id)['volumeSet']['item'].first 219: 220: unless volume['status'] == status 221: sleep 2 222: wait_for_volume_status(status, volume_id) 223: end 224: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 217 217: def wait_for_volume_status(status, volume_id) 218: volume = @ec2.describe_volumes(:volume_id => volume_id)['volumeSet']['item'].first 219: 220: unless volume['status'] == status 221: sleep 2 222: wait_for_volume_status(status, volume_id) 223: end 224: end