The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

HoneyClient::Manager::VM - Perl extension to instantiate a SOAP server that provides programmmatic access to all VM clients within the locally running VMware Server / GSX server.

VERSION

This documentation refers to HoneyClient::Manager:VM version 0.99.

SYNOPSIS

CREATING THE SOAP SERVER

  use HoneyClient::Manager::VM;

  # Handle SOAP requests on the default address:port.
  my $URL = HoneyClient::Manager::VM->init();

  # Handle SOAP requests on TCP port localhost:9090
  my $URL = HoneyClient::Manager::VM->init(address => "localhost", 
                                           port    => 9090);

  print "Server URL: " . $URL . "\n";

  # Create a cleanup function, to execute whenever
  # the SOAP server needs to be destroyed.
  sub cleanup {
      HoneyClient::Manager::VM->destroy();
      exit;
  }

  # Install the cleanup handler, in case parent process
  # dies unexpectedly.
  $SIG{HUP}       = \&cleanup;
  $SIG{INT}       = \&cleanup;
  $SIG{QUIT}      = \&cleanup;
  $SIG{ABRT}      = \&cleanup;
  $SIG{PIPE}      = \&cleanup;
  $SIG{TERM}      = \&cleanup;

  # Catch all parent code errors, in order to perform cleanup
  # on all child processes before exiting.
  eval {
      # Do rest of the parent processing here...
  };

  # We assume you still want to still want to "die" on
  # any errors found within the eval block.
  if ($@) {
      HoneyClient::Manager::VM->destroy();
      die $@; 
  }

  # Even if no errors occurred, initiate cleanup.
  cleanup();

INTERACTING WITH THE SOAP SERVER

  use HoneyClient::Util::SOAP qw(getClientHandle);

  # Create a new SOAP client, to talk to the HoneyClient::Manager::VM
  # module.
  my $stub = getClientHandle(namespace => "HoneyClient::Manager::VM");
  my $som;

  # Enumerate all registered VMs.
  $som = $stub->enumerate();
  my @list = $som->paramsall;
  print "\t$_\n" foreach (@list);
  print "\n";

  # Assume we have a particular VM.
  my $vmConfig = "/path/to/vm.vmx";

  # See if a particular VM is registered.
  $som = $stub->isRegisteredVM(config => $vmConfig);
  if ($som->result) {
      print "Yes, the VM is registered.";
  } else {
      print "No, the VM is not registered.";
  }

  # Register a particular VM.
  $som = $stub->registerVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }

  # Unregister a particular VM.
  $som = $stub->unregisterVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }

  # Get the state of a particular VM.
  use VMware::VmPerl qw(VM_EXECUTION_STATE_ON
                        VM_EXECUTION_STATE_OFF
                        VM_EXECUTION_STATE_STUCK
                        VM_EXECUTION_STATE_SUSPENDED);
  $som = $stub->getStateVM(config => $vmConfig);
  if ($som->result == VM_EXECUTION_STATE_ON) {
      print "ON\n";
  } elsif ($som->result == VM_EXECUTION_STATE_OFF) {
      print "OFF\n";
  } elsif ($som->result == VM_EXECUTION_STATE_SUSPENDED) {
      print "SUSPENDED\n";
  } elsif ($som->result == VM_EXECUTION_STATE_STUCK) {
      print "STUCK\n";
  } else {
      print "UNKNOWN\n";
  }

  # Start a particular VM.
  $som = $stub->startVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }
  
  # Stop a particular VM.
  $som = $stub->stopVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }
  
  # Reboot a particular VM.
  $som = $stub->rebootVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }
  
  # Suspend a particular VM.
  $som = $stub->suspendVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }

  # After starting a particular VM, if the VM's
  # state is STUCK, we can try automatically answering
  # any pending questions that the VMware Server / GSX
  # daemon is waiting for.
  #
  # Note: In most cases, this call doesn't need to
  # be made, since startVM() will try this call
  # automatically, if needed.
  $som = $stub->answerVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }

  # Create a new full clone from a particular VM 
  # and put the clone in the "/vm/TEST" directory.
  my $destDir = "/vm/TEST";
  $som = $stub->fullCloneVM(src_config => $vmConfig, dest_dir => $destDir);
  my $cloneConfig = $som->result;
  if ($som->result) {
      print "Successfully created clone VM at ($cloneConfig)!\n";
  } else {
      print "Failed to create clone!\n";
  }
  
  # Create a new quick clone from a particular VM
  # and put the clone in the "/vm/TEST" directory.
  my $destDir = "/vm/TEST";
  $som = $stub->quickCloneVM(src_config => $vmConfig, dest_dir => $destDir);
  my $cloneConfig = $som->result;
  if ($som->result) {
      print "Successfully created clone VM at ($cloneConfig)!\n";
  } else {
      print "Failed to create clone!\n";
  }
  
  # Set a particular VM to be a master image,
  # allowing us to call quickCloneVM() without
  # any arguments.
  $som = $stub->setMasterVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }

  # Get the name of a particular VM.
  $som = $stub->getNameVM(config => $vmConfig);
  my $dispName = $som->result;
  if ($som->result) {
      print "VM Name: \"$dispName\"\n";
  } else {
      print "Failed to get VM name!\n";
  }

  # Set the name of a particular VM to "BLAH".
  $som = $stub->setNameVM(config => $vmConfig, name => "BLAH");
  my $dispName = $som->result;
  if ($som->result) {
      print "VM Renamed To: \"$dispName\"\n";
  } else {
      print "Failed to rename VM!\n";
  }

  # Get the MAC address of a particular VM's first NIC.
  $som = $stub->getMACaddrVM(config => $vmConfig);
  my $macAddress = $som->result;
  if ($som->result) {
      print "VM MAC Address: \"$macAddress\"\n";
  } else {
      print "Failed to get VM MAC address!\n";
  }
  
  # Get the IP address of a particular VM's first NIC.
  $som = $stub->getIPaddrVM(config => $vmConfig);
  my $ipAddress = $som->result;
  if ($som->result) {
      print "VM IP Address: \"$ipAddress\"\n";
  } else {
      print "Failed to get VM IP address!\n";
  }

  # Destroy a particular VM.
  $som = $stub->destroyVM(config => $vmConfig);
  if ($som->result) {
      print "Success!\n";
  } else {
      print "Failed!\n";
  }

  # Save a snapshot of a particular VM, saving the
  # snapshot to "/path/to/snapshot.tar.gz".
  $som = $stub->snapshotVM(config => $vmConfig, snapshot_file => "/path/to/snapshot.tar.gz");
  my $destSnapshot = $som->result;
  if ($som->result) {
      print "Successfully snapshotted VM at ($destSnapshot)!\n";
  } else {
      print "Failed to snapshot VM!\n";
  }

  # Revert a particular VM back to a previous snapshot,
  # where the snapshot file is located at
  # "/path/to/snapshot.tar.gz".
  $som = $stub->revertVM(config => $vmConfig, snapshot_file => "/path/to/snapshot.tar.gz");
  my $revertConfig = $som->result;
  if ($som->result) {
      print "Successfully reverted VM at ($revertConfig)!\n";
  } else {
      print "Failed to revert VM!\n";
  }

DESCRIPTION

Once created, the daemon acts as a stand-alone SOAP server, processing individual requests and manipulating VMs on the locally running VMware Server / GSX server.

LOCAL FUNCTIONS

The following init() and destroy() functions are the only direct calls required to startup and shutdown the SOAP server.

All other interactions with this daemon should be performed as SOAP::Lite function calls, in order to ensure consistency across client sessions. See the "EXTERNAL SOAP FUNCTIONS" section, for more details.

HoneyClient::Manager::VM->init(address => $localAddr, port => $localPort)

    Starts a new SOAP server, within a child process.

    Inputs: $localAddr is an optional argument, specifying the IP address for the SOAP server to listen on. $localPort is an optional argument, specifying the TCP port for the SOAP server to listen on.

    Output: The full URL of the web service provided by the SOAP server.

HoneyClient::Manager::VM->destroy()

    Terminates the SOAP server within the child process.

    Output: True if successful, false otherwise.

EXTERNAL SOAP FUNCTIONS

isRegisteredVM(config => $config)

    Indicates if a specified VM is already registered.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if already registered, false otherwise.

enumerate()

    Returns an enumeration of all registered VMs.

    Output: An array containing the configuration files for each registered VM.

getStateVM(config => $config)

    Gets the powered state of a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: One of the following VMware::VmPerl constants: VM_EXECUTION_STATE_ON VM_EXECUTION_STATE_OFF VM_EXECUTION_STATE_SUSPENDED VM_EXECUTION_STATE_STUCK VM_EXECUTION_STATE_UNKNOWN

startVM(config => $config)

    Powers on a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if power on was successful, false otherwise.

stopVM(config => $config)

    Powers off a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if power off was successful, false otherwise.

rebootVM(config => $config)

    Reboots a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if reboot was successful, false otherwise.

suspendVM(config => $config)

    Suspends a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if suspend was successful, false otherwise.

fullCloneVM(src_config => $srcConfig, dest_dir => $destDir)

    Completely clones a specified VM.

    Inputs: $srcConfig is the full, absolute path to the source VM's configuration file, as it sits on the host VMware Server / GSX server's disk. $destDir is an optional argument, containing the absolute path where the cloned VM contents will reside.

    Output: Absolute path of the cloned VM's configuration file, if successful.

    Notes: If $destDir is not specified, then the cloned VM will reside in a subdirectory within the main directory specified by the global $DATASTORE_PATH variable.

    The format of this automatically generated subdirectory will be a randomly generated hexadecimal string of the length $VM_ID_LENGTH.

    Cloning VMs can be a time consuming operation, depending on how big the VM is. This is because the entire VM data is cloned, including all hard disks. As such, the web service call completes while these filesystem-intensive operations are performed in the background within a child thread.

    Once cloned, the new VM will be automatically started, in order to update the VM's unique UUID and the VM's network MAC address.

    Thus, it is recommended that once a fullCloneVM() operation is performed, you call getStateVM() on the cloned VM's configuration file to make sure the VM is powered on, prior to performing any additional operations on the cloned VM.

getNameVM(config => $config)

    Gets the display name of a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: The display name of the VM.

setNameVM(config => $config, name => $name)

    Sets the display name of a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk. $name is the new display name to assign the VM.

    Output: The new display name of the VM, if successful.

getMACaddrVM(config => $config)

    Gets the MAC address of a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: The MAC address of the VM, if successful.

    Notes: This function will only return the MAC address of the VM's first ethernet interface.

    By default upon powering on a VM, VMware Server / GSX will generate a new MAC address for any VM, if the VM's on-disk location has changed.

    Thus, if you manually migrate a VM to a new location on disk and proceed to call this function prior to powering on the VM, then you'll get a bogus MAC address that will immediately change once the VM is powered on.

    This is precisely why the provided *CloneVM() functions power on the cloned VM immediately after cloning -- in order to initialize a new UUID and MAC address for the cloned VM.

getIPaddrVM(config => $config, mac_address => $macAddress)

    Gets the IP address of a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk. $macAddress is the MAC address of the specified VM.

    Output: The IP address of the VM, if successful.

    Notes: This function will only return the IP address of the VM's first ethernet interface.

    This function can return a result if either a $config or a $macAddress is specified. However, at least one parameter must be present.

registerVM(config => $config)

    Registers a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if successful, false otherwise.

unregisterVM(config => $config)

    Unregisters a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if successful, false otherwise.

answerVM(config => $config)

    Automatically answer any normal, pending questions for a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if successful, false otherwise.

    Notes: This function attempts to answer (sanely) most of the normal questions that a VMware Server / GSX server usually asks when powering on cloned or faulty VMs.

destroyVM(config => $config)

    Destroys a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk.

    Output: True if successful, false otherwise.

setMasterVM(config => $config, dont_register => $dontUnregister)

    Prepares a specified VM as a "Master VM image", to be used for creating multiple subsequent "quick clone VMs".

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk. $dontUnregister is an optional argument, indicating if the Master VM should be unregistered once set. By default, the Master VM is unregistered (to prevent the master VMs contents from changing). If this argument is defined, then the Master VM will remain registered.

    Output: True if successful, false otherwise.

    Notes: Once a Master VM is prepared, clone VMs can be quickly created, whose unique data is recorded as journalled differences against the Master VM's image.

    By default, the Master VM will automatically be unregistered, since its hard disks must never change while quick clone VMs are actively using them.

quickCloneVM(src_config => $config, dest_dir => $destDir)

    Creates a differential clone, using a specified master VM as a basis.

    Inputs: $config is an optional argument, specifying the full, absolute path to the source Master VM's configuration file, as it sits on the host VMware Server / GSX server's disk. $destDir is an optional argument, containing the absolute path where the quick clone VM's contents will reside.

    Output: Absolute path of the cloned VM's configuration file, if successful.

    Notes: If $config is not specified, then this function will attempt to use the last Master VM configuration that was specified via the setMasterVM() function. Otherwise, if a Master VM configuration was passed to this function, then it will become the global Master VM and this function will create a corresponding quick clone.

    If $config is not specified and no Master VM was set via a previous setMasterVM() call, then this function will fail.

    If $destDir is not specified, then the cloned VM will reside in a subdirectory within the main directory specified by the global $DATASTORE_PATH variable.

    The format of this automatically generated subdirectory will be a randomly generated hexadecimal string of the length $VM_ID_LENGTH.

    Quick cloning VMs can be a time consuming operation, depending on how big the VM is. This is because the most VM data is copied, including all *REDO data. As such, the web service call completes while these filesystem-intensive operations are performed in the background within a child thread.

    Once cloned, the new VM will be automatically started, in order to update the VM's unique UUID and the VM's network MAC address.

    Thus, it is recommended that once a quickCloneVM() operation is performed, you call getStateVM() on the cloned VM's configuration file to make sure the VM is powered on, prior to performing any additional operations on the cloned VM.

snapshotVM(config => $config, snapshot_file => $snapshotFile)

    Creates a snapshot of a specified VM.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk. $snapshotFile is an optional argument, indicating the full, absolute path and filename of where the snapshot file should be stored.

    Output: Absolute path to the snapshot file, if successful.

    Notes: If $snapshotFile is not specified, all snapshots will be stored within the directory specified by the global variable $SNAPSHOT_PATH, by default.

    The format of this destination directory is: "$SNAPSHOT_PATH/$VMDIRNAME-YYYYMMDDThhmmss.tar.gz", using ISO8601 date format variables.

    Once executed, the function will attempt to: - Suspend the VM, if it's running. - Package and compress all data within the VM's subdirectory, where the archive resides at the snapshot location. - Start the VM back up, if it was on previously.

revertVM(config => $config, snapshot_file => $snapshotFile)

    Reverts a specified VM back to a previous snapshot.

    Inputs: $config is the full, absolute path to the VM's configuration file, as it sits on the host VMware Server / GSX server's disk. $snapshotFile is an optional argument, indicating the full, absolute path and filename of where the snapshot file is stored.

    Output: Absolute path to the VM's configuration file, if successful.

    Notes: If $snapshotFile is not specified, it will be assumed that the VM should be re-quickCloned from the Master VM. If the specified VM is not a quick clone, then this operation will fail.

    Once executed, the function will attempt to: - Stop the VM, if it's running. - Destroy the VM contents. - Extract snapshot or re-quickClone from Master VM. - Start the VM back up, if it was on previously.

FAULT REPORTING & IMPLEMENTATION DETAILS

For any filesystem intensive operation, the daemon spawns a child thread to perform the actual I/O operations, allowing the web service call to finish before any timeouts occur.

As a result, if any errors occur within the child threads, the corresponding SOAP::Fault object(s) created will be queued and transmitted back to the next client who makes the next SOAP request -- one fault dequeued and transmitted back per subsequent SOAP request.

The following functions spawn asynchronous child threads:

  • quickCloneVM()

  • fullCloneVM()

  • snapshotVM()

  • revertVM()

BUGS & ASSUMPTIONS

This daemon assumes the VMware Server / GSX server to control is running locally. Furthermore, do NOT run this daemon as root. The daemon will use whatever user/group permissions it was run under, in order to automatically authenticate with the VMware Server / GSX server.

If this daemon is executed on a system to control a VMware Server / GSX server remotely, then any filesystem-specific operations (i.e., cloning) will fail, as those operations cannot be performed on remote VMware Server / GSX servers without direct access to the server's filesystem.

This code relies heavily upon the VMware::VmPerl APIs. The VmPerl APIs are not thread-safe. As such, all VmPerl operations are centralized in the master thread. If a child thread ever needs to perform VmPerl-specific functions, then the child must perform a local callback to the SOAP server, instead of calling VmPerl directly.

SEE ALSO

http://www.honeyclient.org/trac

SOAP::Lite

http://www.soaplite.com

VMware::VmPerl, VMware::VmPerl::Server, VMware::VmPerl::ConnectParams, VMware::VmPerl::VM, VMware::VmPerl::Question

http://www.vmware.com/support/developer/

threads, threads::shared, Thread::Queue, Thread::Semaphore, perlthrtut

POSIX, File::Copy, File::Copy::Recursive, File::Basename, Tie::File

Apache::SessionX::General::MD5

REPORTING BUGS

http://www.honeyclient.org/trac/newticket

ACKNOWLEDGEMENTS

VMware, for providing their VMware::VmPerl API code and offering their VMware Server product as freeware.

Jeffrey William Baker <jwbaker@acm.org> and Gerald Richter <richter@dev.ecos.de>, for using core code from their Apache::Session::Generate::MD5 package to create unique VMIDs.

AUTHORS

Darien Kindlund, <kindlund@mitre.org>

COPYRIGHT & LICENSE

Copyright (C) 2007 The MITRE Corporation. All rights reserved.

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, using version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.