Class BoxGrinder::EBSPlugin
In: lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb
lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb
Parent: BasePlugin

Methods

Constants

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'}

Public Instance methods

[Source]

     # 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

[Source]

     # 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

[Source]

    # 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

[Source]

    # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Validate]