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.
- data/CHANGELOG +11 -0
- data/README.md +32 -34
- data/Rakefile +7 -1
- data/bin/boxgrinder-build +10 -17
- data/boxgrinder-build.gemspec +7 -4
- data/lib/boxgrinder-build/appliance.rb +13 -16
- data/lib/boxgrinder-build/helpers/guestfs-helper.rb +84 -40
- data/lib/boxgrinder-build/helpers/image-helper.rb +54 -85
- data/lib/boxgrinder-build/helpers/linux-helper.rb +29 -9
- data/lib/boxgrinder-build/helpers/plugin-helper.rb +1 -0
- data/lib/boxgrinder-build/plugins/base-plugin.rb +3 -2
- data/lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb +13 -42
- data/lib/boxgrinder-build/plugins/delivery/elastichosts/elastichosts-plugin.rb +209 -0
- data/lib/boxgrinder-build/plugins/os/fedora/fedora-plugin.rb +8 -4
- data/lib/boxgrinder-build/plugins/os/rhel/rhel-plugin.rb +0 -7
- data/lib/boxgrinder-build/plugins/os/rpm-based/kickstart.rb +4 -1
- data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin.rb +52 -9
- data/lib/boxgrinder-build/plugins/os/rpm-based/src/appliance.ks.erb +6 -2
- data/lib/boxgrinder-build/plugins/platform/ec2/ec2-plugin.rb +4 -44
- data/lib/boxgrinder-build/plugins/platform/ec2/src/fstab_32bit +1 -1
- data/lib/boxgrinder-build/plugins/platform/ec2/src/fstab_64bit +1 -1
- data/lib/boxgrinder-build/plugins/platform/ec2/src/menu.lst +2 -2
- data/lib/boxgrinder-build/plugins/platform/virtualbox/virtualbox-plugin.rb +24 -13
- data/lib/boxgrinder-build/plugins/platform/vmware/src/base.vmx +2 -2
- data/lib/boxgrinder-build/plugins/platform/vmware/vmware-plugin.rb +0 -1
- data/rubygem-boxgrinder-build.spec +23 -1
- data/spec/appliance-spec.rb +5 -79
- data/spec/helpers/augeas-helper-spec.rb +1 -0
- data/spec/helpers/guestfs-helper-spec.rb +79 -62
- data/spec/helpers/image-helper-spec.rb +88 -129
- data/spec/helpers/linux-helper-spec.rb +22 -5
- data/spec/helpers/package-helper-spec.rb +1 -0
- data/spec/helpers/plugin-helper-spec.rb +1 -0
- data/spec/managers/plugin-manager-spec.rb +3 -2
- data/spec/plugins/base-plugin-spec.rb +1 -1
- data/spec/plugins/delivery/ebs/ebs-plugin-spec.rb +21 -13
- data/spec/plugins/delivery/elastichosts/elastichosts-plugin-spec.rb +320 -0
- data/spec/plugins/delivery/local/local-plugin-spec.rb +1 -0
- data/spec/plugins/delivery/s3/s3-plugin-spec.rb +1 -0
- data/spec/plugins/delivery/sftp/sftp-plugin-spec.rb +1 -0
- data/spec/plugins/os/centos/centos-plugin-spec.rb +1 -0
- data/spec/plugins/os/fedora/fedora-plugin-spec.rb +13 -1
- data/spec/plugins/os/rhel/rhel-plugin-spec.rb +1 -15
- data/spec/plugins/os/rpm-based/kickstart-spec.rb +1 -0
- data/spec/plugins/os/rpm-based/rpm-based-os-plugin-spec.rb +75 -15
- data/spec/plugins/os/rpm-based/rpm-dependency-validator-spec.rb +1 -0
- data/spec/plugins/platform/ec2/ec2-plugin-spec.rb +10 -43
- data/spec/plugins/platform/virtualbox/virtualbox-plugin-spec.rb +89 -10
- data/spec/plugins/platform/vmware/vmware-plugin-spec.rb +3 -2
- metadata +21 -7
- data/lib/boxgrinder-build/helpers/appliance-customize-helper.rb +0 -45
- 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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
111
|
-
|
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
|
-
|
114
|
-
|
115
|
-
loop_device = get_loop_device
|
101
|
+
# Better make sure...
|
102
|
+
guestfs.sync
|
116
103
|
|
117
|
-
|
118
|
-
|
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
|
-
|
107
|
+
guestfs.rmmountpoint('/out/in')
|
108
|
+
guestfs.rmmountpoint('/out')
|
109
|
+
guestfs.rmmountpoint('/in')
|
124
110
|
|
125
|
-
|
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
|
123
|
+
def customize(disks, options = {})
|
135
124
|
options = {
|
136
|
-
:
|
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.
|
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(
|
23
|
+
def initialize(options = {})
|
24
24
|
@log = options[:log] || LogHelper.new
|
25
25
|
end
|
26
26
|
|
27
|
-
|
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
|
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(
|
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(
|
48
|
-
kernel_version
|
49
|
-
kernel_image_name
|
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
|
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(
|
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] ||
|
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
|
-
|
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
|
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
|
-
|
121
|
-
|
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
|
-
|
127
|
-
|
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(
|
232
|
-
|
233
|
-
|
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 "
|
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
|
-
|
272
|
-
|
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
|
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
|
-
|
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
|
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['
|
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
|