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 2010/06/07 20:59:25 UTC
svn commit: r952366 [2/3] - in
/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware: ./
VIX_API.pm VMware.pm vSphere_SDK.pm
Added: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm?rev=952366&view=auto
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm (added)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm Mon Jun 7 18:59:25 2010
@@ -0,0 +1,3336 @@
+#!/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::Provisioning::VMware::Vmware
+
+=head1 SYNOPSIS
+
+ Needs to be written
+
+=head1 DESCRIPTION
+
+ This module provides VCL support for VMWare
+ http://www.vmware.com
+
+=cut
+
+##############################################################################
+package VCL::Module::Provisioning::VMware::VMware;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../../..";
+
+# Configure inheritance
+use base qw(VCL::Module::Provisioning);
+
+# Specify the version of this module
+our $VERSION = '2.00';
+
+# Specify the version of Perl to use
+use 5.008000;
+
+use strict;
+use warnings;
+use diagnostics;
+use English qw( -no_match_vars );
+use IO::File;
+use Fcntl qw(:DEFAULT :flock);
+use File::Temp qw( tempfile );
+
+use VCL::utils;
+
+use VCL::Module::Provisioning::VMware::vSphere_SDK;
+use VCL::Module::Provisioning::VMware::VIX_API;
+
+##############################################################################
+
+=head1 CLASS VARIABLES
+
+=cut
+
+=head2 %VM_OS_CONFIGURATION
+
+ Data type : hash
+ Description : Maps OS names to the appropriate guestOS and Ethernet virtualDev
+ values to be used in the vmx file.
+
+=cut
+
+our %VM_OS_CONFIGURATION = (
+ # Linux configurations:
+ "linux-x86" => {
+ "guestOS" => "otherlinux",
+ "ethernet-virtualDev" => "vlance",
+ },
+ "linux-x86_64" => {
+ "guestOS" => "otherlinux-64",
+ "ethernet-virtualDev" => "e1000",
+ },
+ # Windows configurations:
+ "xp-x86" => {
+ "guestOS" => "winXPPro",
+ "ethernet-virtualDev" => "vlance",
+ },
+ "xp-x86_64" => {
+ "guestOS" => "winXPPro-64",
+ "ethernet-virtualDev" => "e1000",
+ },
+ "vista-x86" => {
+ "guestOS" => "winvista",
+ "ethernet-virtualDev" => "e1000",
+ },
+ "vista-x86_64" => {
+ "guestOS" => "winvista-64",
+ "ethernet-virtualDev" => "e1000",
+ },
+ "7-x86" => {
+ "guestOS" => "winvista",
+ "ethernet-virtualDev" => "e1000",
+ },
+ "7-x86_64" => {
+ "guestOS" => "winvista-64",
+ "ethernet-virtualDev" => "e1000",
+ },
+ "2003-x86" => {
+ "guestOS" => "winNetEnterprise",
+ "ethernet-virtualDev" => "vlance",
+ },
+ "2003-x86_64" => {
+ "guestOS" => "winNetEnterprise-64",
+ "ethernet-virtualDev" => "e1000",
+ },
+ "2008-x86" => {
+ "guestOS" => "winServer2008Enterprise-32",
+ "ethernet-virtualDev" => "e1000",
+ },
+ "2008-x86_64" => {
+ "guestOS" => "winServer2008Enterprise-64",
+ "ethernet-virtualDev" => "e1000",
+ },
+ # Default Windows configuration if Windows version isn't found above:
+ "windows-x86" => {
+ "guestOS" => "winXPPro",
+ "ethernet-virtualDev" => "vlance",
+ },
+ "windows-x86_64" => {
+ "guestOS" => "winXPPro-64",
+ "ethernet-virtualDev" => "e1000",
+ },
+ # Default configuration if OS is not Windows or Linux:
+ "default-x86" => {
+ "guestOS" => "other",
+ "ethernet-virtualDev" => "vlance",
+ },
+ "default-x86_64" => {
+ "guestOS" => "other-64",
+ "ethernet-virtualDev" => "e1000",
+ },
+);
+
+=head2 $VSPHERE_SDK_PACKAGE
+
+ Data type : string
+ Description : Perl package name for the vSphere SDK module.
+
+=cut
+
+our $VSPHERE_SDK_PACKAGE = 'VCL::Module::Provisioning::VMware::vSphere_SDK';
+
+=head2 $VIX_API_PACKAGE
+
+ Data type : string
+ Description : Perl package name for the VIX API module.
+
+=cut
+
+our $VIX_API_PACKAGE = 'VCL::Module::Provisioning::VMware::VIX_API';
+
+##############################################################################
+
+=head1 OBJECT METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 initialize
+
+ Parameters : none
+ Returns : boolean
+ Description : Determines how the VM and VM host can be contolled. Creates an
+ API object which is used to control the VM throughout the
+ reservation. Creates a VM host OS object to be used to control
+ the VM host throughout the reservation.
+
+=cut
+
+sub initialize {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmhost_data = $self->get_vmhost_datastructure() || return;
+ my $vmhost_computer_name = $vmhost_data->get_computer_node_name() || return;
+ my $vm_computer_name = $self->data->get_computer_node_name() || return;
+
+ my $vmware_api;
+ my $vmhost_os;
+
+ # Create an API object which will be used to control the VM (register, power on, etc.)
+ if (($vmware_api = $self->get_vsphere_sdk_object()) && !$vmware_api->is_restricted()) {
+ notify($ERRORS{'DEBUG'}, 0, "vSphere SDK object will be used to control the VM: $vm_computer_name, and to control the OS of the VM host: $vmhost_computer_name");
+ $vmhost_os = $vmware_api;
+ }
+ elsif (($vmware_api = $self->get_vix_api_object()) && !$vmware_api->is_restricted()) {
+ notify($ERRORS{'DEBUG'}, 0, "VIX API object will be used to control the VM: $vm_computer_name");
+ }
+ # Add code here to create an API object to control free ESXi via SSH once the utility module has been implemented
+ # elsif (...) {
+ # }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to create an API object to control the VM: $vm_computer_name");
+ return;
+ }
+
+ # Create a VM host OS object if the vSphere SDK can't be used to control the VM host OS
+ if (!$vmhost_os) {
+ # Get a DataStructure object containing the VM host's data and get the VM host OS module Perl package name
+ my $vmhost_image_name = $vmhost_data->get_image_name();
+ my $vmhost_os_module_package = $vmhost_data->get_image_os_module_perl_package();
+
+ notify($ERRORS{'DEBUG'}, 0, "attempting to create OS object for the image currently loaded on the VM host: $vmhost_computer_name\nimage name: $vmhost_image_name\nOS module: $vmhost_os_module_package");
+
+ if ($vmhost_os = $self->get_vmhost_os_object($vmhost_os_module_package)) {
+ if ($vmhost_os->is_ssh_responding()) {
+ notify($ERRORS{'DEBUG'}, 0, "OS of VM host $vmhost_computer_name will be controlled via SSH using OS object: " . ref($vmhost_os));
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unable to control OS of VM host $vmhost_computer_name using OS object: " . ref($vmhost_os) . ", VM host is not responding to SSH");
+ return;
+ }
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to create OS object to control the OS of VM host: $vmhost_computer_name");
+ return;
+ }
+ }
+
+ # Store the VM host and API objects in this object
+ $self->{vmhost_os} = $vmhost_os;
+ $self->{api} = $vmware_api;
+
+ # Make sure the vmx and vmdk base directories can be accessed
+ my $vmx_base_directory_path = $self->get_vmx_base_directory_path() || return;
+ if (!$vmhost_os->file_exists($vmx_base_directory_path)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to access vmx base directory path: $vmx_base_directory_path");
+ return;
+ }
+ my $vmdk_base_directory_path = $self->get_vmdk_base_directory_path() || return;
+ if (($vmx_base_directory_path ne $vmdk_base_directory_path) && !$vmhost_os->file_exists($vmdk_base_directory_path)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to access vmdk base directory path: $vmdk_base_directory_path");
+ return;
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "VMware provisioning object initialized:\nVM host OS object type: " . ref($self->{vmhost_os}) . "\nAPI object type: " . ref($self->{api}));
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 load
+
+ Parameters : none
+ Returns : boolean
+ Description : Loads a VM with the requested image.
+
+=cut
+
+sub load {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $reservation_id = $self->data->get_reservation_id() || return;
+ my $vmx_file_path = $self->get_vmx_file_path() || return;
+ my $computer_id = $self->data->get_computer_id() || return;
+ my $computer_name = $self->data->get_computer_short_name() || return;
+ my $image_name = $self->data->get_image_name() || return;
+ my $vmhost_hostname = $self->data->get_vmhost_hostname() || return;
+
+ insertloadlog($reservation_id, $computer_id, "doesimageexists", "image exists $image_name");
+
+ insertloadlog($reservation_id, $computer_id, "startload", "$computer_name $image_name");
+
+ # Check if the .vmdk files exist, copy them if necessary
+ if (!$self->prepare_vmdk()) {
+ notify($ERRORS{'WARNING'}, 0, "failed to prepare vmdk file for $computer_name on VM host: $vmhost_hostname");
+ return;
+ }
+ insertloadlog($reservation_id, $computer_id, "transfervm", "copied $image_name to $computer_name");
+
+ # Generate the .vmx file
+ if (!$self->prepare_vmx()) {
+ notify($ERRORS{'WARNING'}, 0, "failed to prepare vmx file for $computer_name on VM host: $vmhost_hostname");
+ return;
+ }
+ insertloadlog($reservation_id, $computer_id, "vmsetupconfig", "prepared vmx file");
+
+ # Register the VM
+ if (!$self->api->vm_register($vmx_file_path)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to register VM $computer_name on VM host: $vmhost_hostname");
+ return;
+ }
+
+ # Power on the VM
+ if (!$self->api->vm_power_on($vmx_file_path)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to power on VM $computer_name on VM host: $vmhost_hostname");
+ return;
+ }
+ insertloadlog($reservation_id, $computer_id, "startvm", "registered and powered on $computer_name");
+
+ # Call the OS module's post_load() subroutine if implemented
+ if ($self->os->can("post_load") && !$self->os->post_load()) {
+ notify($ERRORS{'WARNING'}, 0, "failed to perform OS post-load tasks on VM $computer_name on VM host: $vmhost_hostname");
+ return;
+ }
+ insertloadlog($reservation_id, $computer_id, "loadimagecomplete", "performed OS post-load tasks on $computer_name");
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 capture
+
+ Parameters : none
+ Returns : boolean
+ Description : Captures a VM image.
+
+=cut
+
+sub capture {
+ 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 $computer_name = $self->data->get_computer_short_name() || return;
+ my $vmhost_hostname = $self->data->get_vmhost_hostname() || return;
+ my $vmprofile_vmdisk = $self->data->get_vmhost_profile_vmdisk() || return;
+ my $image_name = $self->data->get_image_name() || return;
+ my $vmhost_profile_datastore_path = $self->data->get_vmhost_profile_datastore_path();
+
+ # Get the MAC addresses being used by the running VM for this imaging reservation
+ my @vm_mac_addresses = ($self->os->get_private_mac_address(), $self->os->get_public_mac_address());
+ if (!@vm_mac_addresses) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve the private or public MAC address being used by the VM, needed to determine which vmx to capture");
+ return;
+ }
+
+ # Remove the colons from the MAC addresses and convert to lower case so they can be compared
+ map { s/[^\w]//g; $_ = lc($_) } (@vm_mac_addresses);
+
+ # Get the details of all the vmx files on the VM host
+ my $host_vmx_info = $self->get_vmx_info() || return;
+
+ # Check the vmx files on the VM host, find any with matching MAC addresses
+ my @matching_host_vmx_paths;
+ for my $host_vmx_path (sort keys %$host_vmx_info) {
+ my $vmx_file_name = $host_vmx_info->{$host_vmx_path}{"vmx_file_name"} || '';
+
+ # Ignore the vmx file if it is not registered
+ if (!$self->is_vm_registered($host_vmx_path)) {
+ notify($ERRORS{'OK'}, 0, "ignoring vmx file because the VM is not registered: $host_vmx_path");
+ next;
+ }
+
+ # Ignore the vmx file if the VM is powered on
+ my $power_state = $self->api->get_vm_power_state($host_vmx_path) || 'unknown';
+ if ($power_state !~ /on/i) {
+ notify($ERRORS{'DEBUG'}, 0, "ignoring vmx file because the VM is not powered on: $host_vmx_path");
+ next;
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "checking if vmx file is the one being used by $computer_name: $host_vmx_path");
+
+ # Create an array containing the values of any ethernetx.address or ethernetx.generatedaddress lines
+ my @vmx_mac_addresses;
+ for my $vmx_property (keys %{$host_vmx_info->{$host_vmx_path}}) {
+ if ($vmx_property =~ /^ethernet\d+\.(generated)?address$/i) {
+ push @vmx_mac_addresses, $host_vmx_info->{$host_vmx_path}{$vmx_property};
+ }
+ }
+
+ # Remove the colons from the MAC addresses and convert to lowercase so they can be compared
+ map { s/[^\w]//g; $_ = lc($_) } (@vmx_mac_addresses);
+
+ # Check if any elements of the VM MAC address array intersect with the vmx MAC address array
+ notify($ERRORS{'DEBUG'}, 0, "comparing MAC addresses\nused by $computer_name:\n" . join("\n", sort(@vm_mac_addresses)) . "\nconfigured in $host_vmx_path:\n" . join("\n", sort(@vmx_mac_addresses)));
+ my @matching_mac_addresses = map { my $vm_mac_address = $_; grep(/$vm_mac_address/i, @vmx_mac_addresses) } @vm_mac_addresses;
+ if (@matching_mac_addresses) {
+ notify($ERRORS{'DEBUG'}, 0, "found matching MAC address between $computer_name and $vmx_file_name:\n" . join("\n", sort(@matching_mac_addresses)));
+ push @matching_host_vmx_paths, $host_vmx_path;
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "did NOT find matching MAC address between $computer_name and $vmx_file_name");
+ }
+ }
+
+ # Check if any matching vmx files were found
+ if (!@matching_host_vmx_paths) {
+ notify($ERRORS{'WARNING'}, 0, "did not find any vmx files on the VM host containing a MAC address matching $computer_name");
+ return;
+ }
+ elsif (scalar(@matching_host_vmx_paths) > 1) {
+ notify($ERRORS{'WARNING'}, 0, "found multiple vmx files on the VM host containing a MAC address matching $computer_name:\n" . join("\n", @matching_host_vmx_paths));
+ return
+ }
+
+ my $vmx_file_path = $matching_host_vmx_paths[0];
+ notify($ERRORS{'OK'}, 0, "found vmx file being used by $computer_name: $vmx_file_path");
+
+ # Set the vmx file path in this object so that it overrides the default value that would normally be constructed
+ $self->set_vmx_file_path($vmx_file_path) || return;
+
+ # Get the information contained within the vmx file
+ my $vmx_info = $host_vmx_info->{$vmx_file_path};
+ notify($ERRORS{'DEBUG'}, 0, "vmx info for VM to be captured:\n" . format_data($vmx_info));
+
+ # Get the vmdk info from the vmx info
+ my @vmdk_identifiers = keys %{$vmx_info->{vmdk}};
+ if (!@vmdk_identifiers) {
+ notify($ERRORS{'WARNING'}, 0, "did not find vmdk file in vmx info ({vmdk} key):\n" . format_data($vmx_info));
+ return;
+ }
+ elsif (scalar(@vmdk_identifiers) > 1) {
+ notify($ERRORS{'WARNING'}, 0, "found multiple vmdk files in vmx info ({vmdk} keys):\n" . format_data($vmx_info));
+ return;
+ }
+
+ # Get the vmdk file path from the vmx information
+ my $vmdk_file_path = $vmx_info->{vmdk}{$vmdk_identifiers[0]}{vmdk_file_path};
+ if (!$vmdk_file_path) {
+ notify($ERRORS{'WARNING'}, 0, "vmdk file path was not found in the vmx info:\n" . format_data($vmx_info));
+ return;
+ }
+ notify($ERRORS{'DEBUG'}, 0, "vmdk file path used by the VM: $vmdk_file_path");
+
+ # Get the vmdk mode from the vmx information and make sure it's persistent
+ my $vmdk_mode = $vmx_info->{vmdk}{$vmdk_identifiers[0]}{mode};
+ if (!$vmdk_mode) {
+ notify($ERRORS{'WARNING'}, 0, "vmdk mode was not found in the vmx info:\n" . format_data($vmx_info));
+ return;
+ }
+ elsif ($vmdk_mode !~ /(independent-)?persistent/i) {
+ notify($ERRORS{'WARNING'}, 0, "vmdk mode is not persistent: $vmdk_mode");
+ return;
+ }
+ notify($ERRORS{'DEBUG'}, 0, "vmdk mode is valid: $vmdk_mode");
+
+ # Set the vmdk file path in this object so that it overrides the default value that would normally be constructed
+ $self->set_vmdk_file_path($vmdk_file_path) || return;
+
+ # Construct the vmdk file path where the captured image will be saved to
+ my $vmdk_renamed_file_path = "$vmhost_profile_datastore_path/$image_name/$image_name.vmdk";
+
+ # Make sure the vmdk file path for the captured image doesn't already exist
+ if ($vmdk_file_path ne $vmdk_renamed_file_path && $self->vmhost_os->file_exists($vmdk_renamed_file_path)) {
+ notify($ERRORS{'WARNING'}, 0, "vmdk file for captured image already exists: $vmdk_renamed_file_path");
+ return;
+ }
+
+ # Write the details about the new image to ~/currentimage.txt
+ write_currentimage_txt($self->data) || return;
+
+ # Call the OS module's pre_capture() subroutine if implemented
+ if ($self->os->can("pre_capture")) {
+ $self->os->pre_capture({end_state => 'off'}) || return;
+ }
+
+ # Power off the VM if it's not already off
+ my $vm_power_state = $self->api->get_vm_power_state($vmx_file_path) || return;
+ if ($vm_power_state !~ /off/i) {
+ $self->api->vm_power_off($vmx_file_path) || return;
+
+ # Sleep for 5 seconds to make sure the power off is complete
+ sleep 5;
+ }
+
+ ## Get a lockfile so that only 1 process is operating on VM host files at any one time
+ #my $lockfile = $self->get_lockfile("/tmp/$vmhost_hostname.lock", (60 * 10)) || return;
+
+ # Rename the vmdk files on the VM host and change the vmdk directory name to the image name
+ # This ensures that the vmdk and vmx files now reside in different directories
+ # so that the vmdk files can't be deleted when the vmx directory is deleted later on
+ if ($vmdk_file_path ne $vmdk_renamed_file_path) {
+ $self->rename_vmdk($vmdk_file_path, $vmdk_renamed_file_path) || return;
+ $self->set_vmdk_file_path($vmdk_renamed_file_path) || return;
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "vmdk file does not need to be renamed: $vmdk_file_path");
+ }
+
+ # Check if the VM host is using local or network-based disk to store vmdk files
+ # Don't have to do anything else for network disk because the vmdk directory has already been renamed
+ if ($vmprofile_vmdisk eq "localdisk") {
+ # Get the vmdk directory path
+ my $vmdk_directory_path = $self->get_vmdk_directory_path() || return;
+
+ # Copy the vmdk directory from the VM host to the image repository
+ my @vmdk_copy_paths = $self->vmhost_os->find_files($vmdk_directory_path, '*.vmdk');
+ if (!@vmdk_copy_paths) {
+ notify($ERRORS{'WARNING'}, 0, "unable to find vmdk file paths on VM host to copy back to the managment node, vmdk directory path: $vmdk_directory_path, pattern: *.vmdk");
+ return;
+ }
+
+ my $repository_directory_path = $self->get_repository_vmdk_directory_path() || return;
+
+ # Loop through the files, copy each to the management node's repository directory
+ for my $vmdk_copy_path (@vmdk_copy_paths) {
+ my ($vmdk_copy_name) = $vmdk_copy_path =~ /([^\/]+)$/g;
+ if (!$self->vmhost_os->copy_file_from($vmdk_copy_path, "$repository_directory_path/$vmdk_copy_name")) {
+ notify($ERRORS{'WARNING'}, 0, "failed to copy vmdk file from the VM host to the management node:\n '$vmdk_copy_path' --> '$repository_directory_path/$vmdk_copy_name'");
+ return;
+ }
+ }
+
+ # Delete the vmdk directory on the VM host
+ $self->vmhost_os->delete_file($vmdk_directory_path) || return;
+ }
+
+ # Unregister the VM
+ $self->api->vm_unregister($vmx_file_path) || return;
+
+ # Delete the vmx directory
+ if ($self->get_vmx_directory_path() ne $self->get_vmdk_directory_path()) {
+ $self->vmhost_os->delete_file($self->get_vmx_directory_path()) || return;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "vmx directory not deleted because it matches the vmdk directory, this should never happen: " . $self->get_vmdk_directory_path());
+ return;
+ }
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 node_status
+
+ Parameters : none
+ Returns : string -- 'READY' or 'RELOAD'
+ Description : Checks the status of a VM. 'READY' is returned if the VM is
+ registered, powered on, accessible via SSH, and the image loaded
+ matches the requested image. 'RELOAD' is returned otherwise.
+
+=cut
+
+sub node_status {
+ 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 $computer_name = $self->data->get_computer_short_name();
+
+ # Check if the VM is registered
+ if ($self->is_vm_registered()) {
+ notify($ERRORS{'DEBUG'}, 0, "VM $computer_name is registered");
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "VM $computer_name is not registered, returning 'RELOAD'");
+ return {'status' => 'RELOAD'};
+ }
+
+ # Check if the VM is powered on
+ my $power_status = $self->power_status();
+ if ($power_status && $power_status =~/on/i) {
+ notify($ERRORS{'DEBUG'}, 0, "VM $computer_name is powered on");
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "VM $computer_name is not powered on, returning 'RELOAD'");
+ return {'status' => 'RELOAD'};
+ }
+
+ # Check if SSH is available
+ if ($self->os->is_ssh_responding()) {
+ notify($ERRORS{'DEBUG'}, 0, "VM $computer_name is responding to SSH");
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "VM $computer_name is not responding to SSH, returning 'RELOAD'");
+ return {'status' => 'RELOAD'};
+ }
+
+ # Get the contents of currentimage.txt
+ my $current_image_name = $self->os->get_current_image_name();
+ if (!$current_image_name) {
+ notify($ERRORS{'DEBUG'}, 0, "unable to retrieve image name from currentimage.txt on VM $computer_name, returning 'RELOAD'");
+ return {'status' => 'RELOAD'};
+ }
+
+ # Check if currentimage.txt matches the requested image name
+ my $image_name = $self->data->get_image_name();
+ if ($current_image_name eq $image_name) {
+ notify($ERRORS{'DEBUG'}, 0, "currentimage.txt image ($current_image_name) matches requested image name ($image_name) on VM $computer_name, returning 'READY'");
+ return {'status' => 'READY'};
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "currentimage.txt image ($current_image_name) does not match requested image name ($image_name) on VM $computer_name, returning 'RELOAD'");
+ return {'status' => 'RELOAD'};
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 vmhost_os
+
+ Parameters : none
+ Returns : OS object reference
+ Description : Returns the OS object that is used to control the VM host.
+
+=cut
+
+sub vmhost_os {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+ return $self->{vmhost_os};
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 api
+
+ Parameters : none
+ Returns : API object reference
+ Description : Returns the VMware API object that is used to control VMs on the
+ VM host.
+
+=cut
+
+sub api {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ if (!$self->{api}) {
+ notify($ERRORS{'WARNING'}, 0, "api object is not defined");
+ return;
+ }
+
+ return $self->{api};
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmhost_datastructure
+
+ Parameters : none
+ Returns : DataStructure object reference
+ Description : Returns a DataStructure object containing the data for the VM
+ host. The computer and image data stored in the object describe
+ the VM host computer, not the VM. All of the other data in the
+ DataStore object matches the data for the regular reservation
+ DataStructure object.
+
+=cut
+
+sub get_vmhost_datastructure {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $request_data = $self->data->get_request_data() || return;
+ my $reservation_id = $self->data->get_reservation_id() || return;
+ my $vmhost_computer_id = $self->data->get_vmhost_computer_id() || return;
+ my $vmhost_profile_image_id = $self->data->get_vmhost_profile_image_id() || return;
+
+ # Create a DataStructure object containing computer data for the VM host
+ my $vmhost_data = new VCL::DataStructure({request_data => $request_data,
+ reservation_id => $reservation_id,
+ computer_id => $vmhost_computer_id,
+ image_id => $vmhost_profile_image_id});
+ if (!$vmhost_data) {
+ notify($ERRORS{'WARNING'}, 0, "unable to create DataStructure object for VM host");
+ return;
+ }
+
+ # Get the VM host nodename from the DataStructure object which was created for it
+ # This acts as a test to make sure the DataStructure object is working
+ my $vmhost_computer_node_name = $vmhost_data->get_computer_node_name();
+ if (!$vmhost_computer_node_name) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine VM host node name from DataStructure object created for VM host");
+ return;
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "created DataStructure object for VM host: $vmhost_computer_node_name");
+ $self->{vmhost_data} = $vmhost_data;
+ return $vmhost_data;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmhost_os_object
+
+ Parameters : $vmhost_os_perl_package (optional)
+ Returns : OS object reference
+ Description : Creates an OS object to be used to control the VM host OS. An
+ optional argument may be specified containing the Perl package to
+ instantiate. If an argument is not specified, the Perl package of
+ the image currently installed on the VM host computer is used.
+
+=cut
+
+sub get_vmhost_os_object {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Get the VM host OS object type argument
+ my $vmhost_os_perl_package = shift;
+
+ # Get a DataStructure object containing the VM host's data
+ my $vmhost_data = $self->get_vmhost_datastructure() || return;
+
+ # Check if the VM host OS object type was specified as an argument
+ if (!$vmhost_os_perl_package) {
+ # Get the VM host OS module Perl package name
+ $vmhost_os_perl_package = $vmhost_data->get_image_os_module_perl_package();
+ if (!$vmhost_os_perl_package) {
+ notify($ERRORS{'WARNING'}, 0, "unable to create DataStructure or OS object for VM host, failed to retrieve VM host image OS module Perl package name");
+ return;
+ }
+ }
+
+ # Load the VM host OS module if it is different than the one already loaded for the reservation image OS
+ notify($ERRORS{'DEBUG'}, 0, "attempting to load VM host OS module: $vmhost_os_perl_package");
+ eval "use $vmhost_os_perl_package";
+ if ($EVAL_ERROR) {
+ notify($ERRORS{'WARNING'}, 0, "VM host OS module could NOT be loaded: $vmhost_os_perl_package, error: $EVAL_ERROR");
+ return;
+ }
+ notify($ERRORS{'DEBUG'}, 0, "VM host OS module loaded: $vmhost_os_perl_package");
+
+ # Create an OS object for the VM host
+ my $vmhost_os;
+ eval { $vmhost_os = ($vmhost_os_perl_package)->new({data_structure => $vmhost_data}) };
+ if ($vmhost_os) {
+ notify($ERRORS{'OK'}, 0, "VM host OS object created: " . ref($vmhost_os));
+ $self->{vmhost_os} = $vmhost_os;
+ return $self->{vmhost_os};
+ }
+ elsif ($EVAL_ERROR) {
+ notify($ERRORS{'WARNING'}, 0, "VM host OS object could not be created: type: $vmhost_os_perl_package, error:\n$EVAL_ERROR");
+ return;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "VM host OS object could not be created, type: $vmhost_os_perl_package, no eval error");
+ return;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vsphere_sdk_object
+
+ Parameters : none
+ Returns : vSphere SDK object
+ Description : Creates and returns a vSphere SDK object which can be used to
+ control VMs on a VM host.
+
+=cut
+
+sub get_vsphere_sdk_object {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Get a DataStructure object containing the VM host's data
+ my $vmhost_datastructure = $self->get_vmhost_datastructure() || return;
+
+ # Get the VM host nodename from the DataStructure object which was created for it
+ # This acts as a test to make sure the DataStructure object is working
+ my $vmhost_nodename = $vmhost_datastructure->get_computer_node_name();
+ if (!$vmhost_nodename) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine VM host node name from DataStructure object created for VM host");
+ return;
+ }
+
+ # Create a vSphere SDK object
+ my $vsphere_sdk;
+ eval { $vsphere_sdk = VCL::Module::Provisioning::VMware::vSphere_SDK->new({data_structure => $vmhost_datastructure}) };
+ if (!$vsphere_sdk) {
+ my $error = $EVAL_ERROR || 'no eval error';
+ notify($ERRORS{'WARNING'}, 0, "vSphere SDK object could not be created, VM host: $vmhost_nodename, $error");
+ return;
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "created vSphere SDK object, VM host: $vmhost_nodename");
+ return $vsphere_sdk;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vix_api_object
+
+ Parameters : none
+ Returns : VIX API object reference
+ Description : Creates and returns a VMware VIX API object which may be used to
+ control the VMs on a VM host.
+
+=cut
+
+sub get_vix_api_object {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Get a DataStructure object containing the VM host's data
+ my $vmhost_datastructure = $self->get_vmhost_datastructure() || return;
+
+ # Get the VM host nodename from the DataStructure object which was created for it
+ # This acts as a test to make sure the DataStructure object is working
+ my $vmhost_nodename = $vmhost_datastructure->get_computer_node_name();
+ if (!$vmhost_nodename) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine VM host node name from DataStructure object created for VM host");
+ return;
+ }
+
+ # Create a VIX API object
+ my $vix_api;
+ eval { $vix_api = VCL::Module::Provisioning::VMware::VIX_API->new({data_structure => $vmhost_datastructure}) };
+ if (!$vix_api) {
+ my $error = $EVAL_ERROR || 'no eval error';
+ notify($ERRORS{'WARNING'}, 0, "VIX API object could not be created, VM host: $vmhost_nodename, $error");
+ return;
+ }
+
+ notify($ERRORS{'OK'}, 0, "created VIX API object, VM host: $vmhost_nodename");
+ return $vix_api;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 prepare_vmx
+
+ Parameters : none
+ Returns : boolean
+ Description : Creates a .vmx file on the VM host configured for the
+ reservation. Checks if a VM for the same VCL computer entry is
+ already registered. If the VM is already registered, it is
+ unregistered and the files for the existing VM are deleted.
+
+=cut
+
+sub prepare_vmx {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Get the required data to configure the .vmx file
+ my $image_id = $self->data->get_image_id() || return;
+ my $imagerevision_id = $self->data->get_imagerevision_id() || return;
+ my $computer_id = $self->data->get_computer_id() || return;
+ my $vmx_file_name = $self->get_vmx_file_name() || return;
+ my $vmx_file_path = $self->get_vmx_file_path() || return;
+ my $vmx_directory_name = $self->get_vmx_directory_name() || return;
+ my $vmx_directory_path = $self->get_vmx_directory_path() || return;
+ my $vmdk_file_path = $self->get_vmdk_file_path() || return;
+ my $computer_name = $self->data->get_computer_short_name() || return;
+ my $image_name = $self->data->get_image_name() || return;
+ my $vm_ram = $self->get_vm_ram() || return;
+ my $vm_cpu_count = $self->data->get_image_minprocnumber() || 1;
+ my $vm_ethernet_adapter_type = $self->get_vm_ethernet_adapter_type() || return;
+ my $vm_eth0_mac = $self->data->get_computer_eth0_mac_address() || return;
+ my $vm_eth1_mac = $self->data->get_computer_eth1_mac_address() || return;
+ my $vm_private_ip_address = $self->data->get_computer_ip_address() || return;
+ my $vm_public_ip_address = $self->data->get_computer_private_ip_address() || return;
+ my $virtual_switch_0 = $self->data->get_vmhost_profile_virtualswitch0() || return;
+ my $virtual_switch_1 = $self->data->get_vmhost_profile_virtualswitch1() || return;
+ my $vm_disk_adapter_type = $self->get_vm_disk_adapter_type() || return;
+ my $vm_hardware_version = $self->get_vm_virtual_hardware_version() || return;
+ my $vm_persistent = $self->is_vm_persistent();
+ my $guest_os = $self->get_vm_guest_os() || return;
+
+ ## Figure out how much additional space is required for the vmx directory for the VM for this reservation
+ ## This is the number of additional bytes which have not already been allocated the VM will likely use
+ #my $vm_additional_vmx_bytes_required = $self->get_vm_additional_vmx_bytes_required();
+ #return if !defined($vm_additional_vmx_bytes_required);
+ #
+ ## Get the number of bytes available on the device where the base vmx directory resides
+ #my $host_vmx_bytes_available = $self->vmhost_os->get_available_space($self->get_vmx_base_directory_path());
+ #return if !defined($host_vmx_bytes_available);
+ #
+ ## Check if there is enough space available for the VM's vmx files
+ #if ($vm_additional_vmx_bytes_required > $host_vmx_bytes_available) {
+ # my $vmx_deficit_bytes = ($vm_additional_vmx_bytes_required - $host_vmx_bytes_available);
+ # my $vmx_deficit_mb = format_number($vmx_deficit_bytes / 1024 / 1024);
+ # notify($ERRORS{'WARNING'}, 0, "not enough space is available for the vmx files on the VM host, deficit: $vmx_deficit_bytes bytes ($vmx_deficit_mb MB)");
+ #}
+ #else {
+ # notify($ERRORS{'DEBUG'}, 0, "enough space is available for the vmx files on the VM host");
+ #}
+
+ # Get a hash containing info about all the .vmx files that exist on the VM host
+ # Check the VMs on the host to see if any match the computer assigned to this reservation
+ my $host_vmx_info = $self->get_vmx_info();
+ for my $host_vmx_path (keys %$host_vmx_info) {
+ my $host_vmx_computer_id = $host_vmx_info->{$host_vmx_path}{computer_id} || 'unknown';
+
+ # If existing VM is for this computer, delete it
+ if ($computer_id eq $host_vmx_computer_id) {
+ notify($ERRORS{'DEBUG'}, 0, "found vmx file on VM host with matching computer id: $computer_id, $host_vmx_path");
+ if (!$self->delete_vm($host_vmx_path)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to delete VM: $host_vmx_path");
+ return;
+ }
+ }
+ }
+
+ # Get a list of the registered VMs
+ # Make sure the .vmx file actually exists for the registered VM
+ # A VM will remain in the registered list if the .vmx is deleted while it is registered
+ # Unregister any VMs which are missing .vmx files
+ my @registered_vmx_paths = $self->api->get_registered_vms();
+ for my $registered_vmx_path (@registered_vmx_paths) {
+ if (!$self->vmhost_os->file_exists($registered_vmx_path)) {
+ notify($ERRORS{'WARNING'}, 0, "vmx is registered but vmx file does not exist: $registered_vmx_path");
+ if (!$self->api->vm_unregister($registered_vmx_path)) {
+ if ($registered_vmx_path eq $vmx_file_path) {
+ notify($ERRORS{'WARNING'}, 0, "failed to unregister orphaned VM using the same vmx path as the VM for this reservation: $registered_vmx_path");
+ return;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to unregister orphaned VM using different vmx path than this reservation: $registered_vmx_path");
+ }
+ }
+ }
+ }
+
+ # Create the .vmx directory on the host
+ if (!$self->vmhost_os->create_directory($vmx_directory_path)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to create .vmx directory on VM host: $vmx_directory_path");
+ return;
+ }
+
+ # Set the disk parameters based on whether or not persistent mode is used
+ # Also set the display name to distinguish persistent and non-persistent VMs
+ my $display_name;
+ my $vm_disk_mode;
+ my $vm_disk_write_through;
+ if ($vm_persistent) {
+ $display_name = "$computer_name (persistent: $image_name)";
+ $vm_disk_mode = 'independent-persistent';
+ $vm_disk_write_through = "TRUE";
+ }
+ else {
+ $display_name = "$computer_name (nonpersistent: $image_name)";
+ $vm_disk_mode = "independent-nonpersistent";
+ #$vm_disk_mode = "undoable";
+ $vm_disk_write_through = "FALSE";
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "vm info:
+ image ID: $image_id
+ imagerevision ID: $imagerevision_id
+
+ vmx path: $vmx_file_path
+ vmx directory name: $vmx_directory_name
+ vmx directory path: $vmx_directory_path
+ vmdk file path: $vmdk_file_path
+ persistent: $vm_persistent
+ computer ID: $computer_id
+ computer name: $computer_name
+ image name: $image_name
+ guest OS: $guest_os
+ virtual hardware version: $vm_hardware_version
+ RAM: $vm_ram
+ CPU count: $vm_cpu_count
+
+ ethernet adapter type: $vm_ethernet_adapter_type
+
+ virtual switch 0: $virtual_switch_0
+ eth0 MAC address: $vm_eth0_mac
+ private IP address: $vm_private_ip_address
+
+ virtual switch 1: $virtual_switch_1
+ eth1 MAC address: $vm_eth1_mac
+ public IP address: $vm_public_ip_address
+
+ disk adapter type: $vm_disk_adapter_type
+ disk mode: $vm_disk_mode
+ disk write through: $vm_disk_write_through"
+ );
+
+ # Create a hash containing the vmx parameter names and values
+ my %vmx_parameters = (
+ "#image_id" => "$image_id",
+ "#imagerevision_id" => "$imagerevision_id",
+ "#computer_id" => "$computer_id",
+
+ ".encoding" => "UTF-8",
+
+ "config.version" => "8",
+
+ "displayName" => "$display_name",
+
+ "ethernet0.address" => "$vm_eth0_mac",
+ "ethernet0.addressType" => "static",
+ "ethernet0.allowGuestConnectionControl" => "FALSE",
+ "ethernet0.connectionType" => "custom",
+ "ethernet0.present" => "TRUE",
+ "ethernet0.virtualDev" => "$vm_ethernet_adapter_type",
+ "ethernet0.vnet" => "$virtual_switch_0",
+ "ethernet0.wakeOnPcktRcv" => "FALSE",
+
+ "ethernet1.address" => "$vm_eth1_mac",
+ "ethernet1.addressType" => "static",
+ "ethernet1.allowGuestConnectionControl" => "FALSE",
+ "ethernet1.connectionType" => "custom",
+ "ethernet1.present" => "TRUE",
+ "ethernet1.virtualDev" => "$vm_ethernet_adapter_type",
+ "ethernet1.vnet" => "$virtual_switch_1",
+ "ethernet1.wakeOnPcktRcv" => "FALSE",
+
+ "floppy0.present" => "FALSE",
+
+ "guestOS" => "$guest_os",
+
+ "gui.exitOnCLIHLT" => "TRUE", # causes the virtual machine to power off automatically when you choose Start > Shut Down from the Windows guest
+
+ "memsize" => "$vm_ram",
+
+ "msg.autoAnswer" => "TRUE", # tries to automatically answer all questions that may occur at boot-time.
+
+ "numvcpus" => "$vm_cpu_count",
+
+ "powerType.powerOff" => "soft",
+ "powerType.powerOn" => "hard",
+ "powerType.reset" => "soft",
+ "powerType.suspend" => "hard",
+
+ "snapshot.disabled" => "TRUE",
+
+ "tools.remindInstall" => "TRUE",
+ "tools.syncTime" => "FALSE",
+
+ "toolScripts.afterPowerOn" => "TRUE",
+ "toolScripts.afterResume" => "TRUE",
+ "toolScripts.beforeSuspend" => "TRUE",
+ "toolScripts.beforePowerOff" => "TRUE",
+
+ "uuid.action" => "keep", # Keep the VM's uuid, keeps existing MAC
+
+ "virtualHW.version" => "$vm_hardware_version",
+ );
+
+ # Add the disk adapter parameters to the hash
+ if ($vm_disk_adapter_type =~ /ide/i) {
+ %vmx_parameters = (%vmx_parameters, (
+ "ide0:0.fileName" => "$vmdk_file_path",
+ "ide0:0.mode" => "$vm_disk_mode",
+ "ide0:0.present" => "TRUE",
+ "ide0:0.writeThrough" => "$vm_disk_write_through",
+ ));
+ }
+ else {
+ %vmx_parameters = (%vmx_parameters, (
+ "scsi0.present" => "TRUE",
+ "scsi0.virtualDev" => "$vm_disk_adapter_type",
+ "scsi0:0.fileName" => "$vmdk_file_path",
+ "scsi0:0.mode" => "$vm_disk_mode",
+ "scsi0:0.present" => "TRUE",
+ "scsi0:0.writeThrough" => "$vm_disk_write_through",
+ ));
+ }
+
+ # Create a string from the hash
+ my $vmx_contents = "#!/usr/bin/vmware\n";
+ map { $vmx_contents .= "$_ = \"$vmx_parameters{$_}\"\n" } sort keys %vmx_parameters;
+
+ # Create a temporary vmx file on this managment node in /tmp
+ my $temp_vmx_file_path = "/tmp/$vmx_file_name";
+ if (open VMX_TEMP, ">", $temp_vmx_file_path) {
+ print VMX_TEMP $vmx_contents;
+ close VMX_TEMP;
+ notify($ERRORS{'DEBUG'}, 0, "created temporary vmx file: $temp_vmx_file_path");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to create temporary vmx file: $temp_vmx_file_path, error: @!");
+ return;
+ }
+
+ # Copy the temporary vmx file the the VM host
+ $self->vmhost_os->copy_file_to($temp_vmx_file_path, $vmx_file_path) || return;
+ notify($ERRORS{'OK'}, 0, "created vmx file on VM host: $vmx_file_path");
+
+ # Delete the temporary vmx file
+ if (unlink $temp_vmx_file_path) {
+ notify($ERRORS{'DEBUG'}, 0, "deleted temporary vmx file: $temp_vmx_file_path");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to delete temporary vmx file: $temp_vmx_file_path, error: $!");
+ }
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 prepare_vmdk
+
+ Parameters : none
+ Returns : boolean
+ Description : Prepares the .vmdk files on the VM host. This subroutine
+ determines whether or not the vmdk files need to be copied to the
+ VM host.
+
+=cut
+
+sub prepare_vmdk {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $host_vmdk_directory_path = $self->get_vmdk_directory_path() || return;
+ my $repository_vmdk_directory_path = $self->get_repository_vmdk_directory_path() || return;
+ my $image_name = $self->data->get_image_name() || return;
+ my $vmhost_hostname = $self->data->get_vmhost_hostname() || return;
+
+ # Check if the first .vmdk file exists on the host
+ my $host_vmdk_file_path = $self->get_vmdk_file_path() || return;
+ if ($self->vmhost_os->file_exists($host_vmdk_file_path)) {
+ notify($ERRORS{'DEBUG'}, 0, ".vmdk file exists on VM host: $host_vmdk_file_path");
+ return $self->check_vmdk_disk_type();
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, ".vmdk file does NOT exist on VM host: $host_vmdk_file_path");
+ }
+
+ ## Figure out how much additional space is required for the vmdk directory for the VM for this reservation
+ ## This is the number of additional bytes which have not already been allocated the VM will likely use
+ ## The subroutine checks if the vmdk files already exist on the VM host
+ #my $vm_additional_vmdk_bytes_required = $self->get_vm_additional_vmdk_bytes_required();
+ #return if !defined($vm_additional_vmdk_bytes_required);
+ #
+ ## Get the number of bytes available on the device where the base vmdk directory resides
+ #my $host_vmdk_bytes_available = $self->vmhost_os->get_available_space($self->get_vmdk_base_directory_path());
+ #return if !defined($host_vmdk_bytes_available);
+ #
+ ## Check if there is enough space available for the VM's vmdk files
+ #if ($vm_additional_vmdk_bytes_required > $host_vmdk_bytes_available) {
+ # my $vmdk_deficit_bytes = ($vm_additional_vmdk_bytes_required - $host_vmdk_bytes_available);
+ # my $vmdk_deficit_mb = format_number($vmdk_deficit_bytes / 1024 / 1024);
+ # notify($ERRORS{'WARNING'}, 0, "not enough space is available for the vmdk files on the VM host, deficit: $vmdk_deficit_bytes bytes ($vmdk_deficit_mb MB)");
+ # return;
+ #}
+ #else {
+ # notify($ERRORS{'DEBUG'}, 0, "enough space is available for the vmdk files on the VM host");
+ #}
+
+ # Check if the VM is persistent, if so, attempt to copy files locally from the nonpersistent directory if they exist
+ if ($self->is_vm_persistent()) {
+ my $host_vmdk_directory_path_nonpersistent = $self->get_vmdk_directory_path_nonpersistent() || return;
+
+ if (my @vmdk_nonpersistent_file_paths = $self->vmhost_os->find_files($host_vmdk_directory_path_nonpersistent, '*.vmdk')) {
+ my $start_time = time;
+
+ # Loop through the files, copy each file from the non-persistent directory to the persistent directory
+ for my $vmdk_nonpersistent_file_path (sort @vmdk_nonpersistent_file_paths) {
+ # Extract the file name from the path
+ my ($vmdk_copy_file_name) = $vmdk_nonpersistent_file_path =~ /([^\/]+)$/g;
+
+ # Attempt to copy the file on the VM host
+ if (!$self->vmhost_os->copy_file($vmdk_nonpersistent_file_path, "$host_vmdk_directory_path/$vmdk_copy_file_name")) {
+ notify($ERRORS{'WARNING'}, 0, "failed to copy vmdk file from the non-persistent to the persistent directory on the VM host:\n'$vmdk_nonpersistent_file_path' --> '$host_vmdk_directory_path/$vmdk_copy_file_name'");
+ return;
+ }
+ }
+
+ # All vmdk files were copied
+ my $duration = (time - $start_time);
+ notify($ERRORS{'OK'}, 0, "copied vmdk files from nonpersistent to persistent directory on VM host, took " . format_number($duration) . " seconds");
+ return $self->check_vmdk_disk_type();
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "non-persistent set of vmdk files does not exist: '$host_vmdk_directory_path_nonpersistent'");
+ }
+ }
+
+ # VM is either non-persistent or persistent and could not copy files from existing non-persistent directory
+ # Copy the vmdk files from the image repository to the vmdk directory
+ my $start_time = time;
+
+ # Find the vmdk file paths in the image repository directory
+ my @vmdk_repository_file_paths;
+ my $command = "find \"$repository_vmdk_directory_path\" -type f -iname \"*.vmdk\"";
+ my ($exit_status, $output) = run_command($command, 1);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to run command to find files in image repository directory: '$repository_vmdk_directory_path', pattern: '*.vmdk', command:\n$command");
+ return;
+ }
+ elsif (grep(/(^find:.*no such file)/i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "directory does not exist in image repository: '$repository_vmdk_directory_path'");
+ return;
+ }
+ elsif (grep(/(^find: |syntax error|unexpected EOF)/i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "error occurred attempting to find files in image repository directory: '$repository_vmdk_directory_path', pattern: '*.vmdk', command: $command, output:\n" . join("\n", @$output));
+ return;
+ }
+ else {
+ @vmdk_repository_file_paths = @$output;
+ map { chomp $_ } @vmdk_repository_file_paths;
+ notify($ERRORS{'DEBUG'}, 0, "found " . scalar(@vmdk_repository_file_paths) . " vmdk files in image repository directory: '$repository_vmdk_directory_path':\n" . join("\n", sort @vmdk_repository_file_paths));
+ }
+
+ # Loop through the files, copy each from the management node's repository directory to the VM host
+ for my $vmdk_repository_file_path (sort @vmdk_repository_file_paths) {
+ my ($vmdk_copy_name) = $vmdk_repository_file_path =~ /([^\/]+)$/g;
+ if (!$self->vmhost_os->copy_file_to($vmdk_repository_file_path, "$host_vmdk_directory_path/$vmdk_copy_name")) {
+ notify($ERRORS{'WARNING'}, 0, "failed to copy vmdk file from the repository to the VM host: '$vmdk_repository_file_path' --> '$host_vmdk_directory_path/$vmdk_copy_name'");
+ return;
+ }
+ }
+ my $duration = (time - $start_time);
+ notify($ERRORS{'OK'}, 0, "copied .vmdk files from management node image repository to the VM host, took " . format_number($duration) . " seconds");
+
+ return $self->check_vmdk_disk_type();
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 check_vmdk_disk_type
+
+ Parameters : none
+ Returns : boolean
+ Description : Determines if the vmdk disk type is compatible with the VMware
+ product being used on the VM host. This subroutine currently only
+ checks if ESX is being used and the vmdk disk type is flat. If
+ using ESX and the disk type is not flat, a copy of the vmdk is
+ created using the thin virtual disk type in the same directory as
+ the incompatible vmdk directory. The name of the copied vmdk file
+ is the same as the incompatible vmdk file with '-thin' inserted
+ before '.vmdk'. Example:
+ 'vmwarewinxp-base1-v0.vmdk' --> 'vmwarewinxp-base1-v0-thin.vmdk'
+
+ This subroutine returns true unless ESX is being used, the
+ virtual disk type is not flat, and a thin copy cannot be created.
+
+=cut
+
+sub check_vmdk_disk_type {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmdk_file_path = $self->get_vmdk_file_path() || return;
+
+ # Check if the API object implements the required subroutines
+ unless ($self->api->can("get_vmware_product_name")
+ && $self->api->can("get_virtual_disk_type")
+ && $self->api->can("copy_virtual_disk")
+ && $self->api->can("get_virtual_disk_controller_type")) {
+ notify($ERRORS{'DEBUG'}, 0, "skipping vmdk disk type check because required subroutines are not implemented by the API object");
+ return 1;
+ }
+
+ # Retrieve the VMware product name and virtual disk type from the API object
+ my $vmware_product_name = $self->api->get_vmware_product_name();
+ if (!$vmware_product_name) {
+ notify($ERRORS{'DEBUG'}, 0, "skipping vmdk disk type check because VMware product name could not be retrieved from the API object");
+ return 1;
+ }
+ my $virtual_disk_type = $self->api->get_virtual_disk_type($vmdk_file_path);
+ if (!$vmware_product_name) {
+ notify($ERRORS{'DEBUG'}, 0, "skipping vmdk disk type check because virtual disk type could not be retrieved from the API object");
+ return 1;
+ }
+
+ if ($vmware_product_name =~ /esx/i) {
+ if ($virtual_disk_type !~ /flat/i) {
+ notify($ERRORS{'DEBUG'}, 0, "virtual disk type is NOT compatible with $vmware_product_name: $virtual_disk_type");
+
+ my $vmdk_file_path = $self->get_vmdk_file_path() || return;
+ my $vmdk_directory_path = $self->get_vmdk_directory_path() || return;
+ my $vmdk_file_prefix = $self->get_vmdk_file_prefix() || return;
+ my $thin_vmdk_file_path = "$vmdk_directory_path/$vmdk_file_prefix-thin.vmdk";
+
+ if ($self->vmhost_os->file_exists($thin_vmdk_file_path)) {
+ notify($ERRORS{'DEBUG'}, 0, "thin virtual disk already exists: $thin_vmdk_file_path");
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "attempting to create a copy of the virtual disk using the thin virtual disk type: $thin_vmdk_file_path");
+
+ # Get the controller type from the incompatible vmdk file, use it to create the copy
+ my $virtual_disk_controller_type = $self->api->get_virtual_disk_controller_type($vmdk_file_path);
+ if (!$virtual_disk_controller_type) {
+ notify($ERRORS{'WARNING'}, 0, "unable to create a copy of the virtual disk using the thin virtual disk type because the controller type used by the original vmdk file cannot be retrieved");
+ return;
+ }
+
+ # Attempt to create a thin copy of the virtual disk
+ if ($self->api->copy_virtual_disk($vmdk_file_path, $thin_vmdk_file_path, $virtual_disk_controller_type, 'thin')) {
+ notify($ERRORS{'DEBUG'}, 0, "created a copy of the virtual disk using the thin virtual disk type: $thin_vmdk_file_path");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to create a copy of the virtual disk using the thin virtual disk type: $thin_vmdk_file_path");
+ return;
+ }
+ }
+
+ # Update this object to use the thin vmdk file path
+ if ($self->set_vmdk_file_path($thin_vmdk_file_path)) {
+ return 1;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to update the VMware module object to use the thin virtual disk path");
+ return;
+ }
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "virtual disk does not need to be converted for $vmware_product_name: $virtual_disk_type");
+ return 1;
+ }
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "skipping vmdk disk type check because VMware product is not ESX: $vmware_product_name");
+ return 1;
+ }
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmx_base_directory_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the path on the VM host under which the vmx directory is
+ located. Example:
+ vmx file path: /vmfs/volumes/nfs-vmpath/vm1-6-987-v0/vm1-6-987-v0.vmx
+ vmx base directory path: /vmfs/volumes/nfs-vmpath
+
+=cut
+
+sub get_vmx_base_directory_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmx_base_directory_path;
+
+ my $vmhost_profile_vmpath = $self->data->get_vmhost_profile_vmpath();
+ if ($vmhost_profile_vmpath) {
+ $vmhost_profile_vmpath =~ s/\\//g;
+ $vmx_base_directory_path = $vmhost_profile_vmpath;
+ }
+ else {
+ my $vmhost_profile_datastore_path = $self->data->get_vmhost_profile_datastore_path();
+ if ($vmhost_profile_datastore_path) {
+ $vmhost_profile_datastore_path =~ s/\\//g;
+ $vmx_base_directory_path = $vmhost_profile_datastore_path;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine the vmdk base directory, could not determine VM path or datastore path from the database");
+ return;
+ }
+ }
+
+ # Remove any trailing slashes
+ $vmx_base_directory_path =~ s/\/$//g;
+ return $vmx_base_directory_path;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmx_directory_name
+
+ Parameters : none
+ Returns : string
+ Description : Returns the name of the directory in which the .vmx file is
+ located. The name differs depending on whether or not the VM
+ is persistent.
+ If not persistent: <computer name>_<image ID>-<revision>
+ If persistent: <computer name>_<image ID>-<revision>_<request ID>
+
+=cut
+
+sub get_vmx_directory_name {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmx_directory_name;
+
+ if ($ENV{vmx_file_path}) {
+ my $vmx_base_directory_path = $self->get_vmx_base_directory_path() || return;
+ ($vmx_directory_name) = $ENV{vmx_file_path} =~ /^$vmx_base_directory_path\/(.+)\/[^\/]+.vmx$/;
+ return $vmx_directory_name;
+ }
+
+ # Get the computer name, image ID, and revision number
+ my $computer_short_name = $self->data->get_computer_short_name();
+ if (!$computer_short_name) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve computer short name");
+ return;
+ }
+ my $image_id = $self->data->get_image_id();
+ if (!defined($image_id)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve image ID");
+ return;
+ }
+ my $imagerevision_revision = $self->data->get_imagerevision_revision();
+ if (!defined($imagerevision_revision)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve imagerevision revision");
+ return;
+ }
+
+ # Assemble the directory name
+ $vmx_directory_name = "$computer_short_name\_$image_id-v$imagerevision_revision";
+
+ # If persistent, append the request ID
+ if ($self->is_vm_persistent()) {
+ my $request_id = $self->data->get_request_id();
+ if (!defined($request_id)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve request ID");
+ return;
+ }
+ $vmx_directory_name .= "\_$request_id";
+ }
+
+ return $vmx_directory_name;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmx_directory_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the path on the VM host under which the vmx file is
+ located. Example:
+ vmx file path: /vmfs/volumes/nfs-vmpath/vm1-6-987-v0/vm1-6-987-v0.vmx
+ vmx directory path: /vmfs/volumes/nfs-vmpath/vm1-6-987-v0
+
+=cut
+
+sub get_vmx_directory_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmx_base_directory_path = $self->get_vmx_base_directory_path() || return;
+ my $vmx_directory_name = $self->get_vmx_directory_name() || return;
+
+ return "$vmx_base_directory_path/$vmx_directory_name";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmx_file_name
+
+ Parameters : none
+ Returns : string
+ Description : Returns the name of the .vmx file. Example:
+ vmx file path: /vmfs/volumes/nfs-vmpath/vm1-6-987-v0/vm1-6-987-v0.vmx
+ vmx file name: vm1-6-987-v0.vmx
+
+=cut
+
+sub get_vmx_file_name {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ if ($ENV{vmx_file_path}) {
+ my ($vmx_file_name) = $ENV{vmx_file_path} =~ /([^\/]+.vmx)$/g;
+ }
+
+ my $vmx_directory_name = $self->get_vmx_directory_name() || return;
+ return "$vmx_directory_name.vmx";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmx_file_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the path to the .vmx file. Example:
+ vmx file path: /vmfs/volumes/nfs-vmpath/vm1-6-987-v0/vm1-6-987-v0.vmx
+
+=cut
+
+sub get_vmx_file_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ return $ENV{vmx_file_path} if $ENV{vmx_file_path};
+
+ my $vmx_directory_path = $self->get_vmx_directory_path() || return;
+ my $vmx_file_name = $self->get_vmx_file_name() || return;
+ return "$vmx_directory_path/$vmx_file_name";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_base_directory_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the directory path under which the directories which
+ store the .vmdk files are located. Example:
+ vmdk file path: /vmfs/volumes/nfs-datastore/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
+ vmdk base directory path: /vmfs/volumes/nfs-datastore
+
+=cut
+
+sub get_vmdk_base_directory_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Check if $ENV{vmdk_file_path} is set, parse this path if it is set
+ if (my $vmdk_file_path = $ENV{vmdk_file_path}) {
+ my ($vmdk_base_directory_path) = $vmdk_file_path =~ /^(.+)\/[^\/]+\/[^\/]+\.vmdk$/g;
+ if (!$vmdk_base_directory_path) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk base directory path from vmdk file path: $vmdk_file_path");
+ return;
+ }
+ return $vmdk_base_directory_path;
+ }
+ else {
+ # Get the VM host profile datastore path
+ my $vmhost_profile_datastore_path = $self->data->get_vmhost_profile_datastore_path();
+ if (!$vmhost_profile_datastore_path) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve VM host profile datastore path");
+ return;
+ }
+ return $vmhost_profile_datastore_path;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_directory_name
+
+ Parameters : none
+ Returns : string
+ Description : Returns the name of the directory under which the .vmdk files
+ are located. The name differs depending on whether or not the
+ VM is persistent.
+ If not persistent: <image name>
+ If persistent: <computer name>_<image ID>-<revision>_<request ID>
+ Example:
+ vmdk directory path persistent: vmwarewinxp-base234-v12
+ vmdk directory path non-persistent: vm1-6_987-v0_5435
+
+=cut
+
+sub get_vmdk_directory_name {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ if ($self->is_vm_persistent()) {
+ return $self->get_vmdk_directory_name_persistent();
+ }
+ else {
+ return $self->get_vmdk_directory_name_nonpersistent();
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_directory_name_persistent
+
+ Parameters : none
+ Returns : string
+ Description : Returns the name of the directory under which the .vmdk files
+ are located if the VM is persistent:
+ <computer name>_<image ID>-<revision>_<request ID>
+
+=cut
+
+sub get_vmdk_directory_name_persistent {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ if ($ENV{vmdk_file_path}) {
+ my $vmdk_base_directory_path = $self->get_vmdk_base_directory_path() || return;
+ my ($vmdk_directory_name) = $ENV{vmdk_file_path} =~ /^$vmdk_base_directory_path\/(.+)\/[^\/]+.vmdk$/;
+ if ($vmdk_directory_name) {
+ return $vmdk_directory_name;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unable to parse vmdk directory name from vmdk file path: $ENV{vmdk_file_path}");
+ return;
+ }
+ }
+
+ return $self->get_vmx_directory_name();
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_directory_name_nonpersistent
+
+ Parameters : none
+ Returns : string
+ Description : Returns the name of the directory under which the .vmdk files
+ are located if the VM is not persistent:
+ <image name>
+
+=cut
+
+sub get_vmdk_directory_name_nonpersistent {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ if ($ENV{vmdk_file_path}) {
+ my $vmdk_base_directory_path = $self->get_vmdk_base_directory_path() || return;
+ my ($vmdk_directory_name) = $ENV{vmdk_file_path} =~ /^$vmdk_base_directory_path\/(.+)\/[^\/]+.vmdk$/;
+
+ if ($vmdk_directory_name) {
+ return $vmdk_directory_name;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unable to parse vmdk directory name from vmdk file path: $ENV{vmdk_file_path}");
+ return;
+ }
+ }
+
+ my $image_name = $self->data->get_image_name();
+ if (!$image_name) {
+ notify($ERRORS{'WARNING'}, 0, "unable determine vmdk directory name because unable to retrieve image name");
+ return;
+ }
+ return $image_name;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_directory_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the directory path under which the .vmdk files are
+ located. Example:
+ vmdk file path: /vmfs/volumes/nfs-datastore/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
+ vmdk directory path: /vmfs/volumes/nfs-datastore/vmwarewinxp-base234-v12
+
+=cut
+
+sub get_vmdk_directory_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmdk_base_directory_path = $self->get_vmdk_base_directory_path();
+ if (!$vmdk_base_directory_path) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk directory path because vmdk base directory path could not be determined");
+ return;
+ }
+
+ my $vmdk_directory_name = $self->get_vmdk_directory_name() || return;
+ if (!$vmdk_directory_name) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk directory path because vmdk directory name could not be determined");
+ return;
+ }
+
+ return "$vmdk_base_directory_path/$vmdk_directory_name";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_directory_path_nonpersistent
+
+ Parameters : none
+ Returns : string
+ Description : Returns the directory path under which the .vmdk files are
+ located for nonpersistent VMs.
+
+=cut
+
+sub get_vmdk_directory_path_nonpersistent {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmdk_base_directory_path = $self->get_vmdk_base_directory_path() || return;
+ if (!$vmdk_base_directory_path) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine nonpersistent vmdk directory path because vmdk base directory path could not be determined");
+ return;
+ }
+
+ my $vmdk_directory_name = $self->get_vmdk_directory_name_nonpersistent() || return;
+ if (!$vmdk_directory_name) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine nonpersistent vmdk directory path because nonpersistent vmdk directory name could not be determined");
+ return;
+ }
+
+ return "$vmdk_base_directory_path/$vmdk_directory_name";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_file_prefix
+
+ Parameters : none
+ Returns : string
+ Description : Returns the name of the base .vmdk file without the trailing
+ .vmdk. Example:
+ vmdk file path: /vmfs/volumes/nfs-datastore/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
+ vmdk file prefix: vmwarewinxp-base234-v12
+
+=cut
+
+sub get_vmdk_file_prefix {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ if ($ENV{vmdk_file_path}) {
+ my ($vmdk_file_prefix) = $ENV{vmdk_file_path} =~ /([^\/]+).vmdk$/g;
+ return $vmdk_file_prefix;
+ }
+
+ my $image_name = $self->data->get_image_name() || return;
+ return $image_name;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_file_name
+
+ Parameters : none
+ Returns : string
+ Description : Returns the name of the base .vmdk file including .vmdk. Example:
+ vmdk file path: /vmfs/volumes/nfs-datastore/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
+ vmdk file name: vmwarewinxp-base234-v12.vmdk
+
+=cut
+
+sub get_vmdk_file_name {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmdk_file_prefix = $self->get_vmdk_file_prefix() || return;
+ if (!$vmdk_file_prefix) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk file name because vmdk file prefix could not be determined");
+ return;
+ }
+
+ return "$vmdk_file_prefix.vmdk";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_file_path_nonpersistent
+
+ Parameters : none
+ Returns : string
+ Description : Returns the vmdk file path for a nonpersistent VM. This is
+ useful when checking the image size on a VM host using
+ network-based disks. It returns the vmdk file path that would be
+ used for nonperistent VMs.
+
+=cut
+
+sub get_vmdk_file_path_nonpersistent {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmdk_directory_path_nonpersistent = $self->get_vmdk_directory_path_nonpersistent() || return;
+ my $vmdk_file_name = $self->get_vmdk_file_name() || return;
+ return "$vmdk_directory_path_nonpersistent/$vmdk_file_name";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_file_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the path of the vmdk file. Example:
+ vmdk file path: /vmfs/volumes/nfs-datastore/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
+
+=cut
+
+sub get_vmdk_file_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmdk_directory_path = $self->get_vmdk_directory_path() || return;
+ my $vmdk_file_name = $self->get_vmdk_file_name() || return;
+ return "$vmdk_directory_path/$vmdk_file_name";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_repository_vmdk_base_directory_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the image repository directory path on the management
+ node under which the vmdk directories for all of the images
+ reside. Example:
+ repository vmdk file path: /install/vmware_images/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
+ repository vmdk base directory path: /install/vmware_images
+
+
+=cut
+
+sub get_repository_vmdk_base_directory_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Return the path stored in this object if it has already been determined
+ return $self->{repository_base_directory} if (defined $self->{repository_base_directory});
+
+ my $repository_vmdk_base_directory;
+ # Return $VMWAREREPOSITORY if it's set (comes from VMWARE_IMAGEREPOSITORY value in vcld.conf)
+ if ($VMWAREREPOSITORY) {
+ $repository_vmdk_base_directory = $VMWAREREPOSITORY;
+ notify($ERRORS{'DEBUG'}, 0, "using VMWARE_IMAGEREPOSITORY value from vcld.conf: $repository_vmdk_base_directory");
+ }
+ elsif (my $management_node_install_path = $self->data->get_management_node_install_path()) {
+ $repository_vmdk_base_directory = "$management_node_install_path/vmware_images";
+ notify($ERRORS{'DEBUG'}, 0, "using managementnode installpath database value: $repository_vmdk_base_directory");
+ }
+ else {
+ $repository_vmdk_base_directory = "install/vmware_images";
+ notify($ERRORS{'DEBUG'}, 0, "using hard-coded path: $repository_vmdk_base_directory");
+ }
+
+ # Set a value in this object so this doesn't have to be figured out more than once
+ $self->{repository_base_directory} = $repository_vmdk_base_directory;
+ return $repository_vmdk_base_directory;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_repository_vmdk_directory_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the image repository directory path on the management
+ node under which the vmdk files reside. Example:
+ repository vmdk file path: /install/vmware_images/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
+ repository vmdk directory path: /install/vmware_images/vmwarewinxp-base234-v12
+
+=cut
+
+sub get_repository_vmdk_directory_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $repository_vmdk_base_directory = $self->get_repository_vmdk_base_directory_path() || return;
+ my $image_name = $self->data->get_image_name() || return;
+ return "$repository_vmdk_base_directory/$image_name";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_repository_vmdk_file_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the image repository vmdk file path on the management
+ node. Example:
+ repository vmdk file path: /install/vmware_images/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
+
+=cut
+
+sub get_repository_vmdk_file_path {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $repository_vmdk_directory_path = $self->get_repository_vmdk_directory_path() || return;
+ my $vmdk_file_name = $self->get_vmdk_file_name() || return;
+ return "$repository_vmdk_directory_path/$vmdk_file_name";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 is_vm_persistent
+
+ Parameters : none
+ Returns : boolean
+ Description : Determines if a VM should be persistent or not based on whether
+ or not the reservation is an imaging reservation.
+
+=cut
+
+sub is_vm_persistent {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $request_forimaging = $self->data->get_request_forimaging();
+ if ($request_forimaging) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 is_vm_registered
+
+ Parameters : $vmx_file_path (optional)
+ Returns : boolean
+ Description : Determines if a VM is registered. An optional vmx file path
+ argument can be supplied to check if a particular VM is
+ registered. If an argument is not specified, the default vmx file
+ path for the reservation is used.
+
+=cut
+
+sub is_vm_registered {
+ my $self = shift;
+ if (ref($self) !~ /vmware/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
+ # Use the argument if one was supplied
+ my $vmx_file_path = shift || $self->get_vmx_file_path() || return;
+
+ my @registered_vmx_file_paths = $self->api->get_registered_vms();
+
+ if (grep(/$vmx_file_path/, @registered_vmx_file_paths)) {
+ notify($ERRORS{'DEBUG'}, 0, "vmx file is registered: $vmx_file_path");
+ return 1;
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "vmx file is not registered: $vmx_file_path");
+ return 0;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_image_size
+
+ Parameters : $vmdk_file_path (optional)
+ Returns : integer
+ Description : Returns the size of the image in bytes. If the vmdk file path
+ argument is not supplied and the VM disk type in the VM profile
+ is set to localdisk, the size of the image in the image
+ repository on the management node is checked. Otherwise, the size
+ of the image in the vmdk directory on the VM host is checked.
+
+=cut
+
+sub get_image_size {
+ my $self = shift;
+ if (ref($self) !~ /vmware/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() || return;
+ my $vmprofile_vmdisk = $self->data->get_vmhost_profile_vmdisk() || return;
+
+ # Attempt to get the vmdk file path argument
+ # If not supplied, use the default vmdk file path for this reservation
+ my $vmdk_file_path = shift;
+
+ # Try to retrieve the image size from the repository if an argument was not supplied and localdisk is being used
+ if (!$vmdk_file_path && $vmprofile_vmdisk eq "localdisk") {
+ my $repository_vmdk_directory_path = $self->get_repository_vmdk_directory_path() || return;
+ notify($ERRORS{'DEBUG'}, 0, "vm disk type is $vmprofile_vmdisk, checking size of vmdk directory in image repository: $repository_vmdk_directory_path");
+
+ # Run du specifying image repository directory as an argument
+ my ($exit_status, $output) = run_command("du -bc \"$repository_vmdk_directory_path\"", 1);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to run command to determine size of vmdk directory in image repository: $repository_vmdk_directory_path");
+ }
+ elsif (grep(/du: /i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "error occurred attempting to determine size of vmdk directory in image repository: $repository_vmdk_directory_path, output:\n" . join("\n", @$output));
+ }
+ else {
+ my ($total_line) = grep(/total/, @$output);
+ if (!$total_line) {
+ notify($ERRORS{'WARNING'}, 0, "unable to locate 'total' line in du output while attempting to determine size of vmdk directory in image repository: $repository_vmdk_directory_path, output:\n" . join("\n", @$output));
+ }
+ else {
+ my ($bytes_used) = $total_line =~ /(\d+)/;
+ if ($bytes_used =~ /^\d+$/) {
+ my $mb_used = format_number(($bytes_used / 1024 / 1024), 1);
+ my $gb_used = format_number(($bytes_used / 1024 / 1024 / 1024), 2);
+ notify($ERRORS{'DEBUG'}, 0, "size of vmdk directory in image repository $repository_vmdk_directory_path: " . format_number($bytes_used) . " bytes ($mb_used MB, $gb_used GB)");
+ return $bytes_used;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to parse du output to determine size of vmdk directory in image repository: $repository_vmdk_directory_path, output:\n" . join("\n", @$output));
+ return;
+ }
+ }
+ }
+ }
+
+ # Get the vmdk file path if not specified as an argument
+ if (!$vmdk_file_path) {
+ $vmdk_file_path = $self->get_vmdk_file_path() || return;
+ }
+
+ # Extract the directory path and file prefix from the vmdk file path
+ my ($vmdk_directory_path, $vmdk_file_prefix) = $vmdk_file_path =~ /^(.+)\/([^\/]+)\.vmdk$/;
+ if (!$vmdk_directory_path || !$vmdk_file_prefix) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk directory path and vmdk file prefix from vmdk file path: $vmdk_file_path");
+ return;
+ }
+
+ # Assemble a search path
+ my $vmdk_search_path = "$vmdk_directory_path/$vmdk_file_prefix*.vmdk";
+
+ # Get the size of the files on the VM host
+ my $vmdk_size = $self->vmhost_os->get_file_size($vmdk_search_path);
+ if (!defined($vmdk_size)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine the size of vmdk file on VM host $vmhost_hostname:
+ vmdk file path: $vmdk_file_path
+ search path: $vmdk_search_path");
+ return;
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "size of vmdk file on VM host $vmhost_hostname $vmdk_file_path: " . format_number($vmdk_size) . " bytes");
+ return $vmdk_size;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 does_image_exist
+
+ Parameters : none
+ Returns : boolean
+ Description : Determines if an image exists in either the management node's
+ image repository or on the VM host depending on the VM profile
+ disk type setting. If the VM disk type in the VM profile is set
+ to localdisk, the image repository on the management node is
+ checked. Otherwise, the vmdk directory on the VM host is checked.
+
+=cut
+
+sub does_image_exist {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmprofile_vmdisk = $self->data->get_vmhost_profile_vmdisk() || return;
+
+ # Check if the VM host is using local or network-based disk
+ if ($vmprofile_vmdisk eq "localdisk") {
+ # Local disk - check size of directory in image repository
+ my $repository_vmdk_file_path = $self->get_repository_vmdk_file_path() || return;
+ notify($ERRORS{'DEBUG'}, 0, "vm disk type is $vmprofile_vmdisk, checking if vmdk file exists in image repository: $repository_vmdk_file_path");
+
+ # Remove any trailing slashes and separate the directory path and name pattern
+ $repository_vmdk_file_path =~ s/\/*$//g;
+ my ($directory_path, $name_pattern) = $repository_vmdk_file_path =~ /^(.*)\/([^\/]*)/g;
+
+ # Check if the file or directory exists
+ (my ($exit_status, $output) = run_command("find \"$directory_path\" -iname \"$name_pattern\"")) || return;
+ if (!grep(/find: /i, @$output) && grep(/$directory_path/i, @$output)) {
+ notify($ERRORS{'DEBUG'}, 0, "file exists in repository: $repository_vmdk_file_path");
+ return 1;
+ }
+ elsif (grep(/find: /i, @$output) && !grep(/no such file/i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to determine if file exists in repository: $repository_vmdk_file_path, output:\n" . join("\n", @$output));
+ return;
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "file does NOT exist in image repository: $repository_vmdk_file_path");
+ return 0;
+ }
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "vm disk type is $vmprofile_vmdisk, checking if file exists on VM host");
+ return $self->vmhost_os->file_exists($self->get_vmdk_file_path_nonpersistent());
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vmdk_parameter_value
+
+ Parameters : $vmdk_parameter
+ Returns : string
+ Description : Opens the .vmdk file, searches for the parameter argument, and
+ returns the value for the parameter. Example:
+ vmdk file contains: ddb.adapterType = "buslogic"
+ get_vmdk_parameter_value('adapterType') returns buslogic
+
+=cut
+
+sub get_vmdk_parameter_value {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Get the .vmdk parameter argument to search for
+ my $vmdk_parameter = shift;
+ if (!$vmdk_parameter) {
+ notify($ERRORS{'WARNING'}, 0, "vmdk parameter name argument was not specified");
+ return;
+ }
+
+ my $image_repository_vmdk_file_path = $self->get_repository_vmdk_file_path() || return;
+ notify($ERRORS{'DEBUG'}, 0, "attempting to locate $vmdk_parameter value in $image_repository_vmdk_file_path");
+
+ # Open the vmdk file for reading
+ if (open FILE, "<", $image_repository_vmdk_file_path) {
+ notify($ERRORS{'DEBUG'}, 0, "opened vmdk file for reading: $image_repository_vmdk_file_path");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unable to open vmdk file for reading: $image_repository_vmdk_file_path");
+ return;
+ }
+
+ # Read the file line by line - do not read the file all at once
+ # The vmdk file may be very large depending on the type - it may not be split up into a descriptor file and extents
+ # If the vmdk file isn't split, the descriptor section will be at the beginning
+ my $line_count = 0;
+ my $value;
+ while ($line_count < 100) {
+ $line_count++;
+ my $line = <FILE>;
+ chomp $line;
+
+ # Ignore comment lines
+ next if ($line =~ /^\s*#/);
+
+ # Check if the line contains the parameter name
+ if ($line =~ /(^|\.)$vmdk_parameter[\s=]+/i) {
+ notify($ERRORS{'DEBUG'}, 0, "found line containing $vmdk_parameter: '$line'");
+
+ # Extract the value from the line
+ ($value) = $line =~ /\"(.+)\"/;
+ last;
+ }
+ }
+
+ close FILE;
+
+ if (defined($value)) {
+ notify($ERRORS{'DEBUG'}, 0, "found $vmdk_parameter value in vmdk file: '$value'");
+ return $value;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "did not find $vmdk_parameter value in vmdk file");
+ return;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vm_disk_adapter_type
+
+ Parameters : none
+ Returns : string
+ Description : Returns the adapterType value in the vmdk file. Possible return
+ values:
+ -ide
+ -lsilogic
+ -buslogic
+
+=cut
+
+sub get_vm_disk_adapter_type {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ if ($self->api->can("get_virtual_disk_controller_type")) {
+ return $self->api->get_virtual_disk_controller_type($self->get_vmdk_file_path());
+ }
+ else {
+ return $self->get_vmdk_parameter_value('adapterType');
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vm_virtual_hardware_version
+
+ Parameters : none
+ Returns : string
+ Description : Returns the virtualHWVersion value in the vmdk file.
+
+=cut
+
+sub get_vm_virtual_hardware_version {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ if ($self->api->can("get_virtual_disk_hardware_version")) {
+ return $self->api->get_virtual_disk_hardware_version($self->get_vmdk_file_path());
+ }
+ else {
+ return $self->get_vmdk_parameter_value('virtualHWVersion');
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vm_os_configuration
+
+ Parameters : none
+ Returns : hash
+ Description : Returns the information stored in %VM_OS_CONFIGURATION for
+ the guest OS. The guest OS type, OS name, and archictecture are
+ used to determine the appropriate guestOS and ethernet-virtualDev
+ values to be used in the vmx file.
+
+=cut
+
+sub get_vm_os_configuration {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Return previously retrieved data if it exists
+ return $self->{vm_os_configuration} if $self->{vm_os_configuration};
+
+ my $image_os_type = $self->data->get_image_os_type() || return;
+ my $image_os_name = $self->data->get_image_os_name() || return;
+ my $image_architecture = $self->data->get_image_architecture() || return;
+
+ # Figure out the key name in the %VM_OS_CONFIGURATION hash for the guest OS
+ my $vm_os_configuration_key;
+ if ($image_os_type =~ /linux/i) {
+ $vm_os_configuration_key = "linux-$image_architecture";
+ }
+ elsif ($image_os_type =~ /windows/i) {
+ my $regex = 'xp|2003|2008|vista|7';
+ $image_os_name =~ /($regex)/i;
+ my $windows_product = $1;
+ if (!$windows_product) {
+ notify($ERRORS{'WARNING'}, 0, "unsupported Windows product: $image_os_name, it does not contain ($regex), using default values for Windows");
+ $windows_product = 'windows';
+ }
+ $vm_os_configuration_key = "$windows_product-$image_architecture";
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unsupported OS type: $image_os_type, using default values");
+ $vm_os_configuration_key = "default-$image_architecture";
+ }
+
+ # Retrieve the information from the hash, set an object variable
+ $self->{vm_os_configuration} = $VM_OS_CONFIGURATION{$vm_os_configuration_key};
+ if ($self->{vm_os_configuration}) {
+ notify($ERRORS{'DEBUG'}, 0, "found vm OS configuration ($vm_os_configuration_key):\n" . format_data($self->{vm_os_configuration}));
+ return $self->{vm_os_configuration};
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "failed to find vm OS configuration for $vm_os_configuration_key");
+ return;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vm_guest_os
+
+ Parameters : none
+ Returns : string
+ Description : Returns the appropriate guestOS value to be used in the vmx file.
+
+=cut
+
+sub get_vm_guest_os {
+ my $self = shift;
+ if (ref($self) !~ /vmware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
[... 982 lines stripped ...]