boxgrinder-build 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG +11 -0
  2. data/README.md +32 -34
  3. data/Rakefile +7 -1
  4. data/bin/boxgrinder-build +10 -17
  5. data/boxgrinder-build.gemspec +7 -4
  6. data/lib/boxgrinder-build/appliance.rb +13 -16
  7. data/lib/boxgrinder-build/helpers/guestfs-helper.rb +84 -40
  8. data/lib/boxgrinder-build/helpers/image-helper.rb +54 -85
  9. data/lib/boxgrinder-build/helpers/linux-helper.rb +29 -9
  10. data/lib/boxgrinder-build/helpers/plugin-helper.rb +1 -0
  11. data/lib/boxgrinder-build/plugins/base-plugin.rb +3 -2
  12. data/lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb +13 -42
  13. data/lib/boxgrinder-build/plugins/delivery/elastichosts/elastichosts-plugin.rb +209 -0
  14. data/lib/boxgrinder-build/plugins/os/fedora/fedora-plugin.rb +8 -4
  15. data/lib/boxgrinder-build/plugins/os/rhel/rhel-plugin.rb +0 -7
  16. data/lib/boxgrinder-build/plugins/os/rpm-based/kickstart.rb +4 -1
  17. data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin.rb +52 -9
  18. data/lib/boxgrinder-build/plugins/os/rpm-based/src/appliance.ks.erb +6 -2
  19. data/lib/boxgrinder-build/plugins/platform/ec2/ec2-plugin.rb +4 -44
  20. data/lib/boxgrinder-build/plugins/platform/ec2/src/fstab_32bit +1 -1
  21. data/lib/boxgrinder-build/plugins/platform/ec2/src/fstab_64bit +1 -1
  22. data/lib/boxgrinder-build/plugins/platform/ec2/src/menu.lst +2 -2
  23. data/lib/boxgrinder-build/plugins/platform/virtualbox/virtualbox-plugin.rb +24 -13
  24. data/lib/boxgrinder-build/plugins/platform/vmware/src/base.vmx +2 -2
  25. data/lib/boxgrinder-build/plugins/platform/vmware/vmware-plugin.rb +0 -1
  26. data/rubygem-boxgrinder-build.spec +23 -1
  27. data/spec/appliance-spec.rb +5 -79
  28. data/spec/helpers/augeas-helper-spec.rb +1 -0
  29. data/spec/helpers/guestfs-helper-spec.rb +79 -62
  30. data/spec/helpers/image-helper-spec.rb +88 -129
  31. data/spec/helpers/linux-helper-spec.rb +22 -5
  32. data/spec/helpers/package-helper-spec.rb +1 -0
  33. data/spec/helpers/plugin-helper-spec.rb +1 -0
  34. data/spec/managers/plugin-manager-spec.rb +3 -2
  35. data/spec/plugins/base-plugin-spec.rb +1 -1
  36. data/spec/plugins/delivery/ebs/ebs-plugin-spec.rb +21 -13
  37. data/spec/plugins/delivery/elastichosts/elastichosts-plugin-spec.rb +320 -0
  38. data/spec/plugins/delivery/local/local-plugin-spec.rb +1 -0
  39. data/spec/plugins/delivery/s3/s3-plugin-spec.rb +1 -0
  40. data/spec/plugins/delivery/sftp/sftp-plugin-spec.rb +1 -0
  41. data/spec/plugins/os/centos/centos-plugin-spec.rb +1 -0
  42. data/spec/plugins/os/fedora/fedora-plugin-spec.rb +13 -1
  43. data/spec/plugins/os/rhel/rhel-plugin-spec.rb +1 -15
  44. data/spec/plugins/os/rpm-based/kickstart-spec.rb +1 -0
  45. data/spec/plugins/os/rpm-based/rpm-based-os-plugin-spec.rb +75 -15
  46. data/spec/plugins/os/rpm-based/rpm-dependency-validator-spec.rb +1 -0
  47. data/spec/plugins/platform/ec2/ec2-plugin-spec.rb +10 -43
  48. data/spec/plugins/platform/virtualbox/virtualbox-plugin-spec.rb +89 -10
  49. data/spec/plugins/platform/vmware/vmware-plugin-spec.rb +3 -2
  50. metadata +21 -7
  51. data/lib/boxgrinder-build/helpers/appliance-customize-helper.rb +0 -45
  52. data/spec/helpers/appliance-customize-helper-spec.rb +0 -74
@@ -30,49 +30,6 @@ module BoxGrinder
30
30
  @exec_helper = options[:exec_helper] || ExecHelper.new(:log => @log)
31
31
  end
32
32
 
33
- def mount_image(disk, mount_dir)
34
- offsets = calculate_disk_offsets(disk)
35
-
36
- @log.debug "Mounting image #{File.basename(disk)} in #{mount_dir}..."
37
- FileUtils.mkdir_p(mount_dir)
38
-
39
- mounts = {}
40
-
41
- offsets.each do |offset|
42
- loop_device = get_loop_device
43
- @exec_helper.execute("losetup -o #{offset.to_s} #{loop_device} '#{disk}'")
44
- label = @exec_helper.execute("e2label #{loop_device}").strip.chomp.gsub('_', '')
45
- label = '/' if label == ''
46
- mounts[label] = loop_device
47
- end
48
-
49
- @exec_helper.execute("mount #{mounts['/']} '#{mount_dir}'")
50
-
51
- mounts.reject { |key, value| key == '/' }.each do |mount_point, loop_device|
52
- @exec_helper.execute("mount #{loop_device} '#{mount_dir}#{mount_point}'")
53
- end
54
-
55
- # Give some time to mount the images
56
- sleep 2
57
-
58
- @log.trace "Mounts:\n#{mounts}"
59
-
60
- mounts
61
- end
62
-
63
- def umount_image(disk, mount_dir, mounts)
64
- @log.debug "Unmounting image '#{File.basename(disk)}'..."
65
-
66
- mounts.each { |mount_point, loop_device| @exec_helper.execute("umount -d #{loop_device}") unless mount_point == '/' }
67
-
68
- @exec_helper.execute("umount -d #{mounts['/']}")
69
-
70
- # Give some time to umount the image
71
- sleep 2
72
-
73
- FileUtils.rm_rf(mount_dir)
74
- end
75
-
76
33
  def disk_info(disk)
77
34
  YAML.load(@exec_helper.execute("qemu-img info '#{disk}'"))
78
35
  end
@@ -100,63 +57,75 @@ module BoxGrinder
100
57
  end
101
58
  end
102
59
 
103
- def get_loop_device
104
- begin
105
- loop_device = @exec_helper.execute("losetup -f 2>&1").strip
106
- rescue
107
- raise "No free loop devices available, please free at least one. See 'losetup -d' command."
108
- end
60
+ # Synchronizes filesystem from one image with an empty disk image.
61
+ # Input image can be a partioned image or a partition image itself.
62
+ # Output disk is a partition image.
63
+ #
64
+ # See also https://bugzilla.redhat.com/show_bug.cgi?id=690819
65
+ #
66
+ def sync_filesystem(guestfs, guestfs_helper)
67
+ devices = guestfs.list_devices
68
+ in_device = devices.first
69
+ out_device = devices.last
70
+ partitions = guestfs.list_partitions.reject { |i| !(i =~ /^#{in_device}/) }
109
71
 
110
- loop_device
111
- end
72
+ @log.debug "Loading SELinux policy to sync filesystem..."
73
+ partitions.size > 0 ? guestfs_helper.mount_partitions(in_device) : guestfs_helper.mount_partition(in_device, '/')
74
+ guestfs_helper.load_selinux_policy
75
+ partitions.size > 0 ? guestfs_helper.umount_partitions(in_device) : guestfs_helper.umount_partition(in_device)
76
+ @log.debug "SELinux policy was loaded, we're ready to sync filesystem."
77
+
78
+ @log.info "Synchronizing filesystems..."
79
+
80
+ # Create mount points in libguestfs
81
+ guestfs.mkmountpoint('/in')
82
+ guestfs.mkmountpoint('/out')
83
+ guestfs.mkmountpoint('/out/in')
84
+
85
+ # Create filesystem on EC2 disk
86
+ guestfs.mkfs(@appliance_config.default_filesystem_type, out_device)
87
+ # Set root partition label
88
+ guestfs.set_e2label(out_device, '79d3d2d4') # This is a CRC32 from /
89
+
90
+ # Mount empty EC2 disk to /out
91
+ guestfs_helper.mount_partition(out_device, '/out/in')
92
+ partitions.size > 0 ? guestfs_helper.mount_partitions(in_device, '/in') : guestfs_helper.mount_partition(in_device, '/in')
93
+
94
+ @log.debug "Copying files..."
95
+
96
+ # Copy the filesystem
97
+ guestfs.cp_a('/in/', '/out')
98
+
99
+ @log.debug "Files copied."
112
100
 
113
- def calculate_disk_offsets(disk)
114
- @log.debug "Calculating offsets for '#{File.basename(disk)}' disk..."
115
- loop_device = get_loop_device
101
+ # Better make sure...
102
+ guestfs.sync
116
103
 
117
- @exec_helper.execute("losetup #{loop_device} '#{disk}'")
118
- offsets = @exec_helper.execute("parted #{loop_device} 'unit B print' | grep -e '^ [0-9]' | awk '{ print $2 }'").scan(/\d+/)
119
- # wait one secont before freeing loop device
120
- sleep 1
121
- @exec_helper.execute("losetup -d #{loop_device}")
104
+ guestfs_helper.umount_partition(out_device)
105
+ partitions.size > 0 ? guestfs_helper.umount_partitions(in_device) : guestfs_helper.umount_partition(in_device)
122
106
 
123
- @log.trace "Offsets:\n#{offsets}"
107
+ guestfs.rmmountpoint('/out/in')
108
+ guestfs.rmmountpoint('/out')
109
+ guestfs.rmmountpoint('/in')
124
110
 
125
- offsets
111
+ @log.info "Filesystems synchronized."
112
+
113
+ # Remount the destination disk
114
+ guestfs_helper.mount_partition(out_device, '/')
126
115
  end
127
116
 
128
117
  def create_disk(disk, size)
129
118
  @log.trace "Preparing disk..."
130
- @exec_helper.execute "dd if=/dev/zero of='#{disk}' bs=1 count=0 seek=#{size * 1024}M"
119
+ @exec_helper.execute "dd if=/dev/zero of='#{disk}' bs=1 count=0 seek=#{(size * 1024).to_i}M"
131
120
  @log.trace "Disk prepared"
132
121
  end
133
122
 
134
- def create_filesystem(loop_device, options = {})
123
+ def customize(disks, options = {})
135
124
  options = {
136
- :type => @appliance_config.hardware.partitions['/']['type'],
137
- :label => '/'
125
+ :ide_disk => ((@appliance_config.os.name == 'rhel' or @appliance_config.os.name == 'centos') and @appliance_config.os.version == '5') ? true : false
138
126
  }.merge(options)
139
127
 
140
- @log.trace "Creating filesystem..."
141
-
142
- case options[:type]
143
- when 'ext3', 'ext4'
144
- @exec_helper.execute "mke2fs -T #{options[:type]} -L '#{options[:label]}' -F #{loop_device}"
145
- else
146
- raise "Unsupported filesystem specified: #{options[:type]}"
147
- end
148
-
149
- @log.trace "Filesystem created"
150
- end
151
-
152
- def sync_files(from_dir, to_dir)
153
- @log.debug "Syncing files between #{from_dir} and #{to_dir}..."
154
- @exec_helper.execute "rsync -Xura #{from_dir.gsub(' ', '\ ')}/* '#{to_dir}'"
155
- @log.debug "Sync finished."
156
- end
157
-
158
- def customize(disk_path)
159
- GuestFSHelper.new(disk_path, :log => @log).customize(:ide_disk => ((@appliance_config.os.name == 'rhel' or @appliance_config.os.name == 'centos') and @appliance_config.os.version == '5') ? true : false) do |guestfs, guestfs_helper|
128
+ GuestFSHelper.new(disks, @appliance_config, @config, :log => @log).customize(options) do |guestfs, guestfs_helper|
160
129
  yield guestfs, guestfs_helper
161
130
  end
162
131
  end
@@ -20,13 +20,33 @@ require 'boxgrinder-core/helpers/log-helper'
20
20
 
21
21
  module BoxGrinder
22
22
  class LinuxHelper
23
- def initialize( options = {} )
23
+ def initialize(options = {})
24
24
  @log = options[:log] || LogHelper.new
25
25
  end
26
26
 
27
- def kernel_version( guestfs )
27
+ # Returns valid array of sorted mount points
28
+ #
29
+ # ['/', '/home'] => ['/', '/home']
30
+ # ['/tmp-eventlog', '/', '/ubrc', '/tmp-config'] => ['/', '/ubrc', '/tmp-config', '/tmp-eventlog']
31
+ #
32
+ def partition_mount_points(partitions)
33
+ partitions.keys.sort do |a, b|
34
+ if a.count('/') > b.count('/')
35
+ v = 1
36
+ else
37
+ if a.count('/') < b.count('/')
38
+ v = -1
39
+ else
40
+ v = a.length <=> b.length
41
+ end
42
+ end
43
+ v
44
+ end
45
+ end
46
+
47
+ def kernel_version(guestfs)
28
48
  kernel_versions = guestfs.ls("/lib/modules")
29
- version = kernel_versions.last
49
+ version = kernel_versions.last
30
50
 
31
51
  if kernel_versions.size > 1
32
52
  kernel_versions.each do |v|
@@ -40,19 +60,19 @@ module BoxGrinder
40
60
  version
41
61
  end
42
62
 
43
- def kernel_image_name( guestfs )
63
+ def kernel_image_name(guestfs)
44
64
  guestfs.sh("ls -1 /boot | grep initramfs | wc -l").chomp.strip.to_i > 0 ? "initramfs" : "initrd"
45
65
  end
46
66
 
47
- def recreate_kernel_image( guestfs, modules = [] )
48
- kernel_version = kernel_version( guestfs )
49
- kernel_image_name = kernel_image_name( guestfs )
67
+ def recreate_kernel_image(guestfs, modules = [])
68
+ kernel_version = kernel_version(guestfs)
69
+ kernel_image_name = kernel_image_name(guestfs)
50
70
 
51
71
  if guestfs.exists("/sbin/dracut") != 0
52
72
  command = "/sbin/dracut -f -v --add-drivers #{modules.join(' ')}"
53
73
  else
54
74
  drivers_argument = ""
55
- modules.each { |mod| drivers_argument << " --preload=#{mod}" }
75
+ modules.each { |mod| drivers_argument << " --preload=#{mod}" }
56
76
 
57
77
  command = "/sbin/mkinitrd -f -v#{drivers_argument}"
58
78
  end
@@ -60,7 +80,7 @@ module BoxGrinder
60
80
  @log.trace "Additional modules to preload in kernel: #{modules.join(', ')}"
61
81
 
62
82
  @log.debug "Recreating kernel image for #{kernel_version} kernel..."
63
- guestfs.sh( "#{command} /boot/#{kernel_image_name}-#{kernel_version}.img #{kernel_version}" )
83
+ guestfs.sh("#{command} /boot/#{kernel_image_name}-#{kernel_version}.img #{kernel_version}")
64
84
  @log.debug "Kernel image recreated."
65
85
  end
66
86
  end
@@ -24,6 +24,7 @@ require 'boxgrinder-build/plugins/delivery/s3/s3-plugin'
24
24
  require 'boxgrinder-build/plugins/delivery/sftp/sftp-plugin'
25
25
  require 'boxgrinder-build/plugins/delivery/ebs/ebs-plugin'
26
26
  require 'boxgrinder-build/plugins/delivery/local/local-plugin'
27
+ require 'boxgrinder-build/plugins/delivery/elastichosts/elastichosts-plugin'
27
28
 
28
29
  require 'boxgrinder-build/plugins/platform/vmware/vmware-plugin'
29
30
  require 'boxgrinder-build/plugins/platform/ec2/ec2-plugin'
@@ -19,6 +19,7 @@
19
19
  require 'rubygems'
20
20
  require 'boxgrinder-core/helpers/exec-helper'
21
21
  require 'boxgrinder-core/helpers/log-helper'
22
+ require 'boxgrinder-core/errors'
22
23
  require 'boxgrinder-build/helpers/image-helper'
23
24
  require 'boxgrinder-build/managers/plugin-manager'
24
25
  require 'ostruct'
@@ -41,7 +42,7 @@ module BoxGrinder
41
42
  @config = config
42
43
  @appliance_config = appliance_config
43
44
  @options = options
44
- @log = options[:log] || Logger.new(STDOUT)
45
+ @log = options[:log] || LogHelper.new
45
46
  @exec_helper = options[:exec_helper] || ExecHelper.new(:log => @log)
46
47
  @image_helper = options[:image_helper] || ImageHelper.new(@config, @appliance_config, :log => @log)
47
48
  @previous_plugin_info = options[:previous_plugin_info]
@@ -111,7 +112,7 @@ module BoxGrinder
111
112
  more_info = doc.nil? ? '' : "See #{doc} for more info"
112
113
 
113
114
  fields.each do |field|
114
- raise "Please specify a valid '#{field}' key in BoxGrinder configuration file: '#{@config.file}' or use CLI '--#{@plugin_info[:type]}-config #{field}:DATA' argument. #{more_info}" if @plugin_config[field].nil?
115
+ raise PluginValidationError, "Please specify a valid '#{field}' key in BoxGrinder configuration file: '#{@config.file}' or use CLI '--#{@plugin_info[:type]}-config #{field}:DATA' argument. #{more_info}" if @plugin_config[field].nil?
115
116
  end
116
117
  end
117
118
 
@@ -43,12 +43,9 @@ module BoxGrinder
43
43
  }
44
44
 
45
45
  def after_init
46
- begin
46
+ if valid_platform?
47
47
  @current_avaibility_zone = open('http://169.254.169.254/latest/meta-data/placement/availability-zone').string
48
48
  @region = @current_avaibility_zone.scan(/((\w+)-(\w+)-(\d+))/).flatten.first
49
- rescue
50
- @current_avaibility_zone = nil
51
- @region = nil
52
49
  end
53
50
 
54
51
  set_default_config_value('availability_zone', @current_avaibility_zone)
@@ -113,42 +110,17 @@ module BoxGrinder
113
110
  # wait for volume to be attached
114
111
  wait_for_volume_status('in-use', volume_id)
115
112
 
116
- sleep 5 # let's wait to discover the attached volume by OS
113
+ sleep 10 # let's wait to discover the attached volume by OS
117
114
 
118
115
  @log.info "Copying data to EBS volume..."
119
116
 
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)
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)
125
119
 
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"
120
+ @log.debug "Adjusting /etc/fstab..."
121
+ adjust_fstab(guestfs)
131
122
  end
132
123
 
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
124
  @log.debug "Detaching EBS volume..."
153
125
 
154
126
  @ec2.detach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id)
@@ -228,9 +200,9 @@ module BoxGrinder
228
200
  false
229
201
  end
230
202
 
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")
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")
234
206
  end
235
207
 
236
208
  def wait_for_snapshot_status(status, snapshot_id)
@@ -255,7 +227,7 @@ module BoxGrinder
255
227
  return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}")
256
228
  return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}")
257
229
 
258
- raise "Not found device for suffix #{suffix}"
230
+ raise "Device for suffix '#{suffix}' not found!"
259
231
  end
260
232
 
261
233
  def free_device_suffix
@@ -268,13 +240,12 @@ module BoxGrinder
268
240
 
269
241
  def valid_platform?
270
242
  begin
271
- open("http://169.254.169.254/1.0/meta-data/local-ipv4")
272
- true
273
- rescue
243
+ return Resolv.getname("169.254.169.254").include?(".ec2.internal")
244
+ rescue Resolv::ResolvError
274
245
  false
275
246
  end
276
247
  end
277
248
  end
278
249
  end
279
250
 
280
- plugin :class => BoxGrinder::EBSPlugin, :type => :delivery, :name => :ebs, :full_name => "Elastic Block Storage"
251
+ plugin :class => BoxGrinder::EBSPlugin, :type => :delivery, :name => :ebs, :full_name => "Elastic Block Storage"
@@ -0,0 +1,209 @@
1
+ #
2
+ # Copyright 2010 Red Hat, Inc.
3
+ #
4
+ # This is free software; you can redistribute it and/or modify it
5
+ # under the terms of the GNU Lesser General Public License as
6
+ # published by the Free Software Foundation; either version 3 of
7
+ # the License, or (at your option) any later version.
8
+ #
9
+ # This software is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this software; if not, write to the Free
16
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
+
19
+ require 'boxgrinder-build/plugins/base-plugin'
20
+ require 'restclient'
21
+ require 'zlib'
22
+ require 'cgi'
23
+
24
+ module BoxGrinder
25
+ class ElasticHostsPlugin < BasePlugin
26
+ def after_init
27
+ set_default_config_value('chunk', 64) # chunk size in MB
28
+ set_default_config_value('start_part', 0) # part number to start uploading
29
+ set_default_config_value('wait', 5) # wait time before retrying upload
30
+ set_default_config_value('retry', 3) # number of retries
31
+ set_default_config_value('ssl', false) # use SSL?
32
+ set_default_config_value('drive_name', @appliance_config.name)
33
+ end
34
+
35
+ def execute(type = :elastichosts)
36
+ validate_plugin_config(['endpoint', 'username', 'password'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#ElasticHosts_Delivery_Plugin')
37
+
38
+ raise PluginValidationError, "You can use ElasticHosts plugin with base appliances (appliances created with operating system plugins) only, see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#ElasticHosts_Delivery_Plugin." unless @previous_plugin_info[:type] == :os
39
+
40
+ upload
41
+ create_server
42
+ end
43
+
44
+ def disk_size
45
+ size = 0
46
+ @appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] }
47
+ size
48
+ end
49
+
50
+ def hash_to_request(h)
51
+ body = ""
52
+
53
+ h.each do |k, v|
54
+ body << "#{k} #{v.to_s}\n"
55
+ end
56
+
57
+ body
58
+ end
59
+
60
+ def response_to_hash(r)
61
+ h = {}
62
+
63
+ r.each_line do |l|
64
+ h[$1] = $2 if l =~ /(\w+) (.*)/
65
+ end
66
+
67
+ h
68
+ end
69
+
70
+ def create_remote_disk
71
+ size = disk_size
72
+
73
+ @log.info "Creating new #{size} GB disk..."
74
+
75
+
76
+ body = hash_to_request(
77
+ 'size' => size * 1024 *1024 * 1024,
78
+ 'name' => @plugin_config['drive_name']
79
+ )
80
+
81
+ begin
82
+ ret = response_to_hash(RestClient.post(api_url('/drives/create'), body))
83
+
84
+
85
+ @log.info "Disk created with UUID: #{ret['drive']}."
86
+ rescue => e
87
+ @log.error e.info
88
+ raise PluginError, "An error occured while creating the drive, #{e.message}. See logs for more info."
89
+ end
90
+
91
+ ret['drive']
92
+ end
93
+
94
+ def api_url(path)
95
+ "#{@plugin_config['ssl'] ? 'https' : 'http'}://#{CGI.escape(@plugin_config['username'])}:#{@plugin_config['password']}@#{@plugin_config['endpoint']}#{path}"
96
+ end
97
+
98
+ def upload
99
+ @log.info "Uploading appliance..."
100
+
101
+ # Create the disk with specific size or use already existing
102
+ @plugin_config['drive_uuid'] = create_remote_disk unless @plugin_config['drive_uuid']
103
+
104
+ upload_chunks
105
+
106
+ @log.info "Appliance uploaded."
107
+ end
108
+
109
+ def upload_chunks
110
+ @step = @plugin_config['chunk'] * 1024 * 1024 # in bytes
111
+ part = @plugin_config['start_part']
112
+
113
+ @log.info "Uploading disk in #{disk_size * 1024 / @plugin_config['chunk']} parts."
114
+
115
+ File.open(@previous_deliverables.disk, 'rb') do |f|
116
+ while !f.eof?
117
+ f.seek(part * @step, File::SEEK_SET)
118
+
119
+ data = compress(f.read(@step))
120
+ upload_chunk(data, part)
121
+
122
+ part += 1
123
+ end
124
+ end
125
+
126
+ @log.info "Appliance #{@appliance_config.name} uploaded to drive with UUID #{@plugin_config['drive_uuid']}."
127
+ end
128
+
129
+ def compress(data)
130
+ @log.trace "Compressing #{data.size / 1024} kB chunk of data..."
131
+
132
+ io = StringIO.new
133
+
134
+ writer = Zlib::GzipWriter.new(io, Zlib::DEFAULT_COMPRESSION, Zlib::FINISH)
135
+ writer.write(data)
136
+ writer.close
137
+
138
+ @log.trace "Data compressed to #{io.size / 1024} kB."
139
+
140
+ io.string
141
+ end
142
+
143
+ def upload_chunk(data, part)
144
+ try = 1
145
+
146
+ url = api_url("/drives/#{@plugin_config['drive_uuid']}/write/#{@step * part}")
147
+
148
+ begin
149
+ @log.info "Uploading part #{part+1}..."
150
+
151
+ RestClient.post url,
152
+ data,
153
+ :content_type => "application/octet-stream",
154
+ 'Content-Encoding' => 'gzip'
155
+
156
+ @log.info "Part #{part+1} uploaded."
157
+ rescue => e
158
+ @log.warn "An error occured while uploading #{part} chunk, #{e.message}"
159
+ try += 1
160
+
161
+ unless try > @plugin_config['retry']
162
+ # Let's sleep for specified amount of time
163
+ sleep @plugin_config['wait']
164
+ retry
165
+ else
166
+ @log.error e.info
167
+ raise PluginError, "Couldn't upload appliance, #{e.message}."
168
+ end
169
+ end
170
+ end
171
+
172
+ def is_cloudsigma?
173
+ !@plugin_config['endpoint'].match(/cloudsigma\.com$/).nil?
174
+ end
175
+
176
+ # Creates the server for previously uploaded disk
177
+ def create_server
178
+ @log.info "Creating new server..."
179
+
180
+ memory = ((is_cloudsigma? and @appliance_config.hardware.memory < 512) ? 512 : @appliance_config.hardware.memory)
181
+
182
+ body = hash_to_request(
183
+ 'name' => "#{@appliance_config.name}-#{@appliance_config.version}.#{@appliance_config.release}",
184
+ 'cpu' => @appliance_config.hardware.cpus * 1000, # MHz
185
+ 'smp' => 'auto',
186
+ 'mem' => memory,
187
+ 'persistent' => 'true', # hack
188
+ 'ide:0:0' => @plugin_config['drive_uuid'],
189
+ 'boot' => 'ide:0:0',
190
+ 'nic:0:model' => 'e1000',
191
+ 'nic:0:dhcp' => 'auto',
192
+ 'vnc:ip' => 'auto',
193
+ 'vnc:password' => (0...8).map { (('a'..'z').to_a + ('A'..'Z').to_a)[rand(52)] }.join # 8 character VNC password
194
+ )
195
+
196
+ begin
197
+ path = is_cloudsigma? ? '/servers/create' : '/servers/create/stopped'
198
+ ret = response_to_hash(RestClient.post(api_url(path), body))
199
+
200
+ @log.info "Server was registered with '#{ret['name']}' name as '#{ret['server']}' UUID. Use web UI or API tools to start your server."
201
+ rescue => e
202
+ @log.error e.info
203
+ raise PluginError, "An error occured while creating the server, #{e.message}. See logs for more info."
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ plugin :class => BoxGrinder::ElasticHostsPlugin, :type => :delivery, :name => :elastichosts, :full_name => "ElasticHosts"
@@ -49,13 +49,17 @@ module BoxGrinder
49
49
  packages << '@core'
50
50
  packages << "system-config-firewall-base"
51
51
  packages << "dhclient"
52
-
53
- # kernel_PAE for 32 bit, kernel for 64 bit
52
+
54
53
  packages.delete('kernel')
55
54
  packages.delete('kernel-PAE')
56
- packages << (@appliance_config.is64bit? ? "kernel" : "kernel-PAE")
55
+
56
+ if @appliance_config.is64bit?
57
+ packages << "kernel"
58
+ else
59
+ @appliance_config.os.pae ? packages << "kernel-PAE" : packages << "kernel"
60
+ end
57
61
  end
58
62
  end
59
63
  end
60
64
 
61
- plugin :class => BoxGrinder::FedoraPlugin, :type => :os, :name => :fedora, :full_name => "Fedora", :versions => ["13", "14", "15", "rawhide"]
65
+ plugin :class => BoxGrinder::FedoraPlugin, :type => :os, :name => :fedora, :full_name => "Fedora", :versions => ["13", "14", "15", "rawhide"]
@@ -26,8 +26,6 @@ module BoxGrinder
26
26
  end
27
27
 
28
28
  def build_rhel(appliance_definition_file, repos = {})
29
- adjust_partition_table
30
-
31
29
  normalize_packages(@appliance_config.packages)
32
30
 
33
31
  build_with_appliance_creator(appliance_definition_file, repos) do |guestfs, guestfs_helper|
@@ -53,11 +51,6 @@ module BoxGrinder
53
51
  package_array.each { |package| packages << package unless packages.include?(package) }
54
52
  end
55
53
 
56
- # https://bugzilla.redhat.com/show_bug.cgi?id=466275
57
- def adjust_partition_table
58
- @appliance_config.hardware.partitions['/boot'] = {'root' => '/boot', 'type' => 'ext3', 'size' => 0.1} if @appliance_config.hardware.partitions['/boot'].nil?
59
- end
60
-
61
54
  def execute(appliance_definition_file)
62
55
  build_rhel(appliance_definition_file)
63
56
  end
@@ -19,6 +19,7 @@
19
19
  require 'fileutils'
20
20
  require 'yaml'
21
21
  require 'erb'
22
+ require 'zlib'
22
23
 
23
24
  module BoxGrinder
24
25
 
@@ -30,6 +31,8 @@ module BoxGrinder
30
31
  @dir = dir
31
32
  @log = options[:log] || Logger.new(STDOUT)
32
33
 
34
+ @linux_helper = LinuxHelper.new(:log => @log)
35
+
33
36
  @kickstart_file = "#{@dir.tmp}/#{@appliance_config.name}.ks"
34
37
  end
35
38
 
@@ -54,7 +57,7 @@ module BoxGrinder
54
57
 
55
58
  cost = 40
56
59
 
57
- definition['partitions'] = @appliance_config.hardware.partitions
60
+ definition['mount_points'] = @linux_helper.partition_mount_points(@appliance_config.hardware.partitions)
58
61
 
59
62
  repos = []
60
63
  repos += default_repos if @appliance_config.default_repos