[2/2] testbot/VMs: Interface with libvirt instead of VMware/VIX.

Francois Gouget fgouget at codeweavers.com
Fri Oct 19 05:14:31 CDT 2012


This should make WineTestBot independent from the underlying virtualization technology.
---

Compatibility Note: This requires a database schema update. It also 
requires re-registering all the VMs.


 testbot/ddl/update14.sql                       |    7 +
 testbot/ddl/winetestbot.sql                    |    4 +-
 testbot/doc/INSTALL.txt                        |    7 +-
 testbot/doc/winetestbot-schema.dia             |    8 +-
 testbot/lib/WineTestBot/Config.pm              |   19 +-
 testbot/lib/WineTestBot/ConfigLocalTemplate.pl |   27 +-
 testbot/lib/WineTestBot/Jobs.pm                |   64 ++-
 testbot/lib/WineTestBot/VMs.pm                 |  491 ++++++++++++------------
 8 files changed, 335 insertions(+), 292 deletions(-)
 create mode 100644 testbot/ddl/update14.sql

diff --git a/testbot/ddl/update14.sql b/testbot/ddl/update14.sql
new file mode 100644
index 0000000..9da8f58
--- /dev/null
+++ b/testbot/ddl/update14.sql
@@ -0,0 +1,7 @@
+ALTER TABLE VMs
+  DROP VmxHost,
+  DROP VmxFilePath,
+  ADD VirtURI VARCHAR(64) NOT NULL
+      AFTER Status,
+  ADD VirtDomain VARCHAR(32) NOT NULL
+      AFTER VirtURI;
diff --git a/testbot/ddl/winetestbot.sql b/testbot/ddl/winetestbot.sql
index d42298e..f588810 100644
--- a/testbot/ddl/winetestbot.sql
+++ b/testbot/ddl/winetestbot.sql
@@ -49,8 +49,8 @@ CREATE TABLE VMs
   SortOrder    INT(3)           NOT NULL,
   Bits         ENUM('32', '64') NOT NULL,
   Status       ENUM('dirty', 'reverting', 'sleeping', 'idle', 'running', 'offline') NOT NULL,
-  VmxHost      VARCHAR(64)      NULL,
-  VmxFilePath  VARCHAR(64)      NOT NULL,
+  VirtURI      VARCHAR(64)      NOT NULL,
+  VirtDomain   VARCHAR(32)      NOT NULL,
   IdleSnapshot VARCHAR(32)      NOT NULL,
   Hostname     VARCHAR(64)      NOT NULL,
   Interactive  ENUM('Y', 'N')   NOT NULL,
diff --git a/testbot/doc/INSTALL.txt b/testbot/doc/INSTALL.txt
index 6098a0c..76f9aca 100644
--- a/testbot/doc/INSTALL.txt
+++ b/testbot/doc/INSTALL.txt
@@ -6,7 +6,8 @@ Dependencies:
 - MySQL
 - Perl DBD and DBI::mysql modules
 - Sendmail
-- VMware Vix API (http://www.vmware.com/support/developer/vix-api)
+- Sys::Virt (libsys-virt-perl, see http://libvirt.org/)
+- Image::Magick (perlmagick)
 - IO::Socket::IP (libio-socket-ip-perl)
 
 MySQL setup:
@@ -122,7 +123,9 @@ Setup for wine-patches:
 -----------------
 
 Dependencies:
-- VMware
+- libvirtd (see http://libvirt.org/)
+- A virtualization technology supported by libvirt, for instance
+  QEmu/KVM, Xen, VirtualBox or VMware.
 
 
 3. Wine build VM setup
diff --git a/testbot/doc/winetestbot-schema.dia b/testbot/doc/winetestbot-schema.dia
index 41454d0..f5a03af 100644
--- a/testbot/doc/winetestbot-schema.dia
+++ b/testbot/doc/winetestbot-schema.dia
@@ -2253,7 +2253,7 @@
         </dia:composite>
         <dia:composite type="table_attribute">
           <dia:attribute name="name">
-            <dia:string>#VmxHost#</dia:string>
+            <dia:string>#VirtURI#</dia:string>
           </dia:attribute>
           <dia:attribute name="type">
             <dia:string>#VARCHAR(64)#</dia:string>
@@ -2265,7 +2265,7 @@
             <dia:boolean val="false"/>
           </dia:attribute>
           <dia:attribute name="nullable">
-            <dia:boolean val="true"/>
+            <dia:boolean val="false"/>
           </dia:attribute>
           <dia:attribute name="unique">
             <dia:boolean val="false"/>
@@ -2273,10 +2273,10 @@
         </dia:composite>
         <dia:composite type="table_attribute">
           <dia:attribute name="name">
-            <dia:string>#VmxFilePath#</dia:string>
+            <dia:string>#VirtDomain#</dia:string>
           </dia:attribute>
           <dia:attribute name="type">
-            <dia:string>#VARCHAR(64)#</dia:string>
+            <dia:string>#VARCHAR(32)#</dia:string>
           </dia:attribute>
           <dia:attribute name="comment">
             <dia:string>##</dia:string>
diff --git a/testbot/lib/WineTestBot/Config.pm b/testbot/lib/WineTestBot/Config.pm
index bcff569..789ddbe 100644
--- a/testbot/lib/WineTestBot/Config.pm
+++ b/testbot/lib/WineTestBot/Config.pm
@@ -25,11 +25,10 @@ WineTestBot::Config - Site-independent configuration settings
 =cut
 
 use vars qw (@ISA @EXPORT @EXPORT_OK $UseSSL $LogDir $DataDir $BinDir
-             $VixHostType $VixHostUsername $VixHostPassword
-             $VixGuestUsername $VixGuestPassword $DbDataSource $DbUsername
-             $DbPassword $MaxRevertingVMs $MaxRunningVMs $MaxExtraPoweredOnVms
-             $SleepAfterRevert $WaitForToolsInVM $AdminEMail $RobotEMail
-             $WinePatchToOverride $WinePatchCc $SuiteTimeout $SingleTimeout
+             $DbDataSource $DbUsername $DbPassword $MaxRevertingVMs
+             $MaxRunningVMs $MaxExtraPoweredOnVms $SleepAfterRevert
+             $WaitForToolsInVM $AdminEMail $RobotEMail $WinePatchToOverride
+             $WinePatchCc $SuiteTimeout $SingleTimeout
              $BuildTimeout $ReconfigTimeout $OverheadTimeout $TagPrefix
              $ProjectName $PatchesMailingList $PatchResultsEMail $LDAPServer
              $LDAPBindDN $LDAPSearchBase $LDAPSearchFilter
@@ -38,12 +37,10 @@ use vars qw (@ISA @EXPORT @EXPORT_OK $UseSSL $LogDir $DataDir $BinDir
 
 require Exporter;
 @ISA = qw(Exporter);
- at EXPORT = qw($UseSSL $LogDir $DataDir $BinDir $VixHostType
-             $VixHostUsername $VixHostPassword $VixGuestUsername
-             $VixGuestPassword $MaxRevertingVMs $MaxRunningVMs
-             $MaxExtraPoweredOnVms $SleepAfterRevert $WaitForToolsInVM
-             $AdminEMail $RobotEMail $WinePatchToOverride $WinePatchCc
-             $SuiteTimeout
+ at EXPORT = qw($UseSSL $LogDir $DataDir $BinDir
+             $MaxRevertingVMs $MaxRunningVMs $MaxExtraPoweredOnVms
+             $SleepAfterRevert $WaitForToolsInVM $AdminEMail $RobotEMail
+             $WinePatchToOverride $WinePatchCc $SuiteTimeout
              $SingleTimeout $BuildTimeout $ReconfigTimeout $OverheadTimeout
              $TagPrefix $ProjectName $PatchesMailingList $PatchResultsEMail
              $LDAPServer $LDAPBindDN $LDAPSearchBase $LDAPSearchFilter
diff --git a/testbot/lib/WineTestBot/ConfigLocalTemplate.pl b/testbot/lib/WineTestBot/ConfigLocalTemplate.pl
index ebc38d3..06afb77 100644
--- a/testbot/lib/WineTestBot/ConfigLocalTemplate.pl
+++ b/testbot/lib/WineTestBot/ConfigLocalTemplate.pl
@@ -1,6 +1,7 @@
 # WineTestBot configuration
 #
 # Copyright 2009 Ge van Geldorp
+# Copyright 2012 Francois Gouget
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -18,35 +19,9 @@
 
 use strict;
 
-use VMware::Vix::API::Constants;
-
 # Set to "1" if you have a valid SSL certificate, set to "!1" if you don't
 $WineTestBot::Config::UseSSL = 1;
 
-# With vCenter Server, ESX/ESXi hosts, and VMware Server 2.0, use
-# VIX_SERVICEPROVIDER_VMWARE_VI_SERVER. With VMware Workstation, use
-# VIX_SERVICEPROVIDER_VMWARE_WORKSTATION. With VMware Server 1.0.x, use
-# VIX_SERVICEPROVIDER_VMWARE_SERVER. 
-$WineTestBot::Config::VixHostType = undef;
-
-# Username for authentication on the host. With VMware Workstation and VMware
-# Server 1.0.x, use undef to authenticate as the current user on local host.
-# With vCenter Server, ESX/ESXi hosts, and VMware Server 2.0, you must use a
-# valid login. 
-$WineTestBot::Config::VixHostUsername = undef;
-
-# Password for authentication on the host. With VMware Workstation and VMware
-# Server 1.0.x, use undef to authenticate as the current user on local host.
-# With vCenter Server, ESX/ESXi hosts, and VMware Server 2.0, you must use a
-# valid login. 
-$WineTestBot::Config::VixHostPassword = undef;
-
-# The name of a user account on the guest operating system.
-$WineTestBot::Config::VixGuestUsername = undef;
-
-# The password of the account on the guest operating system.
-$WineTestBot::Config::VixGuestPassword = undef;
-
 # The DBI data source name of the database (e.g. "DBI:mysql:winetestbot")
 $WineTestBot::Config::DbDataSource = "DBI:mysql:winetestbot";
 
diff --git a/testbot/lib/WineTestBot/Jobs.pm b/testbot/lib/WineTestBot/Jobs.pm
index 52de881..8ec9bb8 100644
--- a/testbot/lib/WineTestBot/Jobs.pm
+++ b/testbot/lib/WineTestBot/Jobs.pm
@@ -377,13 +377,66 @@ sub CompareTaskStatus
   return $Compare;
 }
 
+=pod
+=over 12
+
+=item C<ScheduleOnHost()>
+
+This manages the VMs and WineTestBot::Task objects corresponding to the
+hypervisors of a given host. To stay within the host's resource limits the
+scheduler must take the following constraints into account:
+=over
+
+=item *
+
+Jobs should be run in decreasing order of priority.
+
+=item *
+
+A Job's Steps must be run in sequential order.
+
+=item *
+
+A Step's tasks can be run in parallel but only one task can be running in a VM
+at a given time. Also a VM must be prepared before it can run its task, see the
+VM Statuses.
+
+=item *
+
+The number of VMs running on the host must be kept under $MaxRunningVMs. The
+rational behind this limit is that the host may not be able to run more VMs
+simultaneously, typically due to memory or CPU constraints. Also note that
+this limit must be respected even if there are more than one hypervisor running
+on the host.
+
+=item *
+
+FIXME: The actual limit on the number of powered on VMs is blurred by the
+$MaxExtraPoweredOnVms setting and the last loop in ScheduleOnHost().
+
+=item *
+
+The number of VMs being reverted on the host at a given time must be kept under
+$MaxRevertingVMs. This may be set to 1 in case the hypervisor gets confused
+when reverting too many VMs at once.
+
+=item *
+
+No Task is started while there are VMs that are being reverted. This is so that
+the tests are not disrupted by the disk or CPU activity caused reverting a VM.
+
+=cut
+
+=back
+=cut
+
 sub ScheduleOnHost
 {
   my $self = shift;
-  my $Host = $_[0];
+  my $Hypervisors = $_[0];
 
   my $HostVMs = CreateVMs();
-  $HostVMs->FilterHost([$Host]);
+  $HostVMs->FilterHypervisor($Hypervisors);
   my ($RevertingVMs, $RunningVMs) = $HostVMs->CountRevertingRunningVMs();
   my $PoweredOnExtraVMs = $HostVMs->CountPoweredOnExtraVMs();
   my %DirtyVMsBlockingJobs;
@@ -507,12 +560,15 @@ sub Schedule
   my %Hosts;
   foreach my $VMKey (@{$VMs->GetKeys()})
   {
-    $Hosts{$VMs->GetItem($VMKey)->VmxHost} = 1;
+    my $VM = $VMs->GetItem($VMKey);
+    my $Host = $VM->GetHost();
+    $Hosts{$Host}->{$VM->VirtURI} = 1;
   }
   my $ErrMessage;
   foreach my $Host (keys %Hosts)
   {
-    my $HostErrMessage = $self->ScheduleOnHost($Host);
+    my @HostHypervisors = keys %{$Hosts{$Host}};
+    my $HostErrMessage = $self->ScheduleOnHost(\@HostHypervisors);
     if (! defined($ErrMessage))
     {
       $ErrMessage = $HostErrMessage;
diff --git a/testbot/lib/WineTestBot/VMs.pm b/testbot/lib/WineTestBot/VMs.pm
index 16f5ff0..5206b05 100644
--- a/testbot/lib/WineTestBot/VMs.pm
+++ b/testbot/lib/WineTestBot/VMs.pm
@@ -1,5 +1,3 @@
-# VM collection and items
-#
 # Copyright 2009 Ge van Geldorp
 # Copyright 2012 Francois Gouget
 #
@@ -19,16 +17,26 @@
 
 use strict;
 
+package WineTestBot::VM::Hypervisors;
+
 =head1 NAME
 
-WineTestBot::VMs - VM collection
+WineTestBot::VM::Hypervisors - A cache of hypervisor objects
 
-=cut
+=head1 DESCRIPTION
+
+A hypervisor is the software running on the host that handles the hardware
+virtualisation in support of the VMs. Thus each host has its own hypervisor,
+but some may have more than one, typically if more than one virtualisation
+software is used such as QEmu and VirtualBox.
+
+WineTestBot typically needs to deal with many VMs spread across a few hosts to
+spread the load and thus a few hypervisors. WineTestBot identifies the
+hypervisors via their VirtURI from which we get a Sys::Virt hypervisor.
+This class caches these  objects so only one is created per URI.
 
-package WineTestBot::VM::HostConnection;
+=cut
 
-use VMware::Vix::Simple;
-use VMware::Vix::API::Constants;
 use WineTestBot::Config;
 
 use vars qw (@ISA @EXPORT_OK);
@@ -38,53 +46,124 @@ require Exporter;
 
 @EXPORT_OK = qw(new);
 
-sub new
+sub new($)
 {
-  my $class = shift;
+  my ($class) = @_;
 
   my $self = {};
   $self = bless $self, $class;
   return $self;
 }
 
-sub GetHostHandle
-{
-  my $self = shift;
-  my $VixHost = $_[0];
+=pod
+=over 12
 
-  my $Key = $VixHost || "";
-  if (defined($self->{$Key}))
-  {
-    return (undef, $self->{$Key});
-  }
+=head1 C<GetHypervisor()>
 
-  my ($Err, $HostHandle) = HostConnect(VIX_API_VERSION,
-                                       $VixHostType, $VixHost, 0,
-                                       $VixHostUsername, $VixHostPassword,
-                                       0, VIX_INVALID_HANDLE);
-  if ($Err != VIX_OK)
-  {
-    return (GetErrorText($Err), VIX_INVALID_HANDLE);
-  }
-  $self->{$Key} = $HostHandle;
+Returns the Sys::Virt hypervisor object corresponding to the specified URI.
+This object is cached so only one hypervisor object is created per URI.
 
-  return (undef, $self->{$Key});
-}
+=back
+=cut
 
-sub DESTROY
+sub GetHypervisor($$)
 {
-  my $self = shift;
+  my ($self, $URI) = @_;
 
-  foreach my $HostHandle (values %{$self})
+  my $Key = $URI || "";
+  if (!defined $self->{$Key})
   {
-    HostDisconnect($HostHandle);
+      eval { $self->{$Key} = Sys::Virt->new(uri => $URI); };
+      return ($@->message(), undef) if ($@);
   }
+
+  return (undef, $self->{$Key});
 }
 
+
 package WineTestBot::VM;
 
-use VMware::Vix::Simple;
-use VMware::Vix::API::Constants;
+=head1 NAME
+
+WineTestBot::VM - A VM instance
+
+=head1 DESCRIPTION
+
+This provides methods for starting, stopping, getting the status of the VM,
+as well as manipulating its snapshots. These methods are implemented through
+Sys::Virt to provide portability across virtualization technologies.
+
+This class also provides methods to copy files to or from the VM and running
+commands in it. This part is used to start the tasks in the VM but is
+implemented independently from the VM's hypervisor since most do not provide
+this functionality.
+
+There are four types of VMs:
+
+=over 12
+
+=item base
+
+This defines the set of Windows VMs that the Wine tests are run on by default,
+especially Wine commits and wine-patches emails.
+
+=item extra
+
+This is a set of extra Windows VMs that users can manually chose to run their
+tests on.
+
+=item build
+
+This is a Unix VM used to build the Wine test binaries.
+
+=item retired
+
+These VMs are no longer used.
+
+=back
+
+
+A VM typically goes through the following states in this order:
+
+=over 12
+
+=item reverting
+
+The VM is currently being reverted to the idle snapshot. Note that the idle
+snapshot is supposed to be taken on a powered on VM so this also powers on the
+VM.
+
+=item sleeping
+
+The VM has been reverted to the idle snapshot and we are now letting it settle
+down for $SleepAfterRevert seconds (for instance so it gets time to renew its
+DHCP leases). It is not running a task yet.
+
+=item idle
+
+The VM powered on and is no longer in its sleeping phase. So it is ready to be
+given a task.
+
+=item running
+
+The VM is running some task.
+
+=item dirty
+
+The VM has completed the task it was given and thus has been powered off. The
+next step will be to revert it to the idle snapshot so it can be used again.
+
+=item offline
+
+This VM should not be used. This is typically the case of Retired VMs but it
+can also happen if an error happens while manipulating the VM.
+
+=back
+
+=cut
+
+use Sys::Virt;
+use Image::Magick;
 
 use ObjectModel::BackEnd;
 use WineTestBot::Config;
@@ -98,28 +177,16 @@ use vars qw (@ISA @EXPORT);
 require Exporter;
 @ISA = qw(WineTestBot::WineTestBotItem Exporter);
 
-sub _initialize
+sub _initialize($$)
 {
-  my $self = shift;
-  my $VMs = $_[0];
+  my ($self, $VMs) = @_;
 
-  $self->{HostConnection} = $VMs->{HostConnection};
-  $self->{VMHandle} = VIX_INVALID_HANDLE;
-  $self->{LoggedInToGuest} = undef;
+  $self->{Hypervisors} = $VMs->{Hypervisors};
+  $self->{Hypervisor} = undef;
+  $self->{Domain} = undef;
   $self->{OldStatus} = undef;
 }
 
-sub DESTROY
-{
-  my $self = shift;
-
-  if ($self->{VMHandle} != VIX_INVALID_HANDLE)
-  {
-    ReleaseHandle($self->{VMHandle});
-    $self->{VMHandle} = VIX_INVALID_HANDLE;
-  }
-}
-
 sub InitializeNew
 {
   my $self = shift;
@@ -130,218 +197,128 @@ sub InitializeNew
   $self->SUPER::InitializeNew(@_);
 }
 
-sub GetVMHandle
+sub GetHost($)
 {
-  my $self = shift;
-
-  if ($self->{VMHandle} != VIX_INVALID_HANDLE)
-  {
-    return (undef, $self->{VMHandle});
-  }
+  my ($self) = @_;
 
-  my $VmxHost = $self->VmxHost;
-  my ($ErrMessage, $HostHandle) = $self->{HostConnection}->GetHostHandle($VmxHost);
-  if (defined($ErrMessage))
-  {
-    return ($ErrMessage, VIX_INVALID_HANDLE);
-  }
-
-  my $Err = VIX_OK;
-  ($Err, $self->{VMHandle}) = VMOpen($HostHandle, $self->VmxFilePath);
-  if ($Err != VIX_OK)
-  {
-    $self->{VMHandle} = VIX_INVALID_HANDLE;
-    return (GetErrorText($Err), VIX_INVALID_HANDLE);
-  }
-
-  return (undef, $self->{VMHandle});
+  # The URI is of the form protocol://user@hostname/hypervisor-specific-data
+  return $1 if ($self->VirtURI =~ m%^[^:]+://(?:[^/@]*@)?([^/]+)/%);
+  return "localhost";
 }
 
-sub CheckError
+sub _GetDomain($)
 {
-  my $self = shift;
-  my $Err = $_[0];
+  my ($self) = @_;
 
-  if ($Err != VIX_OK)
+  if (!defined $self->{Domain})
   {
-    return GetErrorText($Err);
-  }
+    my ($ErrMessage, $Hypervisor) = $self->{Hypervisors}->GetHypervisor($self->VirtURI);
+    return ($ErrMessage,  undef) if (defined $ErrMessage);
 
-  return undef;
-}
-
-sub LoginInGuest
-{
-  my $self = shift;
-  my $VMHandle = $_[0];
-  my $Interactive = $_[1] ? "Y" : "N";
-
-  if (defined($self->{LoggedInToGuest}))
-  {
-    if ($self->{LoggedInToGuest} eq $Interactive)
-    {
-      return undef;
-    }
-    VMLogoutFromGuest($VMHandle);
-    delete $self->{LoggedInToGuest};
+    $self->{Hypervisor} = $Hypervisor;
+    eval { $self->{Domain} = $self->{Hypervisor}->get_domain_by_name($self->VirtDomain) };
+    return ($@->message(), undef) if ($@);
   }
-
-  my $Try = 0;
-  my $Err = -1;
-  while ($Err != VIX_OK && $Try < 5)
-  {
-    $Err = VMLoginInGuest($VMHandle, $VixGuestUsername, $VixGuestPassword,
-                          $Interactive eq "Y" ?
-                          VIX_LOGIN_IN_GUEST_REQUIRE_INTERACTIVE_ENVIRONMENT :
-                          0);
-    if ($Err != VIX_OK)
-    {
-      sleep(15);
-    }
-    $Try++;
-  }
-  if ($Err == VIX_OK)
-  {
-    $self->{LoggedInToGuest} = $Interactive;
-  }
-
-  return $self->CheckError($Err);
+  return (undef, $self->{Domain});
 }
 
-sub UpdateStatus
+sub UpdateStatus($$)
 {
-  my $self = shift;
-  my $VMHandle = $_[0];
+  my ($self, $Domain) = @_;
 
   if ($self->Status eq "offline")
   {
     return undef;
   }
 
-  my ($Err, $PowerState) = GetProperties($VMHandle,
-                                         VIX_PROPERTY_VM_POWER_STATE);
-  if ($Err != VIX_OK)
-  {
-    return GetErrorText($Err);
-  }
-  my $Status;
-  if ($PowerState == VIX_POWERSTATE_POWERED_OFF)
+  my ($State, $Reason) = $Domain->get_state();
+  return $@->message() if ($@);
+  if ($State == Sys::Virt::Domain::STATE_SHUTDOWN or
+      $State == Sys::Virt::Domain::STATE_SHUTOFF)
   {
-    $Status = "dirty";
-    $self->Status($Status);
+    $self->Status("dirty");
     $self->Save();
   }
 
   return undef;
 }
 
-sub RevertToSnapshot
+sub _GetSnapshot($$)
 {
-  my $self = shift;
-  my $SnapshotName = $_[0];
+  my ($self, $SnapshotName) = @_;
 
-  my ($ErrMessage, $VMHandle) = $self->GetVMHandle();
-  if (defined($ErrMessage))
-  {
-    return $ErrMessage;
-  }
+  my ($ErrMessage, $Domain) = $self->_GetDomain();
+  return $ErrMessage if (defined $ErrMessage);
 
-  my ($Err, $SnapshotHandle) = VMGetNamedSnapshot($VMHandle, $SnapshotName);
-  if ($Err != VIX_OK)
-  {
-    return GetErrorText($Err);
-  }
+  my $Snapshot;
+  eval { $Snapshot = $Domain->get_snapshot_by_name($SnapshotName) };
+  return ($@->message(), undef, undef) if ($@);
+  return (undef, $Domain, $Snapshot);
+}
 
-  $Err = VMRevertToSnapshot($VMHandle, $SnapshotHandle, VIX_VMPOWEROP_LAUNCH_GUI,
-                            VIX_INVALID_HANDLE);
-  ReleaseHandle($SnapshotHandle);
-  if ($Err != VIX_OK)
-  {
-    return GetErrorText($Err);
-  }
+sub RevertToSnapshot($$)
+{
+  my ($self, $SnapshotName) = @_;
+  LogMsg("Reverting ", $self->VirtDomain, " to $SnapshotName\n");
+
+  my ($ErrMessage, $Domain, $Snapshot) = $self->_GetSnapshot($SnapshotName);
+  return $ErrMessage if (defined $ErrMessage);
+  eval { $Snapshot->revert_to(Sys::Virt::DomainSnapshot::REVERT_RUNNING) };
+  return $@->message() if ($@);
 
-  return $self->UpdateStatus($VMHandle);
+  return $self->UpdateStatus($Domain);
 }
 
-sub CreateSnapshot
+sub CreateSnapshot($$)
 {
-  my $self = shift;
-  my $SnapshotName = $_[0];
+  my ($self, $SnapshotName) = @_;
 
-  my ($ErrMessage, $VMHandle) = $self->GetVMHandle();
-  if (defined($ErrMessage))
-  {
-    return $ErrMessage;
-  }
+  my ($ErrMessage, $Domain) = $self->_GetDomain();
+  return $ErrMessage if (defined $ErrMessage);
 
-  my ($Err, $SnapshotHandle) = VMCreateSnapshot($VMHandle, $SnapshotName, "",
-                                                VIX_SNAPSHOT_INCLUDE_MEMORY,
-                                                VIX_INVALID_HANDLE);
-  if ($Err != VIX_OK)
-  {
-    ReleaseHandle($SnapshotHandle);
-  }
-  return $self->CheckError($Err);
+  # FIXME: XML escaping
+  my $Xml = "<domainsnapshot><name>$SnapshotName</name></domainsnapshot>";
+  eval { $Domain->create_snapshot($Xml, 0) };
+  return $@->message() if ($@);
+  return undef;
 }
 
-sub RemoveSnapshot
+sub RemoveSnapshot($$)
 {
-  my $self = shift;
-  my $SnapshotName = $_[0];
+  my ($self, $SnapshotName) = @_;
 
-  my ($ErrMessage, $VMHandle) = $self->GetVMHandle();
-  if (defined($ErrMessage))
-  {
-    return $ErrMessage;
-  }
+  my ($ErrMessage, $Domain, $Snapshot) = $self->_GetSnapshot($SnapshotName);
+  return $ErrMessage if (defined $ErrMessage);
 
-  my ($Err, $SnapshotHandle) = VMGetNamedSnapshot($VMHandle, $SnapshotName);
-  if ($Err != VIX_OK)
-  {
-    return GetErrorText($Err);
-  }
-
-  $Err = VMRemoveSnapshot($VMHandle, $SnapshotHandle, 0);
-  ReleaseHandle($SnapshotHandle);
-  return $self->CheckError($Err);
+  eval { $Snapshot->delete(0) };
+  return $@->message() if ($@);
+  return undef;
 }
 
 sub PowerOn
 {
-  my $self = shift;
+  my ($self) = @_;
 
-  my ($ErrMessage, $VMHandle) = $self->GetVMHandle();
-  if (defined($ErrMessage))
-  {
-    return $ErrMessage;
-  }
+  my ($ErrMessage, $Domain) = $self->_GetDomain();
+  return $ErrMessage if (defined $ErrMessage);
 
-  my $Err = VMPowerOn($VMHandle, VIX_VMPOWEROP_NORMAL, VIX_INVALID_HANDLE);
-  if ($Err != VIX_OK)
-  {
-    return GetErrorText($Err);
-  }
+  eval { $Domain->create(0) };
+  return $@->message() if ($@);
 
-  return $self->UpdateStatus($VMHandle);
+  return $self->UpdateStatus($Domain);
 }
 
 sub PowerOff
 {
-  my $self = shift;
+  my ($self) = @_;
 
-  my ($ErrMessage, $VMHandle) = $self->GetVMHandle();
-  if (defined($ErrMessage))
-  {
-    return $ErrMessage;
-  }
+  my ($ErrMessage, $Domain) = $self->_GetDomain();
+  return $ErrMessage if (defined $ErrMessage);
 
-  my $Err = VMPowerOff($VMHandle, VIX_VMPOWEROP_NORMAL);
-  if ($Err != VIX_OK && $Err != VIX_E_VM_NOT_RUNNING)
-  {
-    return GetErrorText($Err);
-  }
+  eval { $Domain->destroy() };
+  return $@->message() if ($@);
 
-  return $self->UpdateStatus($VMHandle);
+  return $self->UpdateStatus($Domain);
 }
 
 sub WaitForToolsInGuest($;$)
@@ -373,31 +350,51 @@ sub RunScriptInGuestTimeout($$$)
   return TestAgent::RunScript($self->Hostname, $ScriptText, $Timeout);
 }
 
-sub CaptureScreenImage
-{
-  my $self = shift;
-
-  my ($ErrMessage, $VMHandle) = $self->GetVMHandle();
-  if (defined($ErrMessage))
-  {
-    return ($ErrMessage, undef, undef);
-  }
+my %StreamData;
 
-  $ErrMessage = $self->LoginInGuest($VMHandle, $self->Interactive);
-  if (defined($ErrMessage))
-  {
-    return ($ErrMessage, undef, undef);
-  }
-
-  my ($Err, $ImageSize, $ImageBytes) = VMCaptureScreenImage($VMHandle,
-                                                            VIX_CAPTURESCREENFORMAT_PNG,
-                                                            VIX_INVALID_HANDLE);
-  if ($Err != VIX_OK)
-  {
-    return (GetErrorText($Err), undef, undef);
-  }
+sub _Stream2Image($$$)
+{
+    my ($Stream, $Data, $Size) = @_;
+    my $Image=$StreamData{$Stream};
+    $Image->{Size} += $Size;
+    $Image->{Bytes} .= $Data;
+    return $Size;
+}
 
-  return (undef, $ImageSize, $ImageBytes);
+sub CaptureScreenImage($)
+{
+  my ($self) = @_;
+  LogMsg("CaptureScreenImage ", $self->Name, "\n");
+
+  my ($ErrMessage, $Domain) = $self->_GetDomain();
+  return ($ErrMessage, undef, undef) if (defined $ErrMessage);
+
+  my $Stream;
+  eval { $Stream = $self->{Hypervisor}->new_stream(0) };
+  return ($@->message(), undef, undef) if ($@);
+
+  my $Image={Size => 0, Bytes => ""};
+  $StreamData{$Stream}=$Image;
+  eval {
+    $Domain->screenshot($Stream, 0, 0);
+    $Stream->recv_all(\&WineTestBot::VM::_Stream2Image);
+    $Stream->finish();
+  };
+  delete $StreamData{$Stream};
+  return ($@->message(), undef, undef) if ($@);
+
+  # The screenshot format depends on the hypervisor (e.g. PPM for QEmu)
+  # but callers expect PNG images.
+  my $image=Image::Magick->new();
+  my ($width, $height, $size, $format) = $image->Ping(blob => $Image->{Bytes});
+  if ($format ne "PNG")
+  {
+    my @blobs=($Image->{Bytes});
+    $image->BlobToImage(@blobs);
+    $Image->{Bytes}=($image->ImageToBlob(magick => 'png'))[0];
+    $Image->{Size}=length($Image->{Bytes});
+  }
+  return (undef, $Image->{Size}, $Image->{Bytes});
 }
 
 sub Status
@@ -460,10 +457,20 @@ sub RunRevert
   return undef;
 }
 
+
 package WineTestBot::VMs;
 
-use VMware::Vix::Simple;
-use VMware::Vix::API::Constants;
+=head1 NAME
+
+WineTestBot::VMs - A VM collection
+
+=head1 DESCRIPTION
+
+This is the collection of VMs the testbot knows about, including the build VM
+and retired (no longer used) VMs.
+
+=cut
+
 use ObjectModel::BasicPropertyDescriptor;
 use ObjectModel::EnumPropertyDescriptor;
 use ObjectModel::PropertyDescriptor;
@@ -478,9 +485,7 @@ require Exporter;
 sub _initialize
 {
   my $self = shift;
-
-  $self->{HostConnection} = WineTestBot::VM::HostConnection->new();
-
+  $self->{Hypervisors} = WineTestBot::VM::Hypervisors->new();
   $self->SUPER::_initialize(@_);
 }
 
@@ -492,8 +497,8 @@ BEGIN
     CreateBasicPropertyDescriptor("SortOrder", "Display order", !1, 1, "N", 3),
     CreateEnumPropertyDescriptor("Bits", "32 or 64 bits", !1, 1, ['32', '64']),
     CreateEnumPropertyDescriptor("Status", "Current status", !1, 1, ['dirty', 'reverting', 'sleeping', 'idle', 'running', 'offline']),
-    CreateBasicPropertyDescriptor("VmxHost", "Host where VM is located", !1, !1, "A", 64),
-    CreateBasicPropertyDescriptor("VmxFilePath", "Path to .vmx file", !1, 1, "A", 64),
+    CreateBasicPropertyDescriptor("VirtURI", "LibVirt URI of the VM", !1, 1, "A", 64),
+    CreateBasicPropertyDescriptor("VirtDomain", "LibVirt Domain for the VM", !1, 1, "A", 32),
     CreateBasicPropertyDescriptor("IdleSnapshot", "Name of idle snapshot", !1, 1, "A", 32),
     CreateBasicPropertyDescriptor("Hostname", "The VM hostname", !1, 1, "A", 64),
     CreateBasicPropertyDescriptor("Interactive", "Needs interactive flag", !1, 1, "B", 1),
@@ -576,12 +581,12 @@ sub SortKeysBySortOrder
   return \@SortedKeys;
 }
 
-sub FilterHost
+sub FilterHypervisor
 {
   my $self = shift;
-  my $Host = $_[0];
+  my $Hypervisor = $_[0];
 
-  $self->AddFilter("VmxHost", $Host);
+  $self->AddFilter("VirtURI", $Hypervisor);
 }
 
 1;
-- 
1.7.10.4



More information about the wine-patches mailing list