summaryrefslogtreecommitdiffstats
path: root/features/support/helpers/vm_helper.rb
diff options
context:
space:
mode:
Diffstat (limited to 'features/support/helpers/vm_helper.rb')
-rw-r--r--features/support/helpers/vm_helper.rb532
1 files changed, 391 insertions, 141 deletions
diff --git a/features/support/helpers/vm_helper.rb b/features/support/helpers/vm_helper.rb
index 2b5ad291..6d7204d4 100644
--- a/features/support/helpers/vm_helper.rb
+++ b/features/support/helpers/vm_helper.rb
@@ -1,79 +1,122 @@
require 'libvirt'
require 'rexml/document'
-class VM
+class ExecutionFailedInVM < StandardError
+end
+
+class VMNet
- # These class attributes will be lazily initialized during the first
- # instantiation:
- # This is the libvirt connection, of which we only want one and
- # which can persist for different VM instances (even in parallel)
- @@virt = nil
- # This is a storage helper that deals with volume manipulation. The
- # storage it deals with persists across VMs, by necessity.
- @@storage = nil
+ attr_reader :net_name, :net
- def VM.storage
- return @@storage
+ def initialize(virt, xml_path)
+ @virt = virt
+ @net_name = LIBVIRT_NETWORK_NAME
+ net_xml = File.read("#{xml_path}/default_net.xml")
+ rexml = REXML::Document.new(net_xml)
+ rexml.elements['network'].add_element('name')
+ rexml.elements['network/name'].text = @net_name
+ rexml.elements['network'].add_element('uuid')
+ rexml.elements['network/uuid'].text = LIBVIRT_NETWORK_UUID
+ update(rexml.to_s)
+ rescue Exception => e
+ destroy_and_undefine
+ raise e
end
- def storage
- return @@storage
+ # We lookup by name so we also catch networks from previous test
+ # suite runs that weren't properly cleaned up (e.g. aborted).
+ def destroy_and_undefine
+ begin
+ old_net = @virt.lookup_network_by_name(@net_name)
+ old_net.destroy if old_net.active?
+ old_net.undefine
+ rescue
+ end
end
- attr_reader :domain, :display, :ip, :net
+ def update(xml)
+ destroy_and_undefine
+ @net = @virt.define_network_xml(xml)
+ @net.create
+ end
+
+ def bridge_name
+ @net.bridge_name
+ end
+
+ def bridge_ip_addr
+ net_xml = REXML::Document.new(@net.xml_desc)
+ IPAddr.new(net_xml.elements['network/ip'].attributes['address']).to_s
+ end
+
+ def guest_real_mac
+ net_xml = REXML::Document.new(@net.xml_desc)
+ net_xml.elements['network/ip/dhcp/host/'].attributes['mac']
+ end
- def initialize(xml_path, x_display)
- @@virt ||= Libvirt::open("qemu:///system")
+ def bridge_mac
+ File.open("/sys/class/net/#{bridge_name}/address", "rb").read.chomp
+ end
+end
+
+
+class VM
+
+ attr_reader :domain, :display, :vmnet, :storage
+
+ def initialize(virt, xml_path, vmnet, storage, x_display)
+ @virt = virt
@xml_path = xml_path
+ @vmnet = vmnet
+ @storage = storage
+ @domain_name = LIBVIRT_DOMAIN_NAME
default_domain_xml = File.read("#{@xml_path}/default.xml")
- update_domain(default_domain_xml)
- default_net_xml = File.read("#{@xml_path}/default_net.xml")
- update_net(default_net_xml)
+ rexml = REXML::Document.new(default_domain_xml)
+ rexml.elements['domain'].add_element('name')
+ rexml.elements['domain/name'].text = @domain_name
+ rexml.elements['domain'].add_element('uuid')
+ rexml.elements['domain/uuid'].text = LIBVIRT_DOMAIN_UUID
+ update(rexml.to_s)
@display = Display.new(@domain_name, x_display)
- set_cdrom_boot($tails_iso)
+ set_cdrom_boot(TAILS_ISO)
plug_network
- # unlike the domain and net the storage pool should survive VM
- # teardown (so a new instance can use e.g. a previously created
- # USB drive), so we only create a new one if there is none.
- @@storage ||= VMStorage.new(@@virt, xml_path)
rescue Exception => e
- clean_up_net
- clean_up_domain
+ destroy_and_undefine
raise e
end
- def update_domain(xml)
- domain_xml = REXML::Document.new(xml)
- @domain_name = domain_xml.elements['domain/name'].text
- clean_up_domain
- @domain = @@virt.define_domain_xml(xml)
- end
-
- def update_net(xml)
- net_xml = REXML::Document.new(xml)
- @net_name = net_xml.elements['network/name'].text
- @ip = net_xml.elements['network/ip/dhcp/host/'].attributes['ip']
- clean_up_net
- @net = @@virt.define_network_xml(xml)
- @net.create
+ def update(xml)
+ destroy_and_undefine
+ @domain = @virt.define_domain_xml(xml)
end
- def clean_up_domain
+ # We lookup by name so we also catch domains from previous test
+ # suite runs that weren't properly cleaned up (e.g. aborted).
+ def destroy_and_undefine
+ @display.stop if @display && @display.active?
begin
- domain = @@virt.lookup_domain_by_name(@domain_name)
- domain.destroy if domain.active?
- domain.undefine
+ old_domain = @virt.lookup_domain_by_name(@domain_name)
+ old_domain.destroy if old_domain.active?
+ old_domain.undefine
rescue
end
end
- def clean_up_net
- begin
- net = @@virt.lookup_network_by_name(@net_name)
- net.destroy if net.active?
- net.undefine
- rescue
- end
+ def real_mac
+ @vmnet.guest_real_mac
+ end
+
+ def set_hardware_clock(time)
+ assert(not(is_running?), 'The hardware clock cannot be set when the ' +
+ 'VM is running')
+ assert(time.instance_of?(Time), "Argument must be of type 'Time'")
+ adjustment = (time - Time.now).to_i
+ domain_rexml = REXML::Document.new(@domain.xml_desc)
+ clock_rexml_element = domain_rexml.elements['domain'].add_element('clock')
+ clock_rexml_element.add_attributes('offset' => 'variable',
+ 'basis' => 'utc',
+ 'adjustment' => adjustment.to_s)
+ update(domain_rexml.to_s)
end
def set_network_link_state(state)
@@ -82,7 +125,7 @@ class VM
if is_running?
@domain.update_device(domain_xml.elements['domain/devices/interface'].to_s)
else
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
end
@@ -94,97 +137,101 @@ class VM
set_network_link_state('down')
end
- def set_cdrom_tray_state(state)
- domain_xml = REXML::Document.new(@domain.xml_desc)
- domain_xml.elements.each('domain/devices/disk') do |e|
- if e.attribute('device').to_s == "cdrom"
- e.elements['target'].attributes['tray'] = state
- if is_running?
- @domain.update_device(e.to_s)
- else
- update_domain(domain_xml.to_s)
- end
- end
- end
- end
-
- def eject_cdrom
- set_cdrom_tray_state('open')
- end
-
- def close_cdrom
- set_cdrom_tray_state('closed')
- end
-
def set_boot_device(dev)
if is_running?
raise "boot settings can only be set for inactive vms"
end
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain/os/boot'].attributes['dev'] = dev
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
def set_cdrom_image(image)
+ image = nil if image == ''
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements.each('domain/devices/disk') do |e|
if e.attribute('device').to_s == "cdrom"
- if ! e.elements['source']
- e.add_element('source')
+ if image.nil?
+ e.elements.delete('source')
+ else
+ if ! e.elements['source']
+ e.add_element('source')
+ end
+ e.elements['source'].attributes['file'] = image
end
- e.elements['source'].attributes['file'] = image
if is_running?
- @domain.update_device(e.to_s, Libvirt::Domain::DEVICE_MODIFY_FORCE)
+ @domain.update_device(e.to_s)
else
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
end
end
end
def remove_cdrom
- set_cdrom_image('')
+ set_cdrom_image(nil)
+ rescue Libvirt::Error => e
+ # While the CD-ROM is removed successfully we still get this
+ # error, so let's ignore it.
+ acceptable_error =
+ "Call to virDomainUpdateDeviceFlags failed: internal error: unable to " +
+ "execute QEMU command 'eject': (Tray of device '.*' is not open|" +
+ "Device '.*' is locked)"
+ raise e if not(Regexp.new(acceptable_error).match(e.to_s))
end
def set_cdrom_boot(image)
if is_running?
- raise "boot settings can only be set for inactice vms"
+ raise "boot settings can only be set for inactive vms"
end
set_boot_device('cdrom')
set_cdrom_image(image)
- close_cdrom
end
- def plug_drive(name, type)
- # Get the next free /dev/sdX on guest
- used_devs = []
+ def list_disk_devs
+ ret = []
domain_xml = REXML::Document.new(@domain.xml_desc)
- domain_xml.elements.each('domain/devices/disk/target') do |e|
- used_devs <<= e.attribute('dev').to_s
+ domain_xml.elements.each('domain/devices/disk') do |e|
+ ret << e.elements['target'].attribute('dev').to_s
+ end
+ return ret
+ end
+
+ def plug_drive(name, type)
+ if disk_plugged?(name)
+ raise "disk '#{name}' already plugged"
end
+ removable_usb = nil
+ case type
+ when "removable usb", "usb"
+ type = "usb"
+ removable_usb = "on"
+ when "non-removable usb"
+ type = "usb"
+ removable_usb = "off"
+ end
+ # Get the next free /dev/sdX on guest
letter = 'a'
dev = "sd" + letter
- while used_devs.include? dev
+ while list_disk_devs.include?(dev)
letter = (letter[0].ord + 1).chr
dev = "sd" + letter
end
assert letter <= 'z'
xml = REXML::Document.new(File.read("#{@xml_path}/disk.xml"))
- xml.elements['disk/source'].attributes['file'] = @@storage.disk_path(name)
- xml.elements['disk/driver'].attributes['type'] = @@storage.disk_format(name)
+ xml.elements['disk/source'].attributes['file'] = @storage.disk_path(name)
+ xml.elements['disk/driver'].attributes['type'] = @storage.disk_format(name)
xml.elements['disk/target'].attributes['dev'] = dev
xml.elements['disk/target'].attributes['bus'] = type
- if type == "usb"
- xml.elements['disk/target'].attributes['removable'] = 'on'
- end
+ xml.elements['disk/target'].attributes['removable'] = removable_usb if removable_usb
if is_running?
@domain.attach_device(xml.to_s)
else
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain/devices'].add_element(xml)
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
end
@@ -192,7 +239,7 @@ class VM
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements.each('domain/devices/disk') do |e|
begin
- if e.elements['source'].attribute('file').to_s == @@storage.disk_path(name)
+ if e.elements['source'].attribute('file').to_s == @storage.disk_path(name)
return e.to_s
end
rescue
@@ -202,25 +249,64 @@ class VM
return nil
end
+ def disk_rexml_desc(name)
+ xml = disk_xml_desc(name)
+ if xml
+ return REXML::Document.new(xml)
+ else
+ return nil
+ end
+ end
+
def unplug_drive(name)
xml = disk_xml_desc(name)
@domain.detach_device(xml)
end
+ def disk_type(dev)
+ domain_xml = REXML::Document.new(@domain.xml_desc)
+ domain_xml.elements.each('domain/devices/disk') do |e|
+ if e.elements['target'].attribute('dev').to_s == dev
+ return e.elements['driver'].attribute('type').to_s
+ end
+ end
+ raise "No such disk device '#{dev}'"
+ end
+
def disk_dev(name)
- xml = REXML::Document.new(disk_xml_desc(name))
- return "/dev/" + xml.elements['disk/target'].attribute('dev').to_s
+ rexml = disk_rexml_desc(name) or return nil
+ return "/dev/" + rexml.elements['disk/target'].attribute('dev').to_s
+ end
+
+ def disk_name(dev)
+ dev = File.basename(dev)
+ domain_xml = REXML::Document.new(@domain.xml_desc)
+ domain_xml.elements.each('domain/devices/disk') do |e|
+ if /^#{e.elements['target'].attribute('dev').to_s}/.match(dev)
+ return File.basename(e.elements['source'].attribute('file').to_s)
+ end
+ end
+ raise "No such disk device '#{dev}'"
+ end
+
+ def udisks_disk_dev(name)
+ return disk_dev(name).gsub('/dev/', '/org/freedesktop/UDisks/devices/')
end
def disk_detected?(name)
- return execute("test -b #{disk_dev(name)}").success?
+ dev = disk_dev(name) or return false
+ return execute("test -b #{dev}").success?
+ end
+
+ def disk_plugged?(name)
+ return not(disk_xml_desc(name).nil?)
end
def set_disk_boot(name, type)
if is_running?
raise "boot settings can only be set for inactive vms"
end
- plug_drive(name, type)
+ plug_drive(name, type) if not(disk_plugged?(name))
set_boot_device('hd')
# For some reason setting the boot device doesn't prevent cdrom
# boot unless it's empty
@@ -231,14 +317,19 @@ class VM
# XXX-9p in common_steps.rb for more information.
def add_share(source, tag)
if is_running?
- raise "shares can only be added to inactice vms"
+ raise "shares can only be added to inactive vms"
end
+ # The complete source directory must be group readable by the user
+ # running the virtual machine, and world readable so the user inside
+ # the VM can access it (since we use the passthrough security model).
+ FileUtils.chown_R(nil, "libvirt-qemu", source)
+ FileUtils.chmod_R("go+rX", source)
xml = REXML::Document.new(File.read("#{@xml_path}/fs_share.xml"))
xml.elements['filesystem/source'].attributes['dir'] = source
xml.elements['filesystem/target'].attributes['dir'] = tag
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain/devices'].add_element(xml)
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
def list_shares
@@ -251,13 +342,13 @@ class VM
end
def set_ram_size(size, unit = "KiB")
- raise "System memory can only be added to inactice vms" if is_running?
+ raise "System memory can only be added to inactive vms" if is_running?
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain/memory'].text = size
domain_xml.elements['domain/memory'].attributes['unit'] = unit
domain_xml.elements['domain/currentMemory'].text = size
domain_xml.elements['domain/currentMemory'].attributes['unit'] = unit
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
def get_ram_size_in_bytes
@@ -268,24 +359,24 @@ class VM
end
def set_arch(arch)
- raise "System architecture can only be set to inactice vms" if is_running?
+ raise "System architecture can only be set to inactive vms" if is_running?
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain/os/type'].attributes['arch'] = arch
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
def add_hypervisor_feature(feature)
- raise "Hypervisor features can only be added to inactice vms" if is_running?
+ raise "Hypervisor features can only be added to inactive vms" if is_running?
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain/features'].add_element(feature)
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
def drop_hypervisor_feature(feature)
- raise "Hypervisor features can only be fropped from inactice vms" if is_running?
+ raise "Hypervisor features can only be fropped from inactive vms" if is_running?
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain/features'].delete_element(feature)
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
def disable_pae_workaround
@@ -295,24 +386,24 @@ class VM
xml = <<EOF
<qemu:commandline xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
<qemu:arg value='-cpu'/>
- <qemu:arg value='pentium,-pae'/>
+ <qemu:arg value='qemu32,-pae'/>
</qemu:commandline>
EOF
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain'].add_element(REXML::Document.new(xml))
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
end
def set_os_loader(type)
if is_running?
- raise "boot settings can only be set for inactice vms"
+ raise "boot settings can only be set for inactive vms"
end
if type == 'UEFI'
domain_xml = REXML::Document.new(@domain.xml_desc)
domain_xml.elements['domain/os'].add_element(REXML::Document.new(
'<loader>/usr/share/ovmf/OVMF.fd</loader>'
))
- update_domain(domain_xml.to_s)
+ update(domain_xml.to_s)
else
raise "unsupported OS loader type"
end
@@ -326,21 +417,38 @@ EOF
end
end
- def execute(cmd, user = "root")
- return VMCommand.new(self, cmd, { :user => user, :spawn => false })
+ def execute(cmd, options = {})
+ options[:user] ||= "root"
+ options[:spawn] ||= false
+ if options[:libs]
+ libs = options[:libs]
+ options.delete(:libs)
+ libs = [libs] if not(libs.methods.include? :map)
+ cmds = libs.map do |lib_name|
+ ". /usr/local/lib/tails-shell-library/#{lib_name}.sh"
+ end
+ cmds << cmd
+ cmd = cmds.join(" && ")
+ end
+ return VMCommand.new(self, cmd, options)
end
- def execute_successfully(cmd, user = "root")
- p = execute(cmd, user)
- assert_vmcommand_success(p)
+ def execute_successfully(*args)
+ p = execute(*args)
+ begin
+ assert_vmcommand_success(p)
+ rescue Test::Unit::AssertionFailedError => e
+ raise ExecutionFailedInVM.new(e)
+ end
return p
end
- def spawn(cmd, user = "root")
- return VMCommand.new(self, cmd, { :user => user, :spawn => true })
+ def spawn(cmd, options = {})
+ options[:spawn] = true
+ return execute(cmd, options)
end
- def wait_until_remote_shell_is_up(timeout = 30)
+ def wait_until_remote_shell_is_up(timeout = 90)
VMCommand.wait_until_remote_shell_is_up(self, timeout)
end
@@ -361,32 +469,182 @@ EOF
return execute("pidof -x -o '%PPID' " + process).stdout.chomp.split
end
+ def select_virtual_desktop(desktop_number, user = LIVE_USER)
+ assert(desktop_number >= 0 && desktop_number <=3,
+ "Only values between 0 and 3 are valid virtual desktop numbers")
+ execute_successfully(
+ "xdotool set_desktop '#{desktop_number}'",
+ :user => user
+ )
+ end
+
+ def focus_window(window_title, user = LIVE_USER)
+ def do_focus(window_title, user)
+ execute_successfully(
+ "xdotool search --name '#{window_title}' windowactivate --sync",
+ :user => user
+ )
+ end
+
+ begin
+ do_focus(window_title, user)
+ rescue ExecutionFailedInVM
+ # Often when xdotool fails to focus a window it'll work when retried
+ # after redrawing the screen. Switching to a new virtual desktop then
+ # back seems to be a reliable way to handle this.
+ select_virtual_desktop(3)
+ select_virtual_desktop(0)
+ sleep 5 # there aren't any visual indicators which can be used here
+ do_focus(window_title, user)
+ end
+ end
+
def file_exist?(file)
- execute("test -e #{file}").success?
+ execute("test -e '#{file}'").success?
+ end
+
+ def directory_exist?(directory)
+ execute("test -d '#{directory}'").success?
end
def file_content(file, user = 'root')
# We don't quote #{file} on purpose: we sometimes pass environment variables
# or globs that we want to be interpreted by the shell.
- cmd = execute("cat #{file}", user)
+ cmd = execute("cat #{file}", :user => user)
assert(cmd.success?,
"Could not cat '#{file}':\n#{cmd.stdout}\n#{cmd.stderr}")
return cmd.stdout
end
- def save_snapshot(path)
- @domain.save(path)
- @display.stop
+ def file_append(file, lines, user = 'root')
+ lines = lines.split("\n") if lines.class == String
+ lines.each do |line|
+ cmd = execute("echo '#{line}' >> '#{file}'", :user => user)
+ assert(cmd.success?,
+ "Could not append to '#{file}':\n#{cmd.stdout}\n#{cmd.stderr}")
+ end
+ end
+
+ def set_clipboard(text)
+ execute_successfully("echo -n '#{text}' | xsel --input --clipboard",
+ :user => LIVE_USER)
end
- def restore_snapshot(path)
- # Clean up current domain so its snapshot can be restored
- clean_up_domain
- Libvirt::Domain::restore(@@virt, path)
- @domain = @@virt.lookup_domain_by_name(@domain_name)
+ def get_clipboard
+ execute_successfully("xsel --output --clipboard", :user => LIVE_USER).stdout
+ end
+
+ def internal_snapshot_xml(name)
+ disk_devs = list_disk_devs
+ disks_xml = " <disks>\n"
+ for dev in disk_devs
+ snapshot_type = disk_type(dev) == "qcow2" ? 'internal' : 'no'
+ disks_xml +=
+ " <disk name='#{dev}' snapshot='#{snapshot_type}'></disk>\n"
+ end
+ disks_xml += " </disks>"
+ return <<-EOF
+<domainsnapshot>
+ <name>#{name}</name>
+ <description>Snapshot for #{name}</description>
+#{disks_xml}
+ </domainsnapshot>
+EOF
+ end
+
+ def VM.ram_only_snapshot_path(name)
+ return "#{$config["TMPDIR"]}/#{name}-snapshot.memstate"
+ end
+
+ def save_snapshot(name)
+ # If we have no qcow2 disk device, we'll use "memory state"
+ # snapshots, and if we have at least one qcow2 disk device, we'll
+ # use internal "system checkpoint" (memory + disks) snapshots. We
+ # have to do this since internal snapshots don't work when no
+ # such disk is available. We can do this with external snapshots,
+ # which are better in many ways, but libvirt doesn't know how to
+ # restore (revert back to) them yet.
+ # WARNING: If only transient disks, i.e. disks that were plugged
+ # after starting the domain, are used then the memory state will
+ # be dropped. External snapshots would also fix this.
+ internal_snapshot = false
+ domain_xml = REXML::Document.new(@domain.xml_desc)
+ domain_xml.elements.each('domain/devices/disk') do |e|
+ if e.elements['driver'].attribute('type').to_s == "qcow2"
+ internal_snapshot = true
+ break
+ end
+ end
+
+ # Note: In this case the "opposite" of `internal_snapshot` is not
+ # anything relating to external snapshots, but actually "memory
+ # state"(-only) snapshots.
+ if internal_snapshot
+ xml = internal_snapshot_xml(name)
+ @domain.snapshot_create_xml(xml)
+ else
+ snapshot_path = VM.ram_only_snapshot_path(name)
+ @domain.save(snapshot_path)
+ # For consistency with the internal snapshot case (which is
+ # "live", so the domain doesn't go down) we immediately restore
+ # the snapshot.
+ # Assumption: that *immediate* save + restore doesn't mess up
+ # with network state and similar, and is fast enough to not make
+ # the clock drift too much.
+ restore_snapshot(name)
+ end
+ end
+
+ def restore_snapshot(name)
+ @domain.destroy if is_running?
+ @display.stop if @display and @display.active?
+ # See comment in save_snapshot() for details on why we use two
+ # different type of snapshots.
+ potential_ram_only_snapshot_path = VM.ram_only_snapshot_path(name)
+ if File.exist?(potential_ram_only_snapshot_path)
+ Libvirt::Domain::restore(@virt, potential_ram_only_snapshot_path)
+ @domain = @virt.lookup_domain_by_name(@domain_name)
+ else
+ begin
+ potential_internal_snapshot = @domain.lookup_snapshot_by_name(name)
+ @domain.revert_to_snapshot(potential_internal_snapshot)
+ rescue Libvirt::RetrieveError
+ raise "No such (internal nor external) snapshot #{name}"
+ end
+ end
@display.start
end
+ def VM.remove_snapshot(name)
+ old_domain = $virt.lookup_domain_by_name(LIBVIRT_DOMAIN_NAME)
+ potential_ram_only_snapshot_path = VM.ram_only_snapshot_path(name)
+ if File.exist?(potential_ram_only_snapshot_path)
+ File.delete(potential_ram_only_snapshot_path)
+ else
+ snapshot = old_domain.lookup_snapshot_by_name(name)
+ snapshot.delete
+ end
+ end
+
+ def VM.snapshot_exists?(name)
+ return true if File.exist?(VM.ram_only_snapshot_path(name))
+ old_domain = $virt.lookup_domain_by_name(LIBVIRT_DOMAIN_NAME)
+ snapshot = old_domain.lookup_snapshot_by_name(name)
+ return snapshot != nil
+ rescue Libvirt::RetrieveError
+ return false
+ end
+
+ def VM.remove_all_snapshots
+ Dir.glob("#{$config["TMPDIR"]}/*-snapshot.memstate").each do |file|
+ File.delete(file)
+ end
+ old_domain = $virt.lookup_domain_by_name(LIBVIRT_DOMAIN_NAME)
+ old_domain.list_all_snapshots.each { |snapshot| snapshot.delete }
+ rescue Libvirt::RetrieveError
+ # No such domain, so no snapshots either.
+ end
+
def start
return if is_running?
@domain.create
@@ -394,9 +652,7 @@ EOF
end
def reset
- # ruby-libvirt 0.4 does not support the reset method.
- # XXX: Once we use Jessie, use @domain.reset instead.
- system("virsh -c qemu:///system reset " + @domain_name) if is_running?
+ @domain.reset if is_running?
end
def power_off
@@ -404,12 +660,6 @@ EOF
@display.stop
end
- def destroy
- clean_up_domain
- clean_up_net
- power_off
- end
-
def take_screenshot(description)
@display.take_screenshot(description)
end