From 51680b6ebb645d37ebdfcd122ca163b3a638aefa Mon Sep 17 00:00:00 2001 From: Tails developers Date: Fri, 19 Dec 2014 00:40:08 +0100 Subject: files copied from https://git-tails.immerda.ch/tails - many thanks to the tails developers for their nice work and documentation of it - these files have been released under the GNU General Public License version 3 or (at your option) any later version features/images has been omitted --- features/support/helpers/storage_helper.rb | 143 +++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 features/support/helpers/storage_helper.rb (limited to 'features/support/helpers/storage_helper.rb') 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 -- cgit v1.2.3-70-g09d2