You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2017/06/12 16:33:35 UTC
svn commit: r1798487 [4/7] - in /vcl/trunk/managementnode/lib/VCL/Module:
OS/ OS/Linux/ OS/Linux/firewall/ OS/Windows/ OS/Windows/Version_5/
OS/Windows/Version_6/ Predictive/ Provisioning/ Provisioning/VMware/
Modified: vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm?rev=1798487&r1=1798486&r2=1798487&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm Mon Jun 12 16:33:35 2017
@@ -1,4007 +1,4007 @@
-#!/usr/bin/perl -w
-###############################################################################
-# $Id: VIM_SSH.pm 952366 2010-06-07 18:59:25Z arkurth $
-###############################################################################
-# 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::VIM_SSH;
-
-=head1 SYNOPSIS
-
- my $vmhost_datastructure = $self->get_vmhost_datastructure();
- my $VIM_SSH = VCL::Module::Provisioning::VMware::VIM_SSH->new({data_structure => $vmhost_datastructure});
- my @registered_vms = $VIM_SSH->get_registered_vms();
-
-=head1 DESCRIPTION
-
- This module provides support for the vSphere SDK. The vSphere SDK can be used
- to manage VMware Server 2.x, ESX 3.0.x, ESX/ESXi 3.5, ESX/ESXi 4.0, vCenter
- Server 2.5, and vCenter Server 4.0.
-
-=cut
-
-###############################################################################
-package VCL::Module::Provisioning::VMware::VIM_SSH;
-
-# 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.4.2';
-
-# Specify the version of Perl to use
-use 5.008000;
-
-use strict;
-use warnings;
-use diagnostics;
-
-use English '-no_match_vars';
-use VCL::utils;
-
-###############################################################################
-
-=head1 PRIVATE OBJECT METHODS
-
-=cut
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 initialize
-
- Parameters : none
- Returns : boolean
- Description : Initializes the vSphere SDK object by establishing a connection
- to 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;
-
- if (!defined($self->{vmhost_os})) {
- #
- if (!defined $args->{vmhost_os}) {
- notify($ERRORS{'WARNING'}, 0, "required 'vmhost_os' argument was not passed");
- return;
- }
-
- #
- if (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;
- }
-
- #
- $self->{vmhost_os} = $args->{vmhost_os};
- }
-
- if (!$self->vmhost_os) {
- return;
- }
-
- my @required_vmhost_os_subroutines = (
- 'execute',
- );
-
- for my $required_vmhost_os_subroutine (@required_vmhost_os_subroutines) {
- if (!$self->vmhost_os->can($required_vmhost_os_subroutine)) {
- notify($ERRORS{'WARNING'}, 0, "required VM host OS subroutine is not implemented: $required_vmhost_os_subroutine");
- return;
- }
- }
-
- # Determine which VIM executable is installed on the VM host
- my $command = 'vim-cmd ; vmware-vim-cmd';
- my ($exit_status, $output) = $self->vmhost_os->execute($command);
- if (!defined($output)) {
- notify($ERRORS{'OK'}, 0, "VIM executable is not available on the VM host");
- return;
- }
- elsif (!grep(/vmsvc/, @$output)) {
- # String 'vmsvc' does not exist in the output, neither of the commands worked
- notify($ERRORS{'DEBUG'}, 0, "VIM executable is not available on the VM host, output:\n" . join("\n", @$output));
- return;
- }
- elsif (grep(/: vim-cmd:.*not found/i, @$output)) {
- # Output contains the line: 'vim-cmd: command not found'
- $self->{vim_cmd} = 'vmware-vim-cmd';
- }
- else {
- # Output contains the line: 'vmware-vim-cmd: command not found'
- # Note: VMware ESX 4.1 has BOTH vim-cmd and vmware-vim-cmd
- $self->{vim_cmd} = 'vim-cmd';
- }
- notify($ERRORS{'DEBUG'}, 0, "VIM executable available on VM host: $self->{vim_cmd}");
-
- notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized");
- return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _run_vim_cmd
-
- Parameters : $vim_arguments, $timeout_seconds (optional), $attempt_limit (optional)
- Returns : array ($exit_status, $output)
- Description : Runs vim-cmd command on the VMware host. This was designed to
- allow it to handle most of the error checking.
-
- By default, 5 attempts are made.
-
- If the exit status of the vim-cmd command is 0 after any attempt,
- $exit_status and $output are returned to the calling subroutine.
-
- If the exit $attempt_limit > 1 and the status is not 0 after all
- attempts are made, undefined is returned. This allows the calling
- subroutine to simply check if result is true if it does not care
- about the output.
-
- There is a special condition if the $attempt_limit is 1 and the
- exit status is not 0. $exit_status and $output are always
- returned so calling subroutine can handle the logic.
-
-=cut
-
-sub _run_vim_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 $vim_arguments = shift;
- if (!$vim_arguments) {
- notify($ERRORS{'WARNING'}, 0, "VIM command arguments were not specified");
- return;
- }
-
- my $timeout_seconds = shift || 60;
- my $attempt_limit = shift || 5;
-
- my $request_state_name = $self->data->get_request_state_name();
- my $vmhost_computer_name = $self->vmhost_os->data->get_computer_short_name();
-
- my $command = "$self->{vim_cmd} $vim_arguments";
-
- my $attempt = 0;
- my $wait_seconds = 5;
-
- my $connection_reset_errors = 0;
-
- ATTEMPT: while ($attempt++ < $attempt_limit) {
-
- my $semaphore;
- if ($attempt > 1) {
- # Wait before making next attempt
- notify($ERRORS{'OK'}, 0, "sleeping $wait_seconds seconds before making attempt $attempt/$attempt_limit");
- sleep_uninterrupted($wait_seconds);
- $semaphore = $self->get_semaphore($vmhost_computer_name, 120, 1) || next ATTEMPT;
- }
-
- # The following error is somewhat common if several processes are adding/removing VMs at the same time:
- # (vmodl.fault.ManagedObjectNotFound) {
- # dynamicType = <unset>,
- # faultCause = (vmodl.MethodFault) null,
- # obj = 'vim.VirtualMachine:672',
- # msg = "The object has already been deleted or has not been completely created",
- # }
-
- # Keep a count of the number of times vim-cmd is executed for the entire vcld state process
- # This will be used to improve performance by reducing the number of calls necessary
- $self->{vim_cmd_calls}++;
- #notify($ERRORS{'DEBUG'}, 0, "vim-cmd call count: $self->{vim_cmd_calls} ($vim_arguments)");
-
- #my $register_semaphore;
- #if ($command =~ /(getallvms|register)/) {
- # $register_semaphore = $self->get_semaphore($vmhost_computer_name, 120, 1);
- # if (!$register_semaphore) {
- # next ATTEMPT;
- # }
- #}
-
- my ($exit_status, $output) = $self->vmhost_os->execute({
- 'command' => $command,
- 'display_output' => 0,
- 'timeout_seconds' => $timeout_seconds,
- #'max_attempts' => 1
- });
-
- if (!defined($output)) {
- notify($ERRORS{'WARNING'}, 0, "attempt $attempt/$attempt_limit: failed to run VIM command on VM host $vmhost_computer_name: $command");
- }
- elsif (grep(/already been deleted/i, @$output)) {
- notify($ERRORS{'OK'}, 0, "attempt $attempt/$attempt_limit: fault occurred attempting to run command on VM host $vmhost_computer_name: $command, output:\n" . join("\n", @$output));
- }
- elsif (grep(/(Failed to login|connection reset|SSL Exception)/i, @$output)) {
- # Try to catch these errors:
- # Failed to login: Connection reset by peer
- # Failed to login: SSL Exception: The SSL handshake timed out local: 127.0.0.1:52713 peer: 127.0.0.1:443.
- $connection_reset_errors++;
- notify($ERRORS{'OK'}, 0, "attempt $attempt/$attempt_limit: connection reset while attempting to run command on VM host $vmhost_computer_name: $command, output:\n" . join("\n", @$output));
-
- # If 2 connection reset errors occured, attempt to run services.sh restart
- if ($connection_reset_errors == 2) {
- if ($self->{services_restarted}) {
- notify($ERRORS{'WARNING'}, 0, "encountered $connection_reset_errors connection reset errors on VM host $vmhost_computer_name, not calling 'services.sh restart', it was already attempted");
- }
- else {
- notify($ERRORS{'OK'}, 0, "calling 'services.sh restart', encountered $connection_reset_errors connection reset errors on VM host $vmhost_computer_name");
- $self->_services_restart();
- $self->{services_restarted} = 1;
- next ATTEMPT;
- }
- }
- elsif ($connection_reset_errors > 2) {
- notify($ERRORS{'WARNING'}, 0, "encountered $connection_reset_errors connection reset errors on VM host $vmhost_computer_name");
- }
- else {
- next ATTEMPT;
- }
-
- # Problem probably won't correct itself
- # If request state is 'inuse', set the reservation.lastcheck value to 20 minutes before request.end
- # This avoids 'inuse' processes from being created over and over again which will fail
- if ($request_state_name eq 'inuse') {
- my $reservation_id = $self->data->get_reservation_id();
- my $request_end_time_epoch = convert_to_epoch_seconds($self->data->get_request_end_time());
- my $current_time_epoch = time;
- my $reservation_lastcheck_epoch = ($request_end_time_epoch-(20*60));
- set_reservation_lastcheck($reservation_lastcheck_epoch, $reservation_id);
- }
- return;
- }
- elsif (grep(/^(vim-cmd:|Killed|terminate called|Aborted|what\()/i, @$output)) {
- # terminate called after throwing an instance of 'std::bad_alloc'
- # what(): std::bad_alloc
- # Aborted
- notify($ERRORS{'WARNING'}, 0, "attempt $attempt/$attempt_limit: failed to execute command on VM host $vmhost_computer_name: $command, exit status: $exit_status, output:\n" . join("\n", @$output));
- next ATTEMPT;
- }
- elsif ($exit_status != 0) {
- if ($attempt_limit == 1) {
- notify($ERRORS{'DEBUG'}, 0, "command failed on VM host $vmhost_computer_name, not making another attempt because attempt limit argument is set to $attempt_limit, error checking will be done by calling subroutine, command: $command, exit status: $exit_status, output:\n" . join("\n", @$output));
- return ($exit_status, $output);
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "attempt $attempt/$attempt_limit: command failed on VM host $vmhost_computer_name: $command, exit status: $exit_status, output:\n" . join("\n", @$output));
- next ATTEMPT;
- }
- }
- else {
- # VIM command command was executed
- if ($attempt > 1) {
- notify($ERRORS{'DEBUG'}, 0, "attempt $attempt/$attempt_limit: executed command on VM host $vmhost_computer_name: $command, exit status: $exit_status");
- }
- else {
- notify($ERRORS{'DEBUG'}, 0, "executed command on VM host $vmhost_computer_name: $command, exit status: $exit_status");
- }
- return ($exit_status, $output);
- }
- }
-
- notify($ERRORS{'WARNING'}, 0, "failed to run VIM command on VM host $vmhost_computer_name: '$command', made $attempt_limit attempts");
- return;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _services_restart
-
- Parameters : none
- Returns : boolean
- Description : Calls 'services.sh restart' on the VM host. This may resolve
- problems where the host is not responding due to a problem with
- one or more services. This should rarely be called.
-
-=cut
-
-sub _services_restart {
- 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_computer_name = $self->vmhost_os->data->get_computer_short_name();
-
- my $semaphore = $self->get_semaphore("$vmhost_computer_name-vmware_services_restart", 0);
- if (!$semaphore) {
- notify($ERRORS{'OK'}, 0, "unable to obtain semaphore, another process is likely restarting services on $vmhost_computer_name, sleeping for 30 seconds and then proceeding");
- sleep_uninterrupted(30);
- return 1;
- }
-
- my $check_services = {
- 'hostd-worker' => '/var/run/vmware/vmware-hostd.PID',
- 'sfcb-vmware_bas' => '/var/run/vmware/vicimprovider.PID',
- 'vmkdevmgr' => '/var/run/vmware/vmkdevmgr.pid',
- 'vmkeventd' => '/var/run/vmware/vmkeventd.pid',
- 'vmsyslogd' => '/var/run/vmware/vmsyslogd.pid',
- 'rhttpproxy-work' => '/var/run/vmware/vmware-rhttpproxy.PID',
- 'vpxa-worker' => '/var/run/vmware/vmware-vpxa.PID',
- };
-
- # Check if the PID files for the following services are correct
- for my $service_name (keys %$check_services) {
- my $pid_file_path = $check_services->{$service_name};
- $self->_check_service_pid($service_name, $pid_file_path);
- }
-
- my $services_command = "services.sh restart";
- notify($ERRORS{'DEBUG'}, 0, "restarting VMware services on $vmhost_computer_name");
- my ($services_exit_status, $services_output) = $self->vmhost_os->execute($services_command, 0, 120);
- if (!defined($services_output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run command on VM host $vmhost_computer_name: $services_command");
- return;
- }
- else {
- notify($ERRORS{'OK'}, 0, "executed command to restart VMware services on $vmhost_computer_name, command: '$services_command', output:\n" . join("\n", @$services_output));
- }
- return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _check_service_pid
-
- Parameters : $process_name, $pid_file_path
- Returns : boolean
- Description : Checks if the PID stored in the PID file matches the parent PID
- of the running service process. Problems occur if the file does
- not match the running process PID. Most often, vim-cmd commands
- fail with an error such as:
- Connect to localhost failed: Connection failure
-
- The PID file is updated with the correct PID if the PID file
- contents cannot be retrieved and parsed or if the PID stored in
- the file does not match the running process.
-
-=cut
-
-sub _check_service_pid {
- 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 ($process_name, $pid_file_path) = @_;
- if (!defined($process_name) || !defined($pid_file_path)) {
- notify($ERRORS{'WARNING'}, 0, "process name and PID file path arguments were not supplied");
- return;
- }
-
- my $vmhost_computer_name = $self->vmhost_os->data->get_computer_short_name();
-
- # Retrieve the running PID
- my $running_pid;
- my $ps_command = "ps |grep $process_name |awk '{print \$2}'";
- my ($ps_exit_status, $ps_output) = $self->vmhost_os->execute($ps_command);
- if (!defined($ps_output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run command to determine main $process_name PID on $vmhost_computer_name");
- }
- else {
- ($running_pid) = "@$ps_output" =~ /(\d+)/g;
- if ($running_pid && $running_pid > 1) {
- notify($ERRORS{'DEBUG'}, 0, "retrieved parent $process_name PID: $running_pid");
- }
- else {
- notify($ERRORS{'DEBUG'}, 0, "parent $process_name process is not running");
- }
- }
-
- # Check if the .pid file exists
- my $pid_file_exists = $self->vmhost_os->file_exists($pid_file_path);
- if (!$running_pid) {
- if ($pid_file_exists) {
- notify($ERRORS{'DEBUG'}, 0, "running $process_name process was not detected but PID file exists: $pid_file_path, deleting file");
- if ($self->vmhost_os->delete_file($pid_file_path)) {
- notify($ERRORS{'DEBUG'}, 0, "deleted file on $vmhost_computer_name: $pid_file_path");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to delete file on $vmhost_computer_name: $pid_file_path");
- }
- return 1;
- }
- else {
- notify($ERRORS{'DEBUG'}, 0, "running $process_name process was not detected and PID file does not exist: $pid_file_path");
- return 1;
- }
- }
- else {
- if ($pid_file_exists) {
- # Retrieve the PID stored in the PID file
- my @pid_file_contents = $self->vmhost_os->get_file_contents($pid_file_path);
- if (@pid_file_contents) {
- my ($file_pid) = "@pid_file_contents" =~ /(\d+)/g;
- if ($file_pid) {
- notify($ERRORS{'DEBUG'}, 0, "retrieved PID stored in $pid_file_path: $file_pid");
- if ($file_pid eq $running_pid) {
- notify($ERRORS{'OK'}, 0, "PID in $pid_file_path ($file_pid) matches PID of parent $process_name process ($running_pid), update not necessary");
- return 1;
- }
- else {
- notify($ERRORS{'OK'}, 0, "PID in $pid_file_path ($file_pid) does not match PID of parent $process_name process ($running_pid), updating $pid_file_path to contain $running_pid");
- }
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to determine PID stored in $pid_file_path, contents:\n" . join("\n", @pid_file_contents));
- }
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to retrieve contents of $pid_file_path");
- }
- }
-
- # Update the PID file with the correct PID
- my $echo_command = "echo -n $running_pid > $pid_file_path";
- my ($echo_exit_status, $echo_output) = $self->vmhost_os->execute($echo_command);
- if (!defined($echo_output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run command to update $pid_file_path on $vmhost_computer_name");
- return;
- }
- elsif (grep(/(ash:|echo:)/, @$echo_output)) {
- notify($ERRORS{'WARNING'}, 0, "error occurred updating $pid_file_path on $vmhost_computer_name, command: '$echo_command', output:\n" . joini("\n", @$echo_output));
- return;
- }
- else {
- notify($ERRORS{'OK'}, 0, "updated $pid_file_path on $vmhost_computer_name to contain the correct PID: $running_pid");
- }
- }
-
- return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _get_vm_list
-
- Parameters : none
- Returns : hash
- Description : Returns an hash with keys containing the IDs of the VMs running
- on the VM host. The values are the vmx file paths.
-
-=cut
-
-sub _get_vm_list {
- 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 $vim_cmd_arguments = "vmsvc/getallvms";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # Check the vim-cmd output
- # Format of the output from "vim-cmd vmsvc/getallvms":
- # Vmid Name File Guest OS Version Annotation
- # 496 vm-ark-mcnc-9 (nonpersistent: vmwarewinxp-base234-v12) [nfs-datastore] vm-ark-mcnc-9_234-v12/vm-ark-mcnc-9_234-v12.vmx winXPProGuest vmx-04
- # 512 vm-ark-mcnc-10 (nonpersistent: vmwarelinux-centosbase1617-v1) [nfs-datastore] vm-ark-mcnc-10_1617-v1/vm-ark-mcnc-10_1617-v1.vmx otherLinuxGuest vmx-04
- if (!grep(/Vmid\s+Name/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine VM IDs, unexpected output returned, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
-
- my %vms;
- for my $line (@$output) {
- my ($vm_id, $vmx_file_path) = $line =~ /^(\d+).*(\[.+\.vmx)/;
-
- # Skip lines that don't begin with a number
- next if !defined($vm_id);
-
- # Make sure the vmx file path was parsed
- if (!$vmx_file_path) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine vmx file path, VIM command arguments: '$vim_cmd_arguments', output line: $line");
- return;
- }
-
- # Get the normal path
- my $vmx_normal_path = $self->_get_normal_path($vmx_file_path);
- if (!$vmx_normal_path) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine normal path: $vmx_file_path");
- #return;
- }
-
- $vms{$vm_id} = $vmx_normal_path;
- }
-
- #notify($ERRORS{'DEBUG'}, 0, "registered VMs IDs found: " . keys(%vms));
- return \%vms;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _get_vm_id
-
- Parameters : $vmx_file_path
- Returns : integer
- Description :
-
-=cut
-
-sub _get_vm_id {
- 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 $vmx_file_path = shift;
- if (!$vmx_file_path) {
- notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not specified");
- return;
- }
-
- return $self->{vm_id}{$vmx_file_path} if $self->{vm_id}{$vmx_file_path};
-
- # Get the VM IDs and vmx paths
- my $vm_list = $self->_get_vm_list();
- if (!defined($vm_list)) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine VM ID, failed to retrieve list of registered VMs and their IDs");
- return;
- }
-
- for my $vm_id (keys %$vm_list) {
- if ($vm_list->{$vm_id} && $vmx_file_path eq $vm_list->{$vm_id}) {
- $self->{vm_id}{$vmx_file_path} = $vm_id;
- return $vm_id;
- }
- }
-
- notify($ERRORS{'WARNING'}, 0, "unable to determine VM ID, vmx file is not registered: $vmx_file_path, registered VMs:\n" . format_data($vm_list));
- return;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _get_vm_summary
-
- Parameters : $vm_id
- Returns : string
- Description : Runs "vim-cmd vmsvc/get.summary <VM ID>" to retrive a summary
- of the configuration of a VM.
-
-=cut
-
-sub _get_vm_summary {
- 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 || $self->get_vmx_file_path();
- if (!$vmx_file_path) {
- notify($ERRORS{'WARNING'}, 0, "vmx file path argument could not be determined");
- return;
- }
-
- my $vm_id = $self->_get_vm_id($vmx_file_path) || return;
-
- my $vim_cmd_arguments = "vmsvc/get.summary $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # The output should look like this:
- # Listsummary:
- # (vim.vm.Summary) {
- # dynamicType = <unset>,
- # vm = 'vim.VirtualMachine:496',
- # runtime = (vim.vm.RuntimeInfo) {
- # dynamicType = <unset>,
- # host = 'vim.HostSystem:ha-host',
- # connectionState = "connected",
- # powerState = "poweredOn",
- # faultToleranceState = "notConfigured",
- # toolsInstallerMounted = false,
- # suspendTime = <unset>,
- # bootTime = "2010-06-08T14:26:48.658743Z",
- # suspendInterval = 0,
- # question = (vim.vm.QuestionInfo) null,
- # memoryOverhead = 119189504,
- # maxCpuUsage = 2000,
- # maxMemoryUsage = 1024,
- # numMksConnections = 0,
- # recordReplayState = "inactive",
- # cleanPowerOff = <unset>,
- # needSecondaryReason = <unset>,
- # },
- # guest = (vim.vm.Summary.GuestSummary) {
- # dynamicType = <unset>,
- # guestId = "winXPProGuest",
- # guestFullName = "Microsoft Windows XP Professional (32-bit)",
- # toolsStatus = "toolsOld",
- # toolsVersionStatus = "guestToolsNeedUpgrade",
- # toolsRunningStatus = "guestToolsRunning",
- # hostName = "APACHE-44896D77.dcs.mcnc.org",
- # ipAddress = "152.46.16.235",
- # },
- # config = (vim.vm.Summary.ConfigSummary) {
- # dynamicType = <unset>,
- # name = "vm-ark-mcnc-9 (nonpersistent: vmwarewinxp-base234-v12)",
- # template = false,
- # vmPathName = "[nfs-datastore] vm-ark-mcnc-9_234-v12/vm-ark-mcnc-9_234-v12.vmx",
- # memorySizeMB = 1024,
- # cpuReservation = <unset>,
- # memoryReservation = <unset>,
- # numCpu = 1,
- # numEthernetCards = 2,
- # numVirtualDisks = 1,
- # uuid = "564d36cf-6988-c91d-0f5f-a62628d46553",
- # instanceUuid = "",
- # guestId = "winXPProGuest",
- # guestFullName = "Microsoft Windows XP Professional (32-bit)",
- # annotation = "",
- # product = (vim.vApp.ProductInfo) null,
- # installBootRequired = <unset>,
- # ftInfo = (vim.vm.FaultToleranceConfigInfo) null,
- # },
- # storage = (vim.vm.Summary.StorageSummary) {
- # dynamicType = <unset>,
- # committed = 4408509391,
- # uncommitted = 11697668096,
- # unshared = 4408509391,
- # timestamp = "2010-06-08T14:26:30.312473Z",
- # },
- # quickStats = (vim.vm.Summary.QuickStats) {
- # dynamicType = <unset>,
- # overallCpuUsage = 20,
- # overallCpuDemand = <unset>,
- # guestMemoryUsage = 40,
- # hostMemoryUsage = 652,
- # guestHeartbeatStatus = "yellow",
- # distributedCpuEntitlement = <unset>,
- # distributedMemoryEntitlement = <unset>,
- # staticCpuEntitlement = <unset>,
- # staticMemoryEntitlement = <unset>,
- # privateMemory = <unset>,
- # sharedMemory = <unset>,
- # swappedMemory = <unset>,
- # balloonedMemory = <unset>,
- # consumedOverheadMemory = <unset>,
- # ftLogBandwidth = <unset>,
- # ftSecondaryLatency = <unset>,
- # ftLatencyStatus = <unset>,
- # },
- # overallStatus = "green",
- # }
- if (!grep(/vim\.vm\.Summary/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "unable to retrieve VM summary, unexpected output returned, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
-
- my $vm_summary_info = $self->_parse_vim_cmd_output($output);
- if (defined($vm_summary_info->{'vim.vm.Summary'})) {
- return $vm_summary_info->{'vim.vm.Summary'};
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to retrieve summary of VM: $vmx_file_path, parsed output does not contain a 'vim.vm.Summary' key:\n" . format_data($vm_summary_info));
- return;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _get_datastore_info
-
- Parameters : none
- Returns : hash reference
- Description :
-
-=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;
- }
-
- # Return previously retrieved datastore name array if it is defined in this object
- if ($self->{datastore}) {
- return $self->{datastore};
- }
-
- my $vmhost_hostname = $self->data->get_vmhost_hostname();
-
- my $vim_cmd_arguments = "hostsvc/datastore/listsummary";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # The output should look like this:
- # (vim.Datastore.Summary) [
- # (vim.Datastore.Summary) {
- # dynamicType = <unset>,
- # datastore = 'vim.Datastore:4bcf0efe-c426acc4-c7e1-001a644d1cc0',
- # name = "local-datastore",
- # url = "/vmfs/volumes/4bcf0efe-c426acc4-c7e1-001a644d1cc0",
- # capacity = 31138512896,
- # freeSpace = 26277314560,
- # uncommitted = 0,
- # accessible = true,
- # multipleHostAccess = <unset>,
- # type = "VMFS",
- # },
- # (vim.Datastore.Summary) {
- # dynamicType = <unset>,
- # datastore = 'vim.Datastore:10.25.0.245:/vmfs/volumes/nfs-datastore',
- # name = "nfs-datastore",
- # url = "/vmfs/volumes/95e378c2-863dd2b4",
- # capacity = 975027175424,
- # freeSpace = 108854874112,
- # uncommitted = 0,
- # accessible = true,
- # multipleHostAccess = <unset>,
- # type = "NFS",
- # },
- # ]
- if (!grep(/vim\.Datastore\.Summary/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine datastore names, unexpected output returned, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
-
- my $datastore_info;
-
- # Split the output into sections for each datastore
- my @output_sections = split(/vim\.Datastore\.Summary/i, join("\n", @$output));
-
- for my $output_section (@output_sections) {
- my ($datastore_name) = $output_section =~ /name\s*=\s*"(.+)"/;
- next if (!defined($datastore_name));
-
- for my $line (split(/[\r\n]+/, $output_section)) {
- # Skip lines which don't contain a '='
- next if $line !~ /=/;
-
- # Parse the line
- my ($parameter, $value) = $line =~ /^\s*(\w+)\s*=[\s"']*([^"',]+)/g;
- if (defined($parameter) && defined($value)) {
- $datastore_info->{$datastore_name}{$parameter} = $value if ($parameter ne 'name');
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to parse parameter and value from line: '$line'");
- }
- }
-
- # Check if the accessible value was retrieved and is not false
- my $datastore_accessible = $datastore_info->{$datastore_name}{accessible};
- if (!$datastore_accessible || $datastore_accessible =~ /false/i) {
- notify($ERRORS{'DEBUG'}, 0, "datastore '$datastore_name' is mounted on $vmhost_hostname but not accessible");
- delete $datastore_info->{$datastore_name};
- next;
- }
-
- # Add a 'normal_path' key to the hash based on the datastore url
- my $datastore_url = $datastore_info->{$datastore_name}{url};
- if (!defined($datastore_url)) {
- notify($ERRORS{'WARNING'}, 0, "failed to determine datastore url from 'vim-cmd $vim_cmd_arguments' output section, datastore name: $datastore_name:\n$output_section");
- delete $datastore_info->{$datastore_name};
- next;
- }
-
- my $datastore_normal_path;
- if ($datastore_url =~ /^\/vmfs\/volumes/i) {
- $datastore_normal_path = "/vmfs/volumes/$datastore_name";
- }
- else {
- $datastore_normal_path = $datastore_url;
- }
- $datastore_info->{$datastore_name}{normal_path} = $datastore_normal_path;
- }
-
- return $datastore_info;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _get_task_ids
-
- Parameters : $vmx_file_path, $task_type
- Returns : array
- Description : Returns an array containing the task IDs recently executed on
- the VM indicated by the $vm_id argument. The task type argument
- must be specified. Example task type values:
- powerOn
- powerOff
- registerVm
- unregisterVm
-
-=cut
-
-sub _get_task_ids {
- 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 || $self->get_vmx_file_path();
- if (!$vmx_file_path) {
- notify($ERRORS{'WARNING'}, 0, "vmx file path argument could not be determined");
- return;
- }
-
- # Get the task type argument
- my $task_type = shift;
-
- my $vm_id = $self->_get_vm_id($vmx_file_path) || return;
-
- my $vim_cmd_arguments = "vmsvc/get.tasklist $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # Expected output:
- # (ManagedObjectReference) [
- # 'vim.Task:haTask-512-vim.VirtualMachine.powerOn-2826',
- # 'vim.Task:haTask-512-vim.VirtualMachine.powerOn-2843',
- # 'vim.Task:haTask-512-vim.VirtualMachine.powerOn-2856'
- # ]
-
- # Expected output if there are no recent tasks:
- # (ManagedObjectReference) []
-
- if (!grep(/ManagedObjectReference/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to retrieve task list, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
- elsif (grep(/\(ManagedObjectReference\)\s*\[\]/i, @$output)) {
- notify($ERRORS{'DEBUG'}, 0, "there are no recent tasks for VM $vm_id");
- return ();
- }
-
- #notify($ERRORS{'DEBUG'}, 0, "task list output:\n" . join("\n", @$output));
-
- # Reverse the output array so the newest tasks are listed first
- my @reversed_output = reverse(@$output);
-
- #notify($ERRORS{'DEBUG'}, 0, "reversed task list output:\n" . join("\n", @reversed_output));
-
- #my (@task_ids) = grep(/haTask-$vm_id-.+$task_type-/, @reversed_output);
- my @task_ids;
- if ($task_type) {
- @task_ids = map { /(haTask-$vm_id-.+$task_type-\d+)/ } @reversed_output;
- }
- else {
- @task_ids = map { /(haTask-$vm_id-.+-\d+)/ } @reversed_output;
- $task_type = 'all';
- }
-
- # Check if a matching task was found
- if (!@task_ids) {
- notify($ERRORS{'WARNING'}, 0, "failed to determine task IDs from output for VM $vm_id, task type: $task_type, output:\n" . join("\n", @$output));
- return;
- }
-
- #notify($ERRORS{'DEBUG'}, 0, "retrieved task IDs for VM $vm_id, task type: $task_type:\n" . join("\n", @task_ids));
- return @task_ids;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _get_task_info
-
- Parameters : $task_id
- Returns : hash reference
- Description :
-
-=cut
-
-sub _get_task_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;
- }
-
- # Get the task ID path argument
- my (@task_ids) = @_;
- @task_ids = $self->_get_task_ids() if (!@task_ids);
-
- my $task_info = {};
-
- for my $task_id (@task_ids) {
- my $vim_cmd_arguments = "vimsvc/task_info $task_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
-
- # Expected output:
- # (vim.TaskInfo) {
- # dynamicType = <unset>,
- # key = "haTask-496-vim.VirtualMachine.powerOn-3072",
- # task = 'vim.Task:haTask-496-vim.VirtualMachine.powerOn-3072',
- # description = (vmodl.LocalizableMessage) null,
- # name = "vim.VirtualMachine.powerOn",
- # descriptionId = "VirtualMachine.powerOn",
- # entity = 'vim.VirtualMachine:496',
- # entityName = "vm-ark-mcnc-9 (nonpersistent: vmwarewinxp-base234-v12)",
- # state = "error",
- # cancelled = false,
- # cancelable = false,
- # error = (vmodl.fault.RequestCanceled) {
- # dynamicType = <unset>,
- # faultCause = (vmodl.MethodFault) null,
- # msg = "The task was canceled by a user.",
- # },
- # result = <unset>,
- # progress = 100,
- # reason = (vim.TaskReasonUser) {
- # dynamicType = <unset>,
- # userName = "root",
- # },
- # queueTime = "2010-06-30T08:48:44.187347Z",
- # startTime = "2010-06-30T08:48:44.187347Z",
- # completeTime = "2010-06-30T08:49:26.381383Z",
- # eventChainId = 3072,
- # changeTag = <unset>,
- # parentTaskKey = <unset>,
- # rootTaskKey = <unset>,
- # }
-
- # Expected output if the task is not found:
- # (vmodl.fault.ManagedObjectNotFound) {
- # dynamicType = <unset>,
- # faultCause = (vmodl.MethodFault) null,
- # obj = 'vim.Task:haTask-496-vim.VirtualMachine.powerOn-3072x',
- # msg = "The object has already been deleted or has not been completely created",
- # }
-
- if (!defined($output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to execute command to retrieve info for task ID: $task_id");
- next;
- }
- elsif (grep(/ManagedObjectNotFound/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "task was not found, task ID: $task_id, output:\n" . join("\n", @$output));
- next;
- }
- elsif (!grep(/vim.TaskInfo/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to retrieve task list, VIM command arguments: '$vim_cmd_arguments' output:\n" . join("\n", @$output));
- next;
- }
- else {
- #notify($ERRORS{'DEBUG'}, 0, "retrieved info for task $task_id");
- $task_info->{$task_id} = join("\n", @$output);
- }
- }
-
- return $task_info;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _wait_for_task
-
- Parameters : $task_id, $timeout_seconds (optional)
- Returns : boolean
- Description : Waits for the vim task to complete. Returns true if the task
- completes successfully.
-
-=cut
-
-sub _wait_for_task {
- 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 task ID path argument
- my $task_id = shift;
- if (!$task_id) {
- notify($ERRORS{'WARNING'}, 0, "task ID argument was not supplied");
- return;
- }
-
- my $timeout_seconds = shift || 30;
-
- my $start_time = time();
-
- while (time() - $start_time < $timeout_seconds) {
- notify($ERRORS{'DEBUG'}, 0, "checking status of task: $task_id");
-
- # Get the task info
- my $task_info = $self->_get_task_info($task_id);
- my $task_info_output = $task_info->{$task_id};
- if (!$task_info || !$task_info_output) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine if task $task_id has completed, task info could not be retrieved");
- return;
- }
-
- # Parse the output to get the task state and progress
- my ($task_state) = $task_info_output =~ /state\s*=\s*"([^"]+)/is;
- if (!$task_state) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine task state from task info output:\n$task_info_output");
- return;
- }
-
- my ($error_message) = $task_info_output =~ /msg\s*=\s*"([^"]+)/;
- $error_message = $task_info_output if !$error_message;
-
- my ($progress)= $task_info_output =~ /progress\s*=\s*(\d+)/;
- $progress = 'unknown' if !defined($progress);
-
- if ($task_state =~ /success/) {
- notify($ERRORS{'DEBUG'}, 0, "task completed successfully: $task_id");
- return 1;
- }
- elsif ($task_state =~ /error|cancelled/) {
-
- # Check if the task failed with the message: 'Operation failed since another task is in progress.'
- if ($error_message =~ /another task is in progress/i) {
- # Retrieve info for all of the VMs recent tasks
- my $task_info_all = $self->_get_task_info();
- notify($ERRORS{'WARNING'}, 0, "task $task_id did not complete successfully, state: $task_state, error message: $error_message, task info:\n" . format_data($task_info_all));
- }
- elsif ($error_message =~ /state of the virtual machine has not changed since the last snapshot/i) {
- # Snapshot may fail if VM is suspended and snapshot was already taken after suspension, message will be:
- # message = "An error occurred while taking a snapshot: The state of the virtual machine has not changed since the last snapshot operation."
- notify($ERRORS{'DEBUG'}, 0, "snapshot task is not necessary: $task_id, message: $error_message");
- return 1;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "task $task_id did not complete successfully, state: $task_state, error message: $error_message");
- }
- return;
- }
- elsif ($task_state =~ /running/) {
- notify($ERRORS{'DEBUG'}, 0, "task state: $task_state, progress: $progress, sleeping for 3 seconds before checking task state again");
- sleep 3;
- }
- else {
- notify($ERRORS{'DEBUG'}, 0, "task state: $task_state, progress: $progress, sleeping for 3 seconds before checking task state again, output:\n$task_info_output");
- sleep 3;
- }
- }
-
- notify($ERRORS{'WARNING'}, 0, "timeout was reached: $timeout_seconds seconds, task never completed");
- return;
-}
-
-###############################################################################
-
-=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;
- }
-
- # Get the VM IDs
- my $vm_list = $self->_get_vm_list();
- if (!defined($vm_list)) {
- notify($ERRORS{'WARNING'}, 0, "unable to retrieve registered VMs, failed to retrieve list of registered VMs and their IDs");
- return;
- }
-
- # Get the vmx path values for each VM
- my @vmx_paths = sort { lc($a) cmp lc($b) } values(%$vm_list);
-
- notify($ERRORS{'DEBUG'}, 0, "found " . scalar(@vmx_paths) . " registered VMs");
- return @vmx_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 $vm_id = $self->_get_vm_id($vmx_file_path) || return;
-
- my $vim_cmd_arguments = "vmsvc/power.getstate $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # The output should look like this:
- # Retrieved runtime info
- # Powered on
-
- # Retrieved runtime info
- # Powered off
-
- # Retrieved runtime info
- # Suspended
-
- notify($ERRORS{'DEBUG'}, 0, "$vim_cmd_arguments:\n" . join("\n", @$output));
-
- if (grep(/powered on/i, @$output)) {
- notify($ERRORS{'DEBUG'}, 0, "VM is powered on: $vmx_file_path");
- return 'on';
- }
- elsif (grep(/powered off/i, @$output)) {
- notify($ERRORS{'DEBUG'}, 0, "VM is powered off: $vmx_file_path");
- return 'off';
- }
- elsif (grep(/suspended/i, @$output)) {
- notify($ERRORS{'DEBUG'}, 0, "VM is suspended: $vmx_file_path");
- return 'suspended';
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to determine power state of $vmx_file_path, VIM command arguments: '$vim_cmd_arguments' output:\n" . join("\n", @$output));
- return;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=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;
- }
-
- # Get the VM ID
- my $vm_id = $self->_get_vm_id($vmx_file_path);
- if (!defined($vm_id)) {
- notify($ERRORS{'WARNING'}, 0, "unable to power on VM because VM ID could not be determined");
- return;
- }
-
- my $vim_cmd_arguments = "vmsvc/power.on $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 360);
- return if !$output;
-
- # Expected output if the VM was not previously powered on:
- # Powering on VM:
-
- # Expected output if the VM was previously powered on:
- # Powering on VM:
- # (vim.fault.InvalidPowerState) {
- # dynamicType = <unset>,
- # faultCause = (vmodl.MethodFault) null,
- # requestedState = "poweredOn",
- # existingState = "poweredOn",
- # msg = "The attempted operation cannot be performed in the current state (Powered On).",
- # }
-
- if (grep(/existingState = "poweredOn"/i, @$output)) {
- notify($ERRORS{'OK'}, 0, "VM is already powered on: $vmx_file_path");
- return 1;
- }
- elsif (!grep(/Powering on VM/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to power on VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
-
- # Get the task ID
- my @task_ids = $self->_get_task_ids($vmx_file_path, 'powerOn');
- if (!@task_ids) {
- notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the task created to power on the VM");
- return;
- }
-
- # Wait for the task to complete
- if ($self->_wait_for_task($task_ids[0])) {
- notify($ERRORS{'OK'}, 0, "powered on VM: $vmx_file_path");
- return 1;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to power on VM: $vmx_file_path, the vim power on task did not complete successfully, vim-cmd $vim_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;
- }
-
- # Check if the VM is already powered off
- my $vm_power_state = $self->get_vm_power_state($vmx_file_path);
- if ($vm_power_state && $vm_power_state =~ /off/i) {
- notify($ERRORS{'DEBUG'}, 0, "VM is already powered off: $vmx_file_path");
- return 1;
- }
-
- # Get the VM ID
- my $vm_id = $self->_get_vm_id($vmx_file_path);
- if (!defined($vm_id)) {
- notify($ERRORS{'WARNING'}, 0, "unable to power off VM because VM ID could not be determined");
- return;
- }
-
- my $vim_cmd_arguments = "vmsvc/power.off $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # Expected output if the VM was not previously powered off:
- # Powering off VM:
-
- # Expected output if the VM was previously powered off:
- # Powering off VM:
- # (vim.fault.InvalidPowerState) {
- # dynamicType = <unset>,
- # faultCause = (vmodl.MethodFault) null,
- # requestedState = "poweredOff",
- # existingState = "poweredOff",
- # msg = "The attempted operation cannot be performed in the current state (Powered Off).",
- # }
-
- if (grep(/existingState = "poweredOff"/i, @$output)) {
- notify($ERRORS{'DEBUG'}, 0, "VM is already powered off: $vmx_file_path");
- return 1;
- }
- elsif (!grep(/Powering off VM/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to power off VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
-
- # Get the task ID
- my @task_ids = $self->_get_task_ids($vmx_file_path, 'powerOff');
- if (!@task_ids) {
- notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the task created to power off the VM");
- return;
- }
-
- # Wait for the task to complete
- if ($self->_wait_for_task($task_ids[0])) {
- notify($ERRORS{'OK'}, 0, "powered off VM: $vmx_file_path");
- return 1;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to power off VM: $vmx_file_path, the vim power off task did not complete successfully, vim-cmd $vim_cmd_arguments output:\n" . join("\n", @$output));
- return;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 vm_suspend
-
- Parameters : $vmx_file_path
- Returns : boolean
- Description : Suspends the VM indicated by the vmx file path argument.
-
-=cut
-
-sub vm_suspend {
- 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;
- }
-
- # Check if the VM is already powered off
- my $vm_power_state = $self->get_vm_power_state($vmx_file_path);
- if ($vm_power_state) {
- if ($vm_power_state =~ /off/i) {
- notify($ERRORS{'DEBUG'}, 0, "VM is already powered off: $vmx_file_path");
- return 1;
- }
- elsif ($vm_power_state =~ /suspend/i) {
- notify($ERRORS{'DEBUG'}, 0, "VM is already suspended: $vmx_file_path");
- return 1;
- }
- }
-
- # Get the VM ID
- my $vm_id = $self->_get_vm_id($vmx_file_path);
- if (!defined($vm_id)) {
- notify($ERRORS{'WARNING'}, 0, "unable to power off VM because VM ID could not be determined");
- return;
- }
-
- notify($ERRORS{'DEBUG'}, 0, "suspending VM: $vmx_file_path ($vm_id)");
- my $start_time = time;
- my $vim_cmd_arguments = "vmsvc/power.suspend $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 400);
- return if !$output;
-
- # Expected output if the VM was not previously suspended:
- # Suspending VM:
-
- # Expected output if the VM was previously suspended or powered off:
- # Suspending VM:
- # Suspend failed
-
- if (!grep(/Suspending VM/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to suspend VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
-
- # Get the task ID
- my @task_ids = $self->_get_task_ids($vmx_file_path, 'suspend');
- if (!@task_ids) {
- notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the task created to suspend the VM");
- return;
- }
-
- # Wait for the task to complete
- if ($self->_wait_for_task($task_ids[0])) {
- my $duration = (time - $start_time);
- notify($ERRORS{'OK'}, 0, "suspended VM: $vmx_file_path, took $duration seconds");
- return 1;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to suspend VM: $vmx_file_path, the vim power off task did not complete successfully, vim-cmd $vim_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;
- }
-
- # Check if the VM is already registered
- if ($self->is_vm_registered($vmx_file_path)) {
- notify($ERRORS{'OK'}, 0, "VM is already registered: $vmx_file_path");
- return 1;
- }
-
- $vmx_file_path =~ s/\\* /\\ /g;
- my $vim_cmd_arguments = "solo/registervm \"$vmx_file_path\"";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 60, 1);
- return if !$output;
-
- # Note: registervm does not produce any output if it was successful
-
- # Expected output if the vmx file path does not exist:
- # (vim.fault.NotFound) {
- # dynamicType = <unset>,
- # faultCause = (vmodl.MethodFault) null,
- # msg = "The object or item referred to could not be found.",
- # }
-
- if (grep(/vim.fault.NotFound/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to register VM, vmx file was not found: $vmx_file_path, output:\n" . join("\n", @$output));
- return;
- }
- elsif (grep(/vim.fault.AlreadyExists/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to register VM on the 1st attempt, an existing invalid VM using the same vmx file path may already already be registered, output:\n" . join("\n", @$output));
-
- # If an "invalid" VM exists using the same .vmx path, this fault will be generated:
- # (vim.fault.AlreadyExists) {
- # faultCause = (vmodl.MethodFault) null,
- # name = "51",
- # msg = "The specified key, name, or identifier '51' already exists."
- my ($vm_id) = join("\n", @$output) =~ /name\s*=\s*"(\d+)"/;
- if ($vm_id) {
- if ($self->vm_unregister($vm_id)) {
- notify($ERRORS{'DEBUG'}, 0, "unregistered existing invalid VM $vm_id, making another attempt to register VM: $vmx_file_path");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to register VM: $vmx_file_path, unable to unregister existing invalid VM $vm_id");
- return;
- }
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to register VM: $vmx_file_path, ID of existing invalid VM could not be determined, was expecting a line beginning with 'name = \"<ID>\"' in output:\n" . join("\n", @$output));
- return;
- }
- }
-
- if (grep(/fault/i, @$output)) {
- # Only made 1 attempt so far, try again if fault occurred, allow 4 more attempts
- ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 60, 4);
- return if !$output;
- }
-
- if (grep(/fault/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to register VM: $vmx_file_path, vim-cmd $vim_cmd_arguments output:\n" . join("\n", @$output));
- return;
- }
-
- # Check to make sure the VM is registered
- if ($self->is_vm_registered($vmx_file_path)) {
- notify($ERRORS{'OK'}, 0, "registered VM: '$vmx_file_path'");
- return 1;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to register VM: '$vmx_file_path'");
- return;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 vm_unregister
-
- Parameters : $vm_identifier
- Returns : boolean
- Description : Unregisters the VM indicated by the argument which may either be
- the .vmx file path or VM ID.
-
-=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;
- }
-
- # Note: allow the VM ID to be passed in case the .vmx file path cannot be determined
- # This allows an invalid VM with a missing .vmx file to be unregistered
-
- my $vm_identifier = shift;
- if (!$vm_identifier) {
- notify($ERRORS{'WARNING'}, 0, "VM identifier argument was not supplied");
- return;
- }
-
- my $vm_id;
- my $vmx_file_path;
- if ($vm_identifier =~ /^\d+$/) {
- $vm_id = $vm_identifier;
- }
- else {
- # Argument should be the vmx file path
- $vmx_file_path = $vm_identifier;
-
- # 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_path");
- return 1;
- }
-
- # Power of the VM if it is powered on or the unregister command will fail
- my $vm_power_state = $self->get_vm_power_state($vmx_file_path);
- if ($vm_power_state && $vm_power_state =~ /on/i) {
- if (!$self->vm_power_off($vmx_file_path)) {
- notify($ERRORS{'WARNING'}, 0, "failed to unregister VM because it could not be powered off: $vmx_file_path");
- return;
- }
- }
-
- $vm_id = $self->_get_vm_id($vmx_file_path);
- if (!defined($vm_id)) {
- notify($ERRORS{'OK'}, 0, "unable to unregister VM because VM ID could not be determined for vmx path argument: $vmx_file_path");
- return;
- }
- }
-
- my $vim_cmd_arguments = "vmsvc/unregister $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
-
- # Delete cached .vmx - VM ID mapping if previously retrieved
- delete $self->{vm_id}{$vm_identifier};
-
- return if !$output;
-
- # Expected output if the VM is not registered:
- # (vim.fault.NotFound) {
- # dynamicType = <unset>,
- # faultCause = (vmodl.MethodFault) null,
- # msg = "Unable to find a VM corresponding to "/vmfs/volumes/nfs-datastore/vm-ark-mcnc-9_234-v12/vm-ark-mcnc-9_234-v12.vmx"",
- # }
-
- if (grep(/fault/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to unregister VM, VIM command arguments: '$vim_cmd_arguments'\noutput:\n" . join("\n", @$output));
- return;
- }
-
- # Check to make sure the VM is not registered
- if ($vmx_file_path && $self->is_vm_registered($vmx_file_path)) {
- notify($ERRORS{'WARNING'}, 0, "failed to unregister VM: $vmx_file_path (ID: $vm_id), it still appears to be registered");
- return;
- }
- else {
- notify($ERRORS{'OK'}, 0, "unregistered VM: $vm_identifier");
- return 1;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=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:
- -FlatVer1
- -FlatVer2
- -RawDiskMappingVer1
- -SparseVer1
- -SparseVer2
-
-=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_directory_path, $vmdk_file_name) = $vmdk_file_path =~ /^(.+)\/([^\/]+\.vmdk)/;
- if (!$vmdk_directory_path || !$vmdk_file_name) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine directory path and file name from vmdk file path: $vmdk_directory_path");
- return;
- }
-
- my $vmdk_directory_datastore_path = $self->_get_datastore_path($vmdk_directory_path);
- if (!$vmdk_directory_datastore_path) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk directory datastore path from vmdk directory path: $vmdk_directory_path");
- return;
- }
-
- # The output of 'vim-cmd hostsvc/datastorebrowser/disksearch' differs for VMware Server 2.x and ESXi
- # The value of 'thin' is not returned if disksearch is run under ESXi
- my $vmware_product_name = $self->get_vmhost_product_name();
- my $vim_cmd_arguments;
- if ($vmware_product_name =~ /esx/i) {
- $vim_cmd_arguments = "hostsvc/datastorebrowser/search 0 \"$vmdk_directory_datastore_path\"";
- }
- else {
- $vim_cmd_arguments = "hostsvc/datastorebrowser/disksearch \"$vmdk_directory_datastore_path\"";
- }
-
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # Expected output:
- #(vim.host.DatastoreBrowser.SearchResults) {
- # dynamicType = <unset>,
- # datastore = 'vim.Datastore:10.10.14.20:/nfs-datastore1',
- # folderPath = "[nfs-datastore1] vclv17-149_234-v14",
- # file = (vim.host.DatastoreBrowser.FileInfo) [
- # (vim.host.DatastoreBrowser.VmDiskInfo) {
- # dynamicType = <unset>,
- # path = "vmwarewinxp-base234-v14.vmdk",
- # fileSize = 5926510592,
- # modification = "2010-11-24T17:06:44Z",
- # owner = <unset>,
- # diskType = "vim.vm.device.VirtualDisk.FlatVer2BackingInfo",
- # capacityKb = 14680064,
- # hardwareVersion = 4,
- # controllerType = "vim.vm.device.VirtualIDEController",
- # diskExtents = (string) [
- # "[nfs-datastore1] vclv17-149_234-v14/vmwarewinxp-base234-v14-flat.vmdk"
- # ],
- # thin = true,
- # }
- # ],
- #}
-
-
- my $output_string = join("\n", @$output);
- my (@disk_info_sections) = split(/vim.host.DatastoreBrowser.VmDiskInfo/, $output_string);
-
- for my $disk_info (@disk_info_sections) {
- my ($disk_path) = $disk_info =~ /\spath = "(.+)"/i;
-
- if (!$disk_path || $disk_path ne $vmdk_file_name) {
- next;
- }
-
- my ($disk_type) = $disk_info =~ /\sdiskType = "(.+)"/i;
- if (!$disk_type) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine disk type, disk path: $disk_path, disk info section from vim-cmd $vim_cmd_arguments output:\n$disk_info");
- next;
- }
-
- # Disk type format: vim.vm.device.VirtualDisk.FlatVer2BackingInfo
- # Remove everything but "FlatVer2"
- $disk_type =~ s/(^.*\.|BackingInfo$)//g;
-
- # Return 'thin' if thin is set to true
- my ($thin) = $disk_info =~ /\sthin\s*=\s*(.+)/i;
- if (defined($thin) && $thin =~ /true/) {
- $disk_type = 'thin';
- }
-
- notify($ERRORS{'DEBUG'}, 0, "$disk_path disk type: $disk_type");
- return $disk_type;
- }
-
- notify($ERRORS{'WARNING'}, 0, "unable to determine disk type for disk: $vmdk_file_path, vim-cmd $vim_cmd_arguments output:\n" . join("\n", @$output));
- return;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 get_virtual_disk_controller_type
-
- Parameters : $vmdk_file_path
- Returns : string
- Description : Retrieves the disk controller type configured for the virtual
- disk specified by the vmdk file path argument. False is returned
- if the controller type cannot be retrieved. A string is returned
- containing one of the following values:
- -IDE
- -lsiLogic
- -busLogic
-
-=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_directory_path, $vmdk_file_name) = $vmdk_file_path =~ /^(.+)\/([^\/]+\.vmdk)/;
- if (!$vmdk_directory_path || !$vmdk_file_name) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine directory path and file name from vmdk file path: $vmdk_directory_path");
- return;
- }
-
- my $vmdk_directory_datastore_path = $self->_get_datastore_path($vmdk_directory_path);
- if (!$vmdk_directory_datastore_path) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk directory datastore path from vmdk directory path: $vmdk_directory_path");
- return;
- }
-
- my $vim_cmd_arguments = "hostsvc/datastorebrowser/searchsubfolders 0 \"$vmdk_directory_datastore_path\"";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # Expected output:
- # (vim.host.DatastoreBrowser.SearchResults) {
- # dynamicType = <unset>,
- # datastore = 'vim.Datastore:10.25.0.245:/vmfs/volumes/nfs-datastore',
- # folderPath = "[nfs-datastore] vmwarewinxp-base234-v12",
- # file = (vim.host.DatastoreBrowser.FileInfo) [
- # (vim.host.DatastoreBrowser.VmDiskInfo) {
- # dynamicType = <unset>,
- # path = "vmwarewinxp-base234-v12.vmdk",
- # fileSize = 4774187008,
- # modification = "2010-06-30T21:03:45Z",
- # owner = <unset>,
- # diskType = "vim.vm.device.VirtualDisk.SparseVer2BackingInfo",
- # capacityKb = 14680064,
- # hardwareVersion = 4,
- # controllerType = "vim.vm.device.VirtualBusLogicController",
- # diskExtents = (string) [
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s001.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s002.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s003.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s004.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s005.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s006.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s007.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s008.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s009.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s010.vmdk"
- # ],
- # thin = false,
- # },
- # (vim.host.DatastoreBrowser.VmDiskInfo) {
- # dynamicType = <unset>,
- # path = "esx_2gb_sparse.vmdk",
- # fileSize = 4410286080,
- # modification = "2010-07-01T18:38:04Z",
- # owner = <unset>,
- # diskType = "vim.vm.device.VirtualDisk.SparseVer2BackingInfo",
- # capacityKb = 14680064,
- # hardwareVersion = 7,
- # controllerType = "vim.vm.device.VirtualIDEController",
- # diskExtents = (string) [
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s001.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s002.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s003.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s004.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s005.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s006.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s007.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s008.vmdk"
- # ],
- # thin = true,
- # },
- # (vim.host.DatastoreBrowser.VmDiskInfo) {
- # dynamicType = <unset>,
- # path = "thin_vmwarewinxp-base234-v12.vmdk",
- # fileSize = 4408459264,
- # modification = "2010-06-30T20:53:51Z",
- # owner = <unset>,
- # diskType = "vim.vm.device.VirtualDisk.FlatVer2BackingInfo",
- # capacityKb = 14680064,
- # hardwareVersion = 4,
- # controllerType = <unset>,
- # diskExtents = (string) [
- # "[nfs-datastore] vmwarewinxp-base234-v12/thin_vmwarewinxp-base234-v12-flat.vmdk"
- # ],
- # thin = true,
- # }
- # ],
- # }
-
- my $output_string = join("\n", @$output);
- my (@disk_info_sections) = split(/vim.host.DatastoreBrowser.VmDiskInfo/, $output_string);
-
- for my $disk_info (@disk_info_sections) {
- my ($disk_path) = $disk_info =~ /\spath = "(.+)"/i;
-
- if (!$disk_path) {
- next;
- }
- elsif ($disk_path ne $vmdk_file_name) {
- #notify($ERRORS{'DEBUG'}, 0, "ignoring disk because the file name does not match $vmdk_file_name: $disk_path");
- next;
- }
-
- my ($controller_type) = $disk_info =~ /\scontrollerType\s*=\s*(.+)/i;
- if (!$controller_type) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine disk controller type, disk path: $disk_path, disk info section from vim-cmd $vim_cmd_arguments output:\n$disk_info");
- next;
- }
-
- if ($controller_type =~ /unset/i) {
- notify($ERRORS{'DEBUG'}, 0, "disk controller type is not set in the vmdk file: $disk_path");
- return 0;
- }
- else {
- # Extract just the controller type name from the value: vim.vm.device.VirtualIDEController --> IDE
- $controller_type =~ s/(.*vim.vm.device.Virtual|Controller.*)//ig;
- notify($ERRORS{'DEBUG'}, 0, "retrieved controller type for $disk_path: '$controller_type'");
- return $controller_type;
- }
- }
-
- notify($ERRORS{'WARNING'}, 0, "unable to determine disk controller type for disk: $vmdk_file_path, vim-cmd $vim_cmd_arguments 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_directory_path, $vmdk_file_name) = $vmdk_file_path =~ /^(.+)\/([^\/]+\.vmdk)/;
- if (!$vmdk_directory_path || !$vmdk_file_name) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine directory path and file name from vmdk file path: $vmdk_directory_path");
- return;
- }
-
- my $vmdk_directory_datastore_path = $self->_get_datastore_path($vmdk_directory_path);
- if (!$vmdk_directory_datastore_path) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk directory datastore path from vmdk directory path: $vmdk_directory_path");
- return;
- }
-
- my $vim_cmd_arguments = "hostsvc/datastorebrowser/searchsubfolders 0 \"$vmdk_directory_datastore_path\"";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # Expected output:
- # (vim.host.DatastoreBrowser.SearchResults) {
- # dynamicType = <unset>,
- # datastore = 'vim.Datastore:10.25.0.245:/vmfs/volumes/nfs-datastore',
- # folderPath = "[nfs-datastore] vmwarewinxp-base234-v12",
- # file = (vim.host.DatastoreBrowser.FileInfo) [
- # (vim.host.DatastoreBrowser.VmDiskInfo) {
- # dynamicType = <unset>,
- # path = "vmwarewinxp-base234-v12.vmdk",
- # fileSize = 4774187008,
- # modification = "2010-06-30T21:03:45Z",
- # owner = <unset>,
- # diskType = "vim.vm.device.VirtualDisk.SparseVer2BackingInfo",
- # capacityKb = 14680064,
- # hardwareVersion = 4,
- # controllerType = "vim.vm.device.VirtualBusLogicController",
- # diskExtents = (string) [
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s001.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s002.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s003.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s004.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s005.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s006.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s007.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s008.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s009.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/vmwarewinxp-base234-v12-s010.vmdk"
- # ],
- # thin = false,
- # },
- # (vim.host.DatastoreBrowser.VmDiskInfo) {
- # dynamicType = <unset>,
- # path = "esx_2gb_sparse.vmdk",
- # fileSize = 4410286080,
- # modification = "2010-07-01T18:38:04Z",
- # owner = <unset>,
- # diskType = "vim.vm.device.VirtualDisk.SparseVer2BackingInfo",
- # capacityKb = 14680064,
- # hardwareVersion = 7,
- # controllerType = "vim.vm.device.VirtualIDEController",
- # diskExtents = (string) [
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s001.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s002.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s003.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s004.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s005.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s006.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s007.vmdk",
- # "[nfs-datastore] vmwarewinxp-base234-v12/esx_2gb_sparse-s008.vmdk"
- # ],
- # thin = true,
- # },
- # (vim.host.DatastoreBrowser.VmDiskInfo) {
- # dynamicType = <unset>,
- # path = "thin_vmwarewinxp-base234-v12.vmdk",
- # fileSize = 4408459264,
- # modification = "2010-06-30T20:53:51Z",
- # owner = <unset>,
- # diskType = "vim.vm.device.VirtualDisk.FlatVer2BackingInfo",
- # capacityKb = 14680064,
- # hardwareVersion = 4,
- # controllerType = <unset>,
- # diskExtents = (string) [
- # "[nfs-datastore] vmwarewinxp-base234-v12/thin_vmwarewinxp-base234-v12-flat.vmdk"
- # ],
- # thin = true,
- # }
- # ],
- # }
-
- my $output_string = join("\n", @$output);
- my (@disk_info_sections) = split(/vim.host.DatastoreBrowser.VmDiskInfo/, $output_string);
-
- for my $disk_info (@disk_info_sections) {
- my ($disk_path) = $disk_info =~ /\spath = "(.+)"/i;
-
- if (!$disk_path) {
- next;
- }
- elsif ($disk_path ne $vmdk_file_name) {
- notify($ERRORS{'DEBUG'}, 0, "ignoring disk because the file name does not match $vmdk_file_name: $disk_path");
- next;
- }
-
- my ($hardware_version) = $disk_info =~ /\shardwareVersion\s*=\s*(\d+)/ig;
- if (!$hardware_version) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine disk hardware version, disk path: $disk_path, disk info section from vim-cmd $vim_cmd_arguments output:\n$disk_info");
- next;
- }
- else {
- notify($ERRORS{'DEBUG'}, 0, "retrieved hardware version for $disk_path: '$hardware_version'");
- return $hardware_version;
- }
- }
-
- notify($ERRORS{'WARNING'}, 0, "unable to determine hardware version for disk: $vmdk_file_path, vim-cmd $vim_cmd_arguments output:\n" . join("\n", @$output));
- return;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 get_network_names
-
- Parameters : none
- Returns : array
- Description : Retrieves the network names configured on the VM host.
-
-=cut
-
-sub get_network_names {
- 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 $vim_cmd_arguments = "solo/environment";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # The output should contain a network section:
- #network = (vim.vm.NetworkInfo) [
- # (vim.vm.NetworkInfo) {
- # dynamicType = <unset>,
- # name = "Private",
- # network = (vim.Network.Summary) {
- # dynamicType = <unset>,
- # network = 'vim.Network:HaNetwork-Private',
- # name = "Private",
- # accessible = true,
- # ipPoolName = "",
- # },
- # },
- # (vim.vm.NetworkInfo) {
- # dynamicType = <unset>,
- # name = "Public",
- # network = (vim.Network.Summary) {
- # dynamicType = <unset>,
- # network = 'vim.Network:HaNetwork-Public',
- # name = "Public",
- # accessible = true,
- # ipPoolName = "",
- # },
- # },
- #],
-
- # Convert the output line array to a string then split it by network sections
- my ($network_info) = join("\n", @$output) =~ /(vim\.vm\.NetworkInfo[^\]]+)/;
- if ($network_info) {
- notify($ERRORS{'DEBUG'}, 0, "network info:\n$network_info");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to retrieve network info, vim-cmd arguments: '$vim_cmd_arguments', $exit_status: $exit_status, output:\n" . join("\n", @$output));
- return;
- }
-
- my (@network_sections) = split(/vim.vm.NetworkInfo/, $network_info);
-
- # Extract the network names from the network sections
- my @network_names;
- for my $network_info (@network_sections) {
- my ($network_name) = $network_info =~ /\sname = "(.+)"/i;
- next if !$network_name;
- push @network_names, $network_name;
- }
-
- notify($ERRORS{'DEBUG'}, 0, "retrieved network names:\n" . join("\n", @network_names));
- return @network_names;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 create_snapshot
-
- Parameters : $vmx_file_path, $name (optional)
- 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 $snapshot_name = shift || ("VCL: " . convert_to_datetime());
-
- # Get the VM ID
- my $vm_id = $self->_get_vm_id($vmx_file_path);
- if (!defined($vm_id)) {
- notify($ERRORS{'WARNING'}, 0, "unable to create snapshot because VM ID could not be determined");
- return;
- }
-
- my $vim_cmd_arguments = "vmsvc/snapshot.create $vm_id '$snapshot_name'";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 60, 1);
- return if !$output;
-
- notify($ERRORS{'DEBUG'}, 0, "create snapshot output:\n" . join("\n", @$output));
-
- # IMPORTANT: Don't check for 'failed' in the output, it may contain failed but the snapshot is not necessary:
- # Snapshot not taken since the state of the virtual machine has not changed since the last snapshot operation.
- #if (grep(/failed|invalid/i, @$output)) {
- # notify($ERRORS{'WARNING'}, 0, "failed to create snapshot of VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- # return;
- #}
-
- # Get the task ID
- my @task_ids = $self->_get_task_ids($vmx_file_path, 'createSnapshot');
- if (!@task_ids) {
- notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the task created to create snapshot");
- return;
- }
-
- # Wait for the task to complete
- if ($self->_wait_for_task($task_ids[0])) {
- notify($ERRORS{'OK'}, 0, "created snapshot of VM: $vmx_file_path, snapshot name: $snapshot_name");
- return 1;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to create snapshot VM: $vmx_file_path, the vim task did not complete successfully, vim-cmd $vim_cmd_arguments output:\n" . join("\n", @$output));
- return;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 remove_snapshots
-
- Parameters : $vmx_file_path
- Returns : boolean
- Description : Removes all snapshots for a VM.
-
-=cut
-
-sub remove_snapshots {
- 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;
- }
-
- # Get the VM ID
- my $vm_id = $self->_get_vm_id($vmx_file_path);
- if (!defined($vm_id)) {
- notify($ERRORS{'WARNING'}, 0, "unable to create snapshot because VM ID could not be determined");
- return;
- }
-
- my $vim_cmd_arguments = "vmsvc/snapshot.removeall $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 7200);
- return if !$output;
-
- notify($ERRORS{'DEBUG'}, 0, "remove snapshots output:\n" . join("\n", @$output));
-
- if (grep(/failed|invalid/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to remove snapshots for VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
-
- # Get the task ID
- my @task_ids = $self->_get_task_ids($vmx_file_path, 'removeAllSnapshots');
- if (!@task_ids) {
- notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the task created to remove snapshots");
- return;
- }
-
- # Wait for the task to complete
- if ($self->_wait_for_task($task_ids[0], 7200)) {
- notify($ERRORS{'OK'}, 0, "removed snapshots for VM: $vmx_file_path");
- return 1;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to remove snapshots for VM: $vmx_file_path, the vim task did not complete successfully, vim-cmd $vim_cmd_arguments output:\n" . join("\n", @$output));
- return;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 snapshot_exists
-
- Parameters : $vmx_file_path
- Returns : boolean
- Description : Determines if a snapshot exists for the VM.
-
-=cut
-
-sub snapshot_exists {
- 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;
- }
-
- # Get the VM ID
- my $vm_id = $self->_get_vm_id($vmx_file_path);
- if (!defined($vm_id)) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine if snapshot exists because VM ID could not be determined");
- return;
- }
-
- my $vim_cmd_arguments = "vmsvc/snapshot.get $vm_id";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- notify($ERRORS{'DEBUG'}, 0, "snapshot.get output:\n" . join("\n", @$output));
-
- if (grep(/failed|invalid/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to determine if snapshot exists for VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
- return;
- }
-
- # Expected output if shapshot exists:
- # Get Snapshot:
- # |-ROOT
- # --Snapshot Name : 1311966951
- # --Snapshot Desciption :
- # --Snapshot Created On : 7/29/2011 19:15:59
- # --Snapshot State : powered off
-
- # Expected output if snapshot does not exist:
- # Get Snapshot:
-
- if (grep(/-ROOT/, @$output)) {
- notify($ERRORS{'DEBUG'}, 0, "snapshot exists for VM $vmx_file_path");
- return 1;
- }
- else {
- notify($ERRORS{'DEBUG'}, 0, "snapshot does NOT exist for VM $vmx_file_path");
- return 0;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 get_cpu_core_count
-
- Parameters : none
- Returns : integer
- Description : Retrieves the quantitiy of CPU cores the VM host has.
-
-=cut
-
-sub get_cpu_core_count {
- 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_hostname = $self->data->get_vmhost_hostname();
-
- my $vim_cmd_arguments = "hostsvc/hosthardware";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # The CPU info should be contained in the output:
- # cpuInfo = (vim.host.CpuInfo) {
- # dynamicType = <unset>,
- # numCpuPackages = 2,
- # numCpuCores = 8,
- # numCpuThreads = 8,
- # hz = 2000070804,
- # },
-
- my ($cpu_cores_line) = grep(/^\s*numCpuCores\s*=/i, @$output);
- if (!$cpu_cores_line) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine VM host $vmhost_hostname CPU core count, output does not contain a 'numCpuCores =' line:\n" . join("\n", @$output));
- return;
- }
- elsif ($cpu_cores_line =~ /(\d+)/) {
- my $cpu_core_count = $1;
- notify($ERRORS{'DEBUG'}, 0, "retrieved VM host $vmhost_hostname CPU core count: $cpu_core_count");
- return $cpu_core_count;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to determine VM host $vmhost_hostname CPU core count from line: $cpu_cores_line");
- return;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 get_cpu_speed
-
- Parameters : none
- Returns : integer
- Description : Retrieves the speed of the VM host's CPUs in MHz.
-
-=cut
-
-sub get_cpu_speed {
- 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_hostname = $self->data->get_vmhost_hostname();
-
- my $vim_cmd_arguments = "hostsvc/hosthardware";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # The CPU info should be contained in the output:
- # cpuInfo = (vim.host.CpuInfo) {
- # dynamicType = <unset>,
- # numCpuPackages = 2,
- # numCpuCores = 8,
- # numCpuThreads = 8,
- # hz = 2000070804,
- # },
-
- my ($hz_line) = grep(/^\s*hz\s*=/i, @$output);
- if (!$hz_line) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine VM host $vmhost_hostname CPU speed, output does not contain a 'hz =' line:\n" . join("\n", @$output));
- return;
- }
- elsif ($hz_line =~ /(\d+)/) {
- my $mhz = int($1 / 1000000);
- notify($ERRORS{'DEBUG'}, 0, "retrieved VM host $vmhost_hostname CPU speed: $mhz MHz");
- return $mhz;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to determine VM host $vmhost_hostname CPU speed from line: $hz_line");
- return;
- }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 get_total_memory
-
- Parameters : none
- Returns : integer
- Description : Retrieves the VM host's total memory capacity in MB.
-
-=cut
-
-sub get_total_memory {
- 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_hostname = $self->data->get_vmhost_hostname();
-
- my $vim_cmd_arguments = "hostsvc/hosthardware";
- my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
- return if !$output;
-
- # The following line should be contained in the output:
- # memorySize = 17178869760,
-
- my ($memory_size_line) = grep(/^\s*memorySize\s*=/i, @$output);
- if (!$memory_size_line) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine VM host $vmhost_hostname total memory capacity, output does not contain a 'memorySize =' line:\n" . join("\n", @$output));
- return;
- }
- elsif ($memory_size_line =~ /(\d+)/) {
- my $memory_mb = int($1 / 1024 / 1024);
- notify($ERRORS{'DEBUG'}, 0, "retrieved VM host $vmhost_hostname total memory capacity: $memory_mb MB");
- return $memory_mb;
- }
- else {
[... 5603 lines stripped ...]