managementnode/lib/VCL/Module/Provisioning/VMware/vmware_cmd.pm (438 lines of code) (raw):

#!/usr/bin/perl -w ############################################################################### # $Id$ ############################################################################### # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### =head1 NAME VCL::Module::Provisioning::VMware::vmware_cmd; =head1 SYNOPSIS my $vmhost_datastructure = $self->get_vmhost_datastructure(); my $vmware_cmd = VCL::Module::Provisioning::VMware::vmware_cmd->new({data_structure => $vmhost_datastructure}); my @registered_vms = $vmware_cmd->get_registered_vms(); =head1 DESCRIPTION This module provides support for VMs to be controlled using VMware Server 1.x's vmware-cmd command via SSH. =cut ############################################################################### package VCL::Module::Provisioning::VMware::vmware_cmd; # Specify the lib path using FindBin use FindBin; use lib "$FindBin::Bin/../../../.."; # Configure inheritance use base qw(VCL::Module::Provisioning::VMware::VMware); # Specify the version of this module our $VERSION = '2.5.1'; # Specify the version of Perl to use use 5.008000; use strict; use warnings; use diagnostics; use VCL::utils; ############################################################################### =head1 PRIVATE OBJECT METHODS =cut #////////////////////////////////////////////////////////////////////////////// =head2 initialize Parameters : none Returns : boolean Description : Initializes the vmware_cmd object by by checking if vmware-cmd is available on the VM host. =cut sub initialize { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $args = shift; # Check to make sure the VM host OS object is available if (!defined $args->{vmhost_os}) { notify($ERRORS{'WARNING'}, 0, "required 'vmhost_os' argument was not passed"); return; } elsif (ref $args->{vmhost_os} !~ /VCL::Module::OS/) { notify($ERRORS{'CRITICAL'}, 0, "'vmhost_os' argument passed is not a reference to a VCL::Module::OS object, type: " . ref($args->{vmhost_os})); return; } # Store a reference to the VM host OS object in this object $self->{vmhost_os} = $args->{vmhost_os}; # Check if vmware-cmd is available on the VM host my $command = 'vmware-cmd'; my ($exit_status, $output) = $self->vmhost_os->execute($command); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to determine if vmware-cmd is available on the VM host"); return; } elsif (grep(/not found/i, @$output)) { notify($ERRORS{'DEBUG'}, 0, "vmware-cmd is not available on the VM host, output:\n" . join("\n", @$output)); return; } else { notify($ERRORS{'DEBUG'}, 0, "vmware-cmd is available on VM host"); } notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized"); return 1; } #////////////////////////////////////////////////////////////////////////////// =head2 _run_vmware_cmd Parameters : $vmware_cmd_arguments Returns : array ($exit_status, $output) Description : Runs vmware-cmd on the VMware host. =cut sub _run_vmware_cmd { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $vmware_cmd_arguments = shift; if (!$vmware_cmd_arguments) { notify($ERRORS{'WARNING'}, 0, "vmware-cmd arguments were not specified"); return; } my $vmhost_computer_name = $self->vmhost_os->data->get_computer_short_name(); my $command = "vmware-cmd $vmware_cmd_arguments"; my ($exit_status, $output) = $self->vmhost_os->execute($command); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run vmware-cmd on VM host $vmhost_computer_name: '$command'"); } else { #notify($ERRORS{'DEBUG'}, 0, "executed vmware-cmd on VM host $vmhost_computer_name: '$command'"); return ($exit_status, $output); } } ############################################################################### =head1 API OBJECT METHODS =cut #////////////////////////////////////////////////////////////////////////////// =head2 get_registered_vms Parameters : none Returns : array Description : Returns an array containing the vmx file paths of the VMs running on the VM host. =cut sub get_registered_vms { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Run 'vmware-cmd -l' my $vmware_cmd_arguments = "-l"; my ($exit_status, $output) = $self->_run_vmware_cmd($vmware_cmd_arguments); return if !$output; my @vmx_file_paths = grep(/^\//, @$output); notify($ERRORS{'DEBUG'}, 0, "registered VMs found: " . scalar(@vmx_file_paths) . "\n" . join("\n", sort @vmx_file_paths)); return @vmx_file_paths; } #////////////////////////////////////////////////////////////////////////////// =head2 get_vm_power_state Parameters : $vmx_file_path Returns : string Description : Returns a string containing the power state of the VM indicated by the vmx file path argument. The string returned may be one of the following values: on off suspended =cut sub get_vm_power_state { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmx file path argument my $vmx_file_path = shift; if (!$vmx_file_path) { notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not supplied"); return; } my $vmx_file_name = $self->_get_file_name($vmx_file_path); # Run 'vmware-cmd <cfg> getstate' my $vmware_cmd_arguments = "\"$vmx_file_path\" getstate"; my ($exit_status, $output) = $self->_run_vmware_cmd($vmware_cmd_arguments); return if !$output; # The output should look like this: # getstate() = off my $vm_power_state; if (grep(/=\s*on/i, @$output)) { $vm_power_state = 'on'; } elsif (grep(/=\s*off/i, @$output)) { $vm_power_state = 'off'; } elsif (grep(/=\s*suspended/i, @$output)) { $vm_power_state = 'suspended'; } else { notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to determine power state of '$vmx_file_path', vmware-cmd arguments: '$vmware_cmd_arguments' output:\n" . join("\n", @$output)); return; } notify($ERRORS{'DEBUG'}, 0, "power state of VM '$vmx_file_name': $vm_power_state"); return $vm_power_state; } #////////////////////////////////////////////////////////////////////////////// =head2 vm_power_on Parameters : $vmx_file_path Returns : boolean Description : Powers on the VM indicated by the vmx file path argument. =cut sub vm_power_on { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmx file path argument my $vmx_file_path = shift; if (!$vmx_file_path) { notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not supplied"); return; } my $vmx_file_name = $self->_get_file_name($vmx_file_path); my $vmware_cmd_arguments = "\"$vmx_file_path\" start"; my ($exit_status, $output) = $self->_run_vmware_cmd($vmware_cmd_arguments); return if !$output; # Expected output if the VM was not previously powered on: # start() = 1 # Expected output if the VM was previously powered on: # VMControl error -8: Invalid operation for virtual machine's current state: # The requested operation ("start") could not be completed because it # conflicted with the state of the virtual machine ("on") at the time the # request was received. This error often occurs because the state of the # virtual machine changed before it received the request. # Expected output if the VM is not registered: /usr/bin/vmware-cmd: Could not # connect to VM /var/lib/vmware/Virtual Machines/Windows XP # Professional/Windows XP Professional.vmx # (VMControl error -11: No such virtual machine: The config file # /var/lib/vmware/Virtual Machines/Windows XP Professional/Windows XP # Professional.vmx is not registered. # Please register the config file on the server. For example: vmware-cmd -s # register "/var/lib/vmware/Virtual Machines/Windows XP Professional/Windows # XP Professional.vmx") if (grep(/\(\"on\"\)/i, @$output)) { notify($ERRORS{'OK'}, 0, "VM is already powered on: '$vmx_file_name'"); return 1; } elsif (grep(/=\s*1/i, @$output)) { notify($ERRORS{'OK'}, 0, "powered on VM: '$vmx_file_name'"); return 1; } elsif (grep(/error -11/i, @$output)) { notify($ERRORS{'WARNING'}, 0, "unable to power on VM because it is not registered: '$vmx_file_path'"); return; } else { notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to power on VM '$vmx_file_path', vmware-cmd arguments: '$vmware_cmd_arguments', output:\n" . join("\n", @$output)); return; } } #////////////////////////////////////////////////////////////////////////////// =head2 vm_power_off Parameters : $vmx_file_path Returns : boolean Description : Powers off the VM indicated by the vmx file path argument. =cut sub vm_power_off { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmx file path argument my $vmx_file_path = shift; if (!$vmx_file_path) { notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not supplied"); return; } my $vmx_file_name = $self->_get_file_name($vmx_file_path); my $vmware_cmd_arguments = "\"$vmx_file_path\" stop hard"; my ($exit_status, $output) = $self->_run_vmware_cmd($vmware_cmd_arguments); return if !$output; # Expected output if the VM was not previously powered on: # stop(hard) = 1 # Expected output if the VM was previously powered on: VMControl error -8: # Invalid operation for virtual machine's current state: The requested # operation ("stop") could not be completed because it conflicted with the # state of the virtual machine ("off") at the time the request was received. # This error often occurs because the state of the virtual machine changed # before it received the request. # Expected output if the VM is not registered: /usr/bin/vmware-cmd: Could not # connect to VM /var/lib/vmware/Virtual Machines/Windows XP # Professional/Windows XP Professional.vmx # (VMControl error -11: No such virtual machine: The config file # /var/lib/vmware/Virtual Machines/Windows XP Professional/Windows XP # Professional.vmx is not registered. # Please register the config file on the server. For example: vmware-cmd -s # register "/var/lib/vmware/Virtual Machines/Windows XP Professional/Windows # XP Professional.vmx") if (grep(/\(\"off\"\)/i, @$output)) { notify($ERRORS{'OK'}, 0, "VM is already powered off: '$vmx_file_name'"); return 1; } elsif (grep(/=\s*1/i, @$output)) { notify($ERRORS{'OK'}, 0, "powered off VM: '$vmx_file_name'"); return 1; } elsif (grep(/error -11/i, @$output)) { notify($ERRORS{'WARNING'}, 0, "unable to power off VM because it is not registered: '$vmx_file_path'"); return; } else { notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to power off VM '$vmx_file_path', vmware-cmd arguments: '$vmware_cmd_arguments', output:\n" . join("\n", @$output)); return; } } #////////////////////////////////////////////////////////////////////////////// =head2 vm_register Parameters : $vmx_file_path Returns : boolean Description : Registers the VM indicated by the vmx file path argument. =cut sub vm_register { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmx file path argument my $vmx_file_path = shift; if (!$vmx_file_path) { notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not supplied"); return; } my $vmx_file_name = $self->_get_file_name($vmx_file_path); my $vmware_cmd_arguments = "-s register \"$vmx_file_path\""; my ($exit_status, $output) = $self->_run_vmware_cmd($vmware_cmd_arguments); return if !$output; # Expected output if the VM is already registered: # VMControl error -20: Virtual machine already exists # Expected output if the VM is successfully registered: # register(<vmx path>) = 1 if (grep(/error -20/i, @$output)) { notify($ERRORS{'OK'}, 0, "VM is already registered: '$vmx_file_name'"); return 1; } elsif (grep(/=\s*1/i, @$output)) { notify($ERRORS{'OK'}, 0, "registered VM: '$vmx_file_name'"); return 1; } else { notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to register VM '$vmx_file_path', vmware-cmd arguments: '$vmware_cmd_arguments', output:\n" . join("\n", @$output)); return; } } #////////////////////////////////////////////////////////////////////////////// =head2 vm_unregister Parameters : $vmx_file_path Returns : boolean Description : Unregisters the VM indicated by the vmx file path argument. =cut sub vm_unregister { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmx file path argument my $vmx_file_path = shift; if (!$vmx_file_path) { notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not supplied"); return; } my $vmx_file_name = $self->_get_file_name($vmx_file_path); # Check if the VM is not registered if (!$self->is_vm_registered($vmx_file_path)) { notify($ERRORS{'OK'}, 0, "VM not unregistered because it is not registered: '$vmx_file_name'"); return 1; } # Power off the VM if it is on my $vm_power_state = $self->get_vm_power_state($vmx_file_path) || ''; if ($vm_power_state =~ /on/i && !$self->vm_power_off($vmx_file_path)) { notify($ERRORS{'WARNING'}, 0, "failed to power off VM before unregistering it: '$vmx_file_name', VM power state: $vm_power_state"); return; } my $vmware_cmd_arguments = "-s unregister \"$vmx_file_path\""; my ($exit_status, $output) = $self->_run_vmware_cmd($vmware_cmd_arguments); return if !$output; # Expected output if the VM is not registered: # VMControl error -11: No such virtual machine # Expected output if the VM is successfully unregistered: # unregister(<vmx path>) = 1 if (grep(/error -11/i, @$output)) { notify($ERRORS{'OK'}, 0, "VM is not registered: '$vmx_file_name'"); } elsif (grep(/=\s*1/i, @$output)) { notify($ERRORS{'OK'}, 0, "unregistered VM: '$vmx_file_name'"); } else { notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to unregister VM '$vmx_file_path', vmware-cmd arguments: '$vmware_cmd_arguments', output:\n" . join("\n", @$output)); return; } # Make sure the VM is not registered if ($self->is_vm_registered($vmx_file_path)) { notify($ERRORS{'WARNING'}, 0, "failed to unregister VM, it appears to still be registered: '$vmx_file_path'"); return; } return 1; } #////////////////////////////////////////////////////////////////////////////// =head2 get_virtual_disk_controller_type Parameters : $vmdk_file_path Returns : Description : Retrieves the disk controller type configured for the virtual disk specified by the vmdk file path argument. A string is returned containing one of the following values: -ide -buslogic -lsilogic =cut sub get_virtual_disk_controller_type { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmdk file path argument my $vmdk_file_path = shift; if (!$vmdk_file_path) { notify($ERRORS{'WARNING'}, 0, "vmdk file path argument was not supplied"); return; } my $vmdk_file_name = $self->_get_file_name($vmdk_file_path); my $vmhost_computer_name = $self->vmhost_os->data->get_computer_short_name(); my $command = "grep -i adapterType \"$vmdk_file_path\""; my ($exit_status, $output) = $self->vmhost_os->execute($command); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run command on VM host $vmhost_computer_name: '$command'"); } my ($adapter_type) = "@$output" =~ /adapterType\s*=\s*\"(\w+)\"/i; if ($adapter_type) { notify($ERRORS{'DEBUG'}, 0, "adapter type configured in '$vmdk_file_name': $adapter_type"); return $adapter_type; } else { notify($ERRORS{'WARNING'}, 0, "unable to determine adapter type configured in '$vmdk_file_name', command: '$command', output:\n" . join("\n", @$output)); return; } } #////////////////////////////////////////////////////////////////////////////// =head2 get_virtual_disk_type Parameters : $vmdk_file_path Returns : Description : Retrieves the disk type configured for the virtual disk specified by the vmdk file path argument. A string is returned containing one of the following values: -monolithicSparse -twoGbMaxExtentSparse -monolithicFlat -twoGbMaxExtentFlat =cut sub get_virtual_disk_type { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmdk file path argument my $vmdk_file_path = shift; if (!$vmdk_file_path) { notify($ERRORS{'WARNING'}, 0, "vmdk file path argument was not supplied"); return; } my $vmdk_file_name = $self->_get_file_name($vmdk_file_path); my $vmhost_computer_name = $self->vmhost_os->data->get_computer_short_name(); my $command = "grep -i createType \"$vmdk_file_path\""; my ($exit_status, $output) = $self->vmhost_os->execute($command); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run command on VM host $vmhost_computer_name: '$command'"); } my ($disk_type) = "@$output" =~ /createType\s*=\s*\"(\w+)\"/i; if ($disk_type) { notify($ERRORS{'DEBUG'}, 0, "disk type configured in '$vmdk_file_name': $disk_type"); return $disk_type; } else { notify($ERRORS{'WARNING'}, 0, "unable to determine disk type configured in '$vmdk_file_name', command: '$command', output:\n" . join("\n", @$output)); return; } } #////////////////////////////////////////////////////////////////////////////// =head2 get_virtual_disk_hardware_version Parameters : $vmdk_file_path Returns : integer Description : Retrieves the hardware version configured for the virtual disk specified by the vmdk file path argument. False is returned if the hardware version cannot be retrieved. =cut sub get_virtual_disk_hardware_version { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmdk file path argument my $vmdk_file_path = shift; if (!$vmdk_file_path) { notify($ERRORS{'WARNING'}, 0, "vmdk file path argument was not supplied"); return; } my $vmdk_file_name = $self->_get_file_name($vmdk_file_path); my $vmhost_computer_name = $self->vmhost_os->data->get_computer_short_name(); my $command = "grep -i virtualHWVersion \"$vmdk_file_path\""; my ($exit_status, $output) = $self->vmhost_os->execute($command); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run command on VM host $vmhost_computer_name: '$command'"); } my ($hardware_version) = "@$output" =~ /virtualHWVersion\s*=\s*\"(\w+)\"/i; if (defined($hardware_version)) { notify($ERRORS{'DEBUG'}, 0, "hardware version configured in '$vmdk_file_name': $hardware_version"); return $hardware_version; } else { notify($ERRORS{'WARNING'}, 0, "unable to determine hardware version configured in '$vmdk_file_name', command: '$command', output:\n" . join("\n", @$output)); return; } } #////////////////////////////////////////////////////////////////////////////// =head2 _get_datastore_info Parameters : none Returns : hash reference Description : Retrieves information about the VM host's datastore from the /etc/vmware/config file and returns a hash containing the information. =cut sub _get_datastore_info { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $vmhost_profile_datastore_path = $self->data->get_vmhost_profile_datastore_path(); my $vmhost_profile_vmpath = $self->data->get_vmhost_profile_vmpath(); $vmhost_profile_datastore_path = normalize_file_path($vmhost_profile_datastore_path); $vmhost_profile_vmpath = normalize_file_path($vmhost_profile_vmpath); # Get the contents of the VMware config file my $config_file_path = '/etc/vmware/config'; my $config_datastore_name; my $config_datastore_path; my @config_contents = $self->vmhost_os->get_file_contents($config_file_path); if (@config_contents) { notify($ERRORS{'DEBUG'}, 0, "retrieved contents of $config_file_path\n" . join("\n", @config_contents)); # Get the datastore name and path from the file contents ($config_datastore_name) = map { $_ =~ /datastore\.name\s*=\s*"([^"]+)"/ } @config_contents; if (!$config_datastore_name) { notify($ERRORS{'WARNING'}, 0, "failed to locate the 'datastore.name' line in $config_file_path"); } ($config_datastore_path) = map { $_ =~ /datastore\.localpath\s*=\s*"([^"]+)"/ } @config_contents; if ($config_datastore_path) { $config_datastore_path = normalize_file_path($config_datastore_path); } else { notify($ERRORS{'WARNING'}, 0, "failed to locate the 'datastore.localpath' line in $config_file_path"); } } else { notify($ERRORS{'WARNING'}, 0, "failed to retrieve the contents of $config_file_path"); } # Create a hash containing datastore names and their paths my %datastore_info; # Keep track of paths added to returning %datastore_info hash so the same path isn't added more than once my %datastore_paths; # Add the datastore found in the config file if (defined($config_datastore_name) && defined($config_datastore_path)) { $datastore_info{$config_datastore_name}{normal_path} = $config_datastore_path; $datastore_paths{$config_datastore_path} = 1; }; # Add datastores for the VM host profile vmpath and datastore if they are different than what's in the config file if (!defined($datastore_paths{$vmhost_profile_vmpath})) { $datastore_info{'vmprofile-vmpath'}{normal_path} = $vmhost_profile_vmpath; $datastore_paths{$vmhost_profile_vmpath} = 1; } if (!defined($datastore_paths{$vmhost_profile_datastore_path})) { $datastore_info{'vmprofile-datastore'}{normal_path} = $vmhost_profile_datastore_path; $datastore_paths{$vmhost_profile_vmpath} = 1; } # Construct a hash containing for my $datastore_name (keys %datastore_info) { my $datastore_path = $datastore_info{$datastore_name}{normal_path}; $datastore_info{$datastore_name}{accessible} = 'true'; $datastore_info{$datastore_name}{type} = 'local'; $datastore_info{$datastore_name}{url} = $datastore_path; my $available_space = $self->vmhost_os->get_available_space($datastore_path); if (defined($available_space)) { $datastore_info{$datastore_name}{freeSpace} = $available_space; } else { notify($ERRORS{'WARNING'}, 0, "failed to determine the amount of space available in datastore '$datastore_name' ($datastore_path)"); } my $total_space = $self->vmhost_os->get_total_space($datastore_path); if (defined($total_space)) { $datastore_info{$datastore_name}{capacity} = $total_space; } else { notify($ERRORS{'WARNING'}, 0, "failed to determine the total amount of space of the volume where datastore '$datastore_name' ($datastore_path) resides"); } } notify($ERRORS{'DEBUG'}, 0, "retrieved datastore info:\n" . format_data(\%datastore_info)); return \%datastore_info; } #////////////////////////////////////////////////////////////////////////////// =head2 create_snapshot Parameters : $vmx_file_path Returns : boolean Description : Creates a snapshot of the VM. =cut sub create_snapshot { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the vmx file path argument my $vmx_file_path = shift; if (!$vmx_file_path) { notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not supplied"); return; } my $command = "vmrun snapshot \"$vmx_file_path\""; my ($exit_status, $output) = $self->vmhost_os->execute($command, 1); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to execute vmrun to create a snapshot of VM: $vmx_file_path, command: '$command'"); return; } elsif ($exit_status != 0 || grep(/error/i, @$output)) { notify($ERRORS{'WARNING'}, 0, "error occurred executing vmrun to create a snapshot of VM: $vmx_file_path, command: '$command', output:\n" . join("\n", @$output)); return; } else { notify($ERRORS{'OK'}, 0, "created snapshot of VM: $vmx_file_path, output:\n" . join("\n", @$output)); return 1; } } #////////////////////////////////////////////////////////////////////////////// 1; __END__ =head1 SEE ALSO L<http://cwiki.apache.org/VCL/> =cut