summaryrefslogtreecommitdiffstats
path: root/features/support/helpers/storage_helper.rb
diff options
context:
space:
mode:
Diffstat (limited to 'features/support/helpers/storage_helper.rb')
-rw-r--r--features/support/helpers/storage_helper.rb143
1 files changed, 143 insertions, 0 deletions
diff --git a/features/support/helpers/storage_helper.rb b/features/support/helpers/storage_helper.rb
new file mode 100644
index 00000000..80a1e1e0
--- /dev/null
+++ b/features/support/helpers/storage_helper.rb
@@ -0,0 +1,143 @@
+# Helper class for manipulating VM storage *volumes*, i.e. it deals
+# only with creation of images and keeps a name => volume path lookup
+# table (plugging drives or getting info of plugged devices is done in
+# the VM class). We'd like better coupling, but given the ridiculous
+# disconnect between Libvirt::StoragePool and Libvirt::Domain (hint:
+# they have nothing with each other to do whatsoever) it's what makes
+# sense.
+
+require 'libvirt'
+require 'rexml/document'
+require 'etc'
+
+class VMStorage
+
+ @@virt = nil
+
+ def initialize(virt, xml_path)
+ @@virt ||= virt
+ @xml_path = xml_path
+ pool_xml = REXML::Document.new(File.read("#{@xml_path}/storage_pool.xml"))
+ pool_name = pool_xml.elements['pool/name'].text
+ begin
+ @pool = @@virt.lookup_storage_pool_by_name(pool_name)
+ rescue Libvirt::RetrieveError
+ # There's no pool with that name, so we don't have to clear it
+ else
+ VMStorage.clear_storage_pool(@pool)
+ end
+ @pool_path = "#{$tmp_dir}/#{pool_name}"
+ pool_xml.elements['pool/target/path'].text = @pool_path
+ @pool = @@virt.define_storage_pool_xml(pool_xml.to_s)
+ @pool.build
+ @pool.create
+ @pool.refresh
+ end
+
+ def VMStorage.clear_storage_pool_volumes(pool)
+ was_not_active = !pool.active?
+ if was_not_active
+ pool.create
+ end
+ pool.list_volumes.each do |vol_name|
+ vol = pool.lookup_volume_by_name(vol_name)
+ vol.delete
+ end
+ if was_not_active
+ pool.destroy
+ end
+ rescue
+ # Some of the above operations can fail if the pool's path was
+ # deleted by external means; let's ignore that.
+ end
+
+ def VMStorage.clear_storage_pool(pool)
+ VMStorage.clear_storage_pool_volumes(pool)
+ pool.destroy if pool.active?
+ pool.undefine
+ end
+
+ def clear_pool
+ VMStorage.clear_storage_pool(@pool)
+ end
+
+ def clear_volumes
+ VMStorage.clear_storage_pool_volumes(@pool)
+ end
+
+ def create_new_disk(name, options = {})
+ options[:size] ||= 2
+ options[:unit] ||= "GiB"
+ options[:type] ||= "qcow2"
+ begin
+ old_vol = @pool.lookup_volume_by_name(name)
+ rescue Libvirt::RetrieveError
+ # noop
+ else
+ old_vol.delete
+ end
+ uid = Etc::getpwnam("libvirt-qemu").uid
+ gid = Etc::getgrnam("libvirt-qemu").gid
+ vol_xml = REXML::Document.new(File.read("#{@xml_path}/volume.xml"))
+ vol_xml.elements['volume/name'].text = name
+ size_b = convert_to_bytes(options[:size].to_f, options[:unit])
+ vol_xml.elements['volume/capacity'].text = size_b.to_s
+ vol_xml.elements['volume/target/format'].attributes["type"] = options[:type]
+ vol_xml.elements['volume/target/path'].text = "#{@pool_path}/#{name}"
+ vol_xml.elements['volume/target/permissions/owner'].text = uid.to_s
+ vol_xml.elements['volume/target/permissions/group'].text = gid.to_s
+ vol = @pool.create_volume_xml(vol_xml.to_s)
+ @pool.refresh
+ end
+
+ def clone_to_new_disk(from, to)
+ begin
+ old_to_vol = @pool.lookup_volume_by_name(to)
+ rescue Libvirt::RetrieveError
+ # noop
+ else
+ old_to_vol.delete
+ end
+ from_vol = @pool.lookup_volume_by_name(from)
+ xml = REXML::Document.new(from_vol.xml_desc)
+ pool_path = REXML::Document.new(@pool.xml_desc).elements['pool/target/path'].text
+ xml.elements['volume/name'].text = to
+ xml.elements['volume/target/path'].text = "#{pool_path}/#{to}"
+ @pool.create_volume_xml_from(xml.to_s, from_vol)
+ end
+
+ def disk_format(name)
+ vol = @pool.lookup_volume_by_name(name)
+ vol_xml = REXML::Document.new(vol.xml_desc)
+ return vol_xml.elements['volume/target/format'].attributes["type"]
+ end
+
+ def disk_path(name)
+ @pool.lookup_volume_by_name(name).path
+ end
+
+ # We use parted for the disk_mk* functions since it can format
+ # partitions "inside" the super block device; mkfs.* need a
+ # partition device (think /dev/sdaX), so we'd have to use something
+ # like losetup or kpartx, which would require administrative
+ # privileges. These functions only work for raw disk images.
+
+ # TODO: We should switch to guestfish/libguestfs (which has
+ # ruby-bindings) so we could use qcow2 instead of raw, and more
+ # easily use LVM volumes.
+
+ # For type, see label-type for mklabel in parted(8)
+ def disk_mklabel(name, type)
+ assert_equal("raw", disk_format(name))
+ path = disk_path(name)
+ cmd_helper("/sbin/parted -s '#{path}' mklabel #{type}")
+ end
+
+ # For fstype, see fs-type for mkfs in parted(8)
+ def disk_mkpartfs(name, fstype)
+ assert(disk_format(name), "raw")
+ path = disk_path(name)
+ cmd_helper("/sbin/parted -s '#{path}' mkpartfs primary '#{fstype}' 0% 100%")
+ end
+
+end