Modified: vcl/sandbox/patches/vSphere_SDK_2.3.2_updated.pm URL: http://svn.apache.org/viewvc/vcl/sandbox/patches/vSphere_SDK_2.3.2_updated.pm?rev=1631761&r1=1631760&r2=1631761&view=diff ============================================================================== --- vcl/sandbox/patches/vSphere_SDK_2.3.2_updated.pm (original) +++ vcl/sandbox/patches/vSphere_SDK_2.3.2_updated.pm Tue Oct 14 13:46:30 2014 @@ -1,3696 +1,3696 @@ -#!/usr/bin/perl -w -############################################################################### -# $Id: vSphere_SDK.pm 1448791 2013-02-21 20:02:20Z jfthomps $ -############################################################################### -# 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::vSphere_SDK; - -=head1 SYNOPSIS - - my $vmhost_datastructure = $self->get_vmhost_datastructure(); - my $vsphere_sdk = VCL::Module::Provisioning::VMware::vSphere_SDK->new({data_structure => $vmhost_datastructure}); - my @registered_vms = $vsphere_sdk->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. - - This patch was created as a temporary fix for deployments running VCL 2.3.2 - which encounter problems where the vSphere function calls die abruptly. The - calls were not wrapped in eval blocks. This modified module adds the following - subroutines: - - _get_service_content - _get_view - _find_entity_view - _find_entity_views - - All calls to the vSphere SDK functions are routed through these subroutines - which include the proper eval blocks. - -=cut - -############################################################################## -package VCL::Module::Provisioning::VMware::vSphere_SDK; - -# 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.3.2'; - -# Specify the version of Perl to use -use 5.008000; - -use strict; -use warnings; -use diagnostics; -use English qw( -no_match_vars ); -use File::Temp qw( tempdir ); -use List::Util qw( max ); - -use VCL::utils; - -############################################################################## - -=head1 API 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; - } - - # Newer versions of LWP::Protocol::https have strict SSL checking enabled by default - # The vSphere SDK won't be able to connect if ESXi or vCenter uses a self-signed certificate - # The following setting disables strict checking: - $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; - - # Override the die handler because process will die if VMware Perl libraries aren't installed - local $SIG{__DIE__} = sub{}; - - eval "use VMware::VIRuntime; use VMware::VILib; use VMware::VIExt"; - if ($EVAL_ERROR) { - notify($ERRORS{'OK'}, 0, "vSphere SDK for Perl does not appear to be installed on this managment node, unable to load VMware vSphere SDK Perl modules, error:\n$EVAL_ERROR"); - return 0; - } - notify($ERRORS{'DEBUG'}, 0, "loaded VMware vSphere SDK modules"); - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - my $vmhost_username = $self->data->get_vmhost_profile_username(); - my $vmhost_password = $self->data->get_vmhost_profile_password(); - my $vmhost_profile_id = $self->data->get_vmhost_profile_id(); - - if (!$vmhost_hostname) { - notify($ERRORS{'WARNING'}, 0, "VM host name could not be retrieved"); - return; - } - elsif (!$vmhost_username) { - notify($ERRORS{'DEBUG'}, 0, "unable to use vSphere SDK, VM host username is not configured in the database for VM profile: $vmhost_profile_id"); - return; - } - elsif (!$vmhost_password) { - notify($ERRORS{'DEBUG'}, 0, "unable to use vSphere SDK, VM host password is not configured in the database for VM profile: $vmhost_profile_id"); - return; - } - - Opts::set_option('username', $vmhost_username); - Opts::set_option('password', $vmhost_password); - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Assemble the URLs to try, URL will vary based on the VMware product - my @possible_vmhost_urls = ( - "https://$vmhost_hostname/sdk", - "https://$vmhost_hostname:8333/sdk", - ); - - # Also add URLs containing the short host name if the VM hostname is a full DNS name - if ($vmhost_hostname =~ /\./) { - my ($vmhost_short_name) = $vmhost_hostname =~ /^([^\.]+)/; - push @possible_vmhost_urls, "https://$vmhost_short_name/sdk"; - push @possible_vmhost_urls, "https://$vmhost_short_name:8333/sdk"; - } - - # Call HostConnect, check how long it takes to connect - my $vim; - for my $host_url (@possible_vmhost_urls) { - Opts::set_option('url', $host_url); - - notify($ERRORS{'DEBUG'}, 0, "attempting to connect to VM host: $host_url ($vmhost_username)"); - eval { $vim = Util::connect(); }; - $vim = 'undefined' if !defined($vim); - my $error_message = $@; - undef $@; - - # It's normal if some connection attempts fail - SSH will be used if the vSphere SDK isn't available - # Don't display a warning unless the error indicates a configuration problem (wrong username or password) - # Possible error messages: - # Cannot complete login due to an incorrect user name or password. - # Error connecting to server at 'https://<VM host>/sdk': Connection refused - if ($error_message && $error_message =~ /incorrect/) { - notify($ERRORS{'WARNING'}, 0, "unable to connect to VM host because username or password is incorrectly configured in the VM profile ($vmhost_username/$vmhost_password), error: $error_message"); - } - elsif (!$vim || $error_message) { - notify($ERRORS{'DEBUG'}, 0, "unable to connect to VM host using URL: $host_url, error:\n$error_message"); - } - else { - notify($ERRORS{'OK'}, 0, "connected to VM host: $host_url, username: '$vmhost_username'"); - last; - } - } - - if (!$vim) { - notify($ERRORS{'DEBUG'}, 0, "failed to connect to VM host $vmhost_hostname"); - return; - } - elsif (!ref($vim)) { - notify($ERRORS{'DEBUG'}, 0, "failed to connect to VM host $vmhost_hostname, Util::connect returned '$vim'"); - return; - } - else { - notify($ERRORS{'DEBUG'}, 0, "connected to $vmhost_hostname, VIM object type: " . ref($vim)); - return 1; - } -} - -#///////////////////////////////////////////////////////////////////////////// - -=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; - } - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - my @vms = $self->_find_entity_views('VirtualMachine', - { - begin_entity => $self->_get_datacenter_view() - } - ); - - my @vmx_paths; - for my $vm (@vms) { - push @vmx_paths, $self->_get_normal_path($vm->summary->config->vmPathName) || return; - } - - notify($ERRORS{'DEBUG'}, 0, "found " . scalar(@vmx_paths) . " registered VMs:\n" . join("\n", @vmx_paths)); - return @vmx_paths; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 vm_register - - Parameters : $vmx_file_path - Returns : boolean - Description : Registers the VM specified by vmx file path argument. Returns - true if the VM is already registered or if the VM was - successfully registered. - -=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 path argument and convert it to a datastore path - my $vmx_path = $self->_get_datastore_path(shift) || return; - - my $datacenter = $self->_get_datacenter_view() || return; - my $vm_folder = $self->_get_view($datacenter->{vmFolder}) || return; - - my $resource_pool = $self->_get_resource_pool_view() || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - my $vm_mo_ref; - eval { $vm_mo_ref = $vm_folder->RegisterVM(path => $vmx_path, - asTemplate => 'false', - pool => $resource_pool - ); - }; - - if ($@) { - if ($@->isa('SoapFault') && ref($@->detail) eq 'AlreadyExists') { - notify($ERRORS{'DEBUG'}, 0, "VM is already registered: $vmx_path"); - return 1; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to register VM: $vmx_path, error:\n$@"); - return; - } - } - - if (ref($vm_mo_ref) ne 'ManagedObjectReference' || $vm_mo_ref->type ne 'VirtualMachine') { - notify($ERRORS{'WARNING'}, 0, "RegisterVM did not return a VirtualMachine ManagedObjectReference:\n" . format_data($vm_mo_ref)); - return; - } - - notify($ERRORS{'DEBUG'}, 0, "registered VM: $vmx_path"); - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 vm_unregister - - Parameters : $vmx_file_path or $vm_view or $vm_mo_ref - Returns : boolean - Description : Unregisters the VM specified by vmx file path argument. Returns - true if the VM is not registered or if the VM was successfully - unregistered. - -=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; - } - - my $argument = shift; - my $vm_view; - my $vm_name; - if (my $type = ref($argument)) { - if ($type eq 'ManagedObjectReference') { - notify($ERRORS{'DEBUG'}, 0, "argument is a ManagedObjectReference, retrieving VM view"); - $vm_view = $self->_get_view($argument) - } - elsif ($type eq 'VirtualMachine') { - $vm_view = $argument; - } - else { - notify($ERRORS{'WARNING'}, 0, "invalid argument reference type: '$type', must be either VirtualMachine or ManagedObjectReference"); - return; - } - - $vm_name = $vm_view->{name}; - if (!$vm_name) { - notify($ERRORS{'WARNING'}, 0, "failed to unregister VM, name could not be determined from VM view:\n" . format_data($vm_view)); - return; - } - } - else { - $vm_name = $argument; - $vm_view = $self->_get_vm_view($argument); - } - - my $vmx_path = $vm_view->{summary}{config}{vmPathName}; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - notify($ERRORS{'DEBUG'}, 0, "attempting to unregister VM: '$vm_name' ($vmx_path)"); - - eval { $vm_view->UnregisterVM(); }; - if ($@) { - notify($ERRORS{'WARNING'}, 0, "failed to unregister VM: $vm_name, error:\n$@"); - return; - } - - # Delete the cached VM object - delete $self->{vm_view_objects}{$vmx_path}; - - notify($ERRORS{'DEBUG'}, 0, "unregistered VM: $vm_name"); - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 vm_power_on - - Parameters : $vmx_file_path - Returns : boolean - Description : Powers on the VM specified by vmx file path argument. Returns - true if the VM was successfully powered on or if it was already - powered on. - -=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 path argument and convert it to a datastore path - my $vmx_path = $self->_get_datastore_path(shift) || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - my $vm = $self->_get_vm_view($vmx_path) || return; - - eval { $vm->PowerOnVM(); }; - if ($@) { - if ($@->isa('SoapFault') && ref($@->detail) eq 'InvalidPowerState') { - my $existing_power_state = $@->detail->existingState->val; - if ($existing_power_state =~ /on/i) { - notify($ERRORS{'DEBUG'}, 0, "VM is already powered on: $vmx_path"); - return 1; - } - } - - notify($ERRORS{'WARNING'}, 0, "failed to power on VM: $vmx_path, error:\n$@"); - return; - } - - notify($ERRORS{'DEBUG'}, 0, "powered on VM: $vmx_path"); - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 vm_power_off - - Parameters : $vmx_file_path - Returns : boolean - Description : Powers off the VM specified by vmx file path argument. Returns - true if the VM was successfully powered off or if it was already - powered off. - -=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 path argument and convert it to a datastore path - my $vmx_path = $self->_get_datastore_path(shift) || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - my $vm = $self->_get_vm_view($vmx_path) || return; - - eval { $vm->PowerOffVM(); }; - if ($@) { - if ($@->isa('SoapFault') && ref($@->detail) eq 'InvalidPowerState') { - my $existing_power_state = $@->detail->existingState->val; - if ($existing_power_state =~ /off/i) { - notify($ERRORS{'DEBUG'}, 0, "VM is already powered off: $vmx_path"); - return 1; - } - } - - notify($ERRORS{'WARNING'}, 0, "failed to power off VM: $vmx_path, error:\n$@"); - return; - } - - notify($ERRORS{'DEBUG'}, 0, "powered off VM: $vmx_path"); - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_vm_power_state - - Parameters : $vmx_file_path - Returns : string - Description : Determines the power state of the VM specified by the vmx file - path argument. A string is returned containing 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 path argument and convert it to a datastore path - my $vmx_path = $self->_get_datastore_path(shift) || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - my $vm = $self->_get_vm_view($vmx_path) || return; - - my $power_state = $vm->runtime->powerState->val; - - my $return_power_state; - if ($power_state =~ /on/i) { - $return_power_state = 'on'; - } - elsif ($power_state =~ /off/i) { - $return_power_state = 'off'; - } - elsif ($power_state =~ /suspended/i) { - $return_power_state = 'suspended'; - } - else { - notify($ERRORS{'WARNING'}, 0, "detected unsupported power state: $power_state"); - $return_power_state = '$power_state'; - } - - notify($ERRORS{'DEBUG'}, 0, "power state of VM $vmx_path: $return_power_state"); - return $return_power_state; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 _clean_vm_name - Parameters : $vm_name - Returns : string - Description : VMWare vCenter supports VM Names of up to 80 characters, but if - the name is greater than 29 characters, it will truncate the - corresponding name and enclosing directory of the virtual disks. - -=cut - -sub _clean_vm_name { - 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 $vm_name = shift || return; - - # if the length of the name is > 29, then truncate it in such a way that - # the image name remains unique in the VCL database - my $MAX_VMNAME_LEN = 29; - if (length $vm_name > $MAX_VMNAME_LEN) { - notify($ERRORS{'DEBUG'}, 0, "truncating VM name $vm_name"); - my $newname = ""; - if ($vm_name =~ m/^(\w+)-(\w+?)(\d*)-(v\d+)$/) { - my $base = $1; - my $name = $2; - my $imgid = $3; - my $version = $4; - my $shortened = substr($name, 0, $MAX_VMNAME_LEN - 2 - length($imgid) - length($base) - length($version)); - $newname = $base . "-" . $shortened . $imgid . "-" . $version; - } - else { - my ($pre_len, $post_len) = ($MAX_VMNAME_LEN - 10, 10); - my ($pre, $post) = $vm_name =~ m/^(.{$pre_len}).*(.{$post_len})$/; - $newname = $pre . $post; - } - if (get_image_info($newname, 0, 1)) { - notify($ERRORS{'WARNING'}, 0, "Naming conflict: $newname already exists in the database"); - } - else { - notify($ERRORS{'DEBUG'}, 0, "Changed image name to: $newname"); - $vm_name = $newname; - } - } - return $vm_name; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 copy_virtual_disk - - Parameters : $source_vmdk_file_path, $destination_vmdk_file_path, $disk_type (optional), $adapter_type (optional) - Returns : boolean - Description : Copies a virtual disk (set of vmdk files). This subroutine allows - a virtual disk to be converted to a different disk type or - adapter type. The source and destination vmdk file path arguments - are required. - - The disk type argument is optional and may be one of the - following values: - -eagerZeroedThick - -all space allocated and wiped clean of any previous contents on the physical media at creation time - -may take longer time during creation compared to other disk formats - -flatMonolithic - -preallocated monolithic disk - -disks in this format can be used with other VMware products - -format is only applicable as a destination format in a clone operation - -not usable for disk creation - -since vSphere API 4.0 - -preallocated - -all space allocated at creation time - -space is zeroed on demand as the space is used - -raw - -raw device - -rdm - -virtual compatibility mode raw disk mapping - -grants access to the entire raw disk and the virtual disk can participate in snapshots - -rdmp - -physical compatibility mode (pass-through) raw disk mapping - -passes SCSI commands directly to the hardware - -cannot participate in snapshots - -sparse2Gb, 2Gbsparse - -sparse disk with 2GB maximum extent size - -can be used with other VMware products - -2GB extent size makes these disks easier to burn to dvd or use on filesystems that don't support large files - -only applicable as a destination format in a clone operation - -not usable for disk creation - -sparseMonolithic - -sparse monolithic disk - -can be used with other VMware products - -only applicable as a destination format in a clone operation - -not usable for disk creation - -since vSphere API 4.0 - -thick - -all space allocated at creation time - -space may contain stale data on the physical media - -primarily used for virtual machine clustering - -generally insecure and should not be used - -due to better performance and security properties, the use of the 'preallocated' format is preferred over this format - -thick2Gb - -thick disk with 2GB maximum extent size - -can be used with other VMware products - -2GB extent size makes these disks easier to burn to dvd or use on filesystems that don't support large files - -only applicable as a destination format in a clone operation - -not usable for disk creation - -thin (default) - -space required for thin-provisioned virtual disk is allocated and zeroed on demand as the space is used - - The adapter type argument is optional and may be one of the - following values: - -busLogic - -ide - -lsiLogic - - If the adapter type argument is not specified an attempt will be - made to retrieve it from the source vmdk file. If this fails, - lsiLogic will be used. - -=cut - -sub copy_virtual_disk { - 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 source and destination path arguments in the datastore path format - my $source_path = $self->_get_datastore_path(shift) || return; - my $destination_path = $self->_get_datastore_path(shift) || return; - - # Make sure the source path ends with .vmdk - if ($source_path !~ /\.vmdk$/i || $destination_path !~ /\.vmdk$/i) { - notify($ERRORS{'WARNING'}, 0, "source and destination path arguments must end with .vmdk:\nsource path argument: $source_path\ndestination path argument: $destination_path"); - return; - } - - - # Get the adapter type and disk type arguments if they were specified - # If not specified, set the default values - my $destination_disk_type = shift || 'thin'; - - # Fix the disk type in case 2gbsparse was passed - if ($destination_disk_type =~ /2gbsparse/i) { - $destination_disk_type = 'sparse2Gb'; - } - - # Check the disk type argument, the string must match exactly or the copy will fail - my @valid_disk_types = qw( eagerZeroedThick flatMonolithic preallocated raw rdm rdmp sparse2Gb sparseMonolithic thick thick2Gb thin ); - if (!grep(/^$destination_disk_type$/, @valid_disk_types)) { - notify($ERRORS{'WARNING'}, 0, "disk type argument is not valid: '$destination_disk_type', it must exactly match (case sensitive) one of the following strings:\n" . join("\n", @valid_disk_types)); - return; - } - - my $vmhost_name = $self->data->get_vmhost_hostname(); - - my $source_datastore_name = $self->_get_datastore_name($source_path) || return; - my $destination_datastore_name = $self->_get_datastore_name($destination_path) || return; - - my $source_datastore = $self->_get_datastore_object($source_datastore_name) || return; - my $destination_datastore = $self->_get_datastore_object($destination_datastore_name) || return; - - my $destination_base_name = $self->_get_file_base_name($destination_path); - - my $datacenter_view = $self->_get_datacenter_view() || return; - my $virtual_disk_manager_view = $self->_get_virtual_disk_manager_view() || return; - - # Get the source vmdk file info so the source adapter and disk type can be displayed - my $source_info = $self->_get_file_info($source_path) || return; - if (scalar(keys %$source_info) != 1) { - notify($ERRORS{'WARNING'}, 0, "unable to copy virtual disk, multiple source files were found:\n" . format_data($source_info)); - } - - my $source_info_file_path = (keys(%$source_info))[0]; - - my $source_adapter_type = $source_info->{$source_info_file_path}{controllerType} || 'lsiLogic'; - my $source_disk_type = $source_info->{$source_info_file_path}{diskType} || ''; - my $source_file_size_bytes = $source_info->{$source_info_file_path}{fileSize} || '0'; - my $source_file_capacity_kb = $source_info->{$source_info_file_path}{capacityKb} || '0'; - my $source_file_capacity_bytes = ($source_file_capacity_kb * 1024); - - # Set the destination adapter type to the source adapter type if it wasn't specified as an argument - my $destination_adapter_type = shift || $source_adapter_type; - - if ($destination_adapter_type =~ /bus/i) { - $destination_adapter_type = 'busLogic'; - } - elsif ($destination_adapter_type =~ /lsi/) { - $destination_adapter_type = 'lsiLogic'; - } - else { - $destination_adapter_type = 'ide'; - } - - if ($source_adapter_type !~ /\w/ || $source_disk_type !~ /\w/ || $source_file_size_bytes !~ /\d/) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve adapter type, disk type, and file size of source file on VM host $vmhost_name: '$source_path', file info:\n" . format_data($source_info)); - return; - } - - # Get the destination partent directory path and create the directory - my $destination_directory_path = $self->_get_parent_directory_datastore_path($destination_path) || return; - $self->create_directory($destination_directory_path) || return; - - notify($ERRORS{'DEBUG'}, 0, "attempting to copy virtual disk on VM host $vmhost_name: '$source_path' --> '$destination_path'\n" . - "source adapter type: $source_adapter_type\n" . - "destination adapter type: $destination_adapter_type\n" . - "disk type: $source_disk_type\n" . - "source capacity: " . get_file_size_info_string($source_file_capacity_bytes) . "\n" . - "source space used: " . get_file_size_info_string($source_file_size_bytes) - ); - - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Create a virtual disk spec object - my $virtual_disk_spec = VirtualDiskSpec->new( - adapterType => $destination_adapter_type, - diskType => $destination_disk_type, - ); - - my $copy_virtual_disk_result; - eval { - $copy_virtual_disk_result = $virtual_disk_manager_view->CopyVirtualDisk( - sourceName => $source_path, - sourceDatacenter => $datacenter_view, - destName => $destination_path, - destDatacenter => $datacenter_view, - destSpec => $virtual_disk_spec, - force => 1 - ); - }; - - # Check if an error occurred - if (my $copy_virtual_disk_fault = $@) { - if ($copy_virtual_disk_fault =~ /No space left/i) { - # Check if the output indicates there is not enough space to copy the vmdk - # Output will contain: - # Fault string: A general system error occurred: No space left on device - # Fault detail: SystemError - notify($ERRORS{'CRITICAL'}, 0, "failed to copy vmdk on VM host $vmhost_name using CopyVirtualDisk function, no space is left on the destination device: '$destination_path'\nerror:\n$copy_virtual_disk_fault"); - return; - } - elsif ($copy_virtual_disk_fault =~ /not implemented/i) { - notify($ERRORS{'DEBUG'}, 0, "unable to copy vmdk using CopyVirtualDisk function, VM host $vmhost_name does not implement the CopyVirtualDisk function"); - - # Delete the destination directory path previously created - $self->delete_file($destination_directory_path); - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to copy vmdk on VM host $vmhost_name using CopyVirtualDisk function: '$source_path' --> '$destination_path'\nerror:\n$copy_virtual_disk_fault"); - return; - } - } - else { - notify($ERRORS{'OK'}, 0, "copied vmdk on VM host $vmhost_name using CopyVirtualDisk function:\n" . format_data($copy_virtual_disk_result)); - return 1; - } - - - my $source_vm_name = $self->_clean_vm_name("source_$destination_base_name"); - my $clone_vm_name = $self->_clean_vm_name($destination_base_name); - - my $source_vm_directory_path = "[$source_datastore_name] $source_vm_name"; - my $clone_vm_directory_path = "[$destination_datastore_name] $clone_vm_name"; - - # Make sure the source and clone directories don't exist - # Otherwise the VM creation/cloning process will create another directory with '_1' appended and the files won't be deleted - if ($self->file_exists($source_vm_directory_path)) { - notify($ERRORS{'WARNING'}, 0, "unable to copy virtual disk, source VM directory path already exists: $source_vm_directory_path"); - return; - } - if ($self->file_exists($clone_vm_directory_path)) { - notify($ERRORS{'WARNING'}, 0, "unable to copy virtual disk, clone VM directory path already exists: $clone_vm_directory_path"); - return; - } - - - my $file_manager = $self->_get_file_manager_view() || return; - my $resource_pool_view = $self->_get_resource_pool_view() || return; - - my $folder_view = $self->_find_entity_view("Folder", - { - begin_entity => $datacenter_view, - filter => { name => "vm" } - } - ); - - if (!$folder_view){ - notify($ERRORS{'WARNING'}, 0, "failed to retrieve VM folder view"); - return; - } - - # Create a virtual machine on top of this virtual disk - # First, create a controller for the virtual disk - my $controller; - if ($destination_adapter_type eq 'lsiLogic') { - $controller = VirtualLsiLogicController->new( - key => 0, - device => [0], - busNumber => 0, - sharedBus => VirtualSCSISharing->new('noSharing') - ); - } - else { - $controller = VirtualBusLogicController->new( - key => 0, - device => [0], - busNumber => 0, - sharedBus => VirtualSCSISharing->new('noSharing') - ); - } - - # Next create a disk type (it will be the same as the source disk) - my $disk_backing_info = ($source_disk_type)->new( - datastore => $source_datastore, - fileName => $source_path, - diskMode => "independent_persistent" - ); - - # Create the actual virtual disk - my $source_vm_disk = VirtualDisk->new( - key => 0, - backing => $disk_backing_info, - capacityInKB => $source_file_capacity_kb, - controllerKey => 0, - unitNumber => 0 - ); - - # Create the specification for creating a source VM - my $source_vm_config = VirtualMachineConfigSpec->new( - name => $source_vm_name, - deviceChange => [ - VirtualDeviceConfigSpec->new( - operation => VirtualDeviceConfigSpecOperation->new('add'), - device => $controller - ), - VirtualDeviceConfigSpec->new( - operation => VirtualDeviceConfigSpecOperation->new('add'), - device => $source_vm_disk - ) - ], - files => VirtualMachineFileInfo->new( - logDirectory => $source_vm_directory_path, - snapshotDirectory => $source_vm_directory_path, - suspendDirectory => $source_vm_directory_path, - vmPathName => $source_vm_directory_path - ) - ); - - # Create the specification for cloning the VM - my $clone_spec = VirtualMachineCloneSpec->new( - config => VirtualMachineConfigSpec->new( - name => $clone_vm_name, - files => VirtualMachineFileInfo->new( - logDirectory => $clone_vm_directory_path, - snapshotDirectory => $clone_vm_directory_path, - suspendDirectory => $clone_vm_directory_path, - vmPathName => $clone_vm_directory_path - ) - ), - powerOn => 0, - template => 0, - location => VirtualMachineRelocateSpec->new( - datastore => $destination_datastore, - pool => $resource_pool_view, - diskMoveType => 'moveAllDiskBackingsAndDisallowSharing', - transform => VirtualMachineRelocateTransformation->new('sparse'), - ) - ); - - - notify($ERRORS{'DEBUG'}, 0, "attempting to copy virtual disk by cloning temporary VM: '$source_path' --> '$destination_path'\n" . - "adapter type: $source_adapter_type\n" . - "source disk type: $source_disk_type\n" . - "source capacity: " . get_file_size_info_string($source_file_capacity_bytes) . "\n" . - "source space used: " . get_file_size_info_string($source_file_size_bytes) . "\n" . - "source VM name: $source_vm_name\n" . - "clone VM name: $clone_vm_name\n" . - "source VM directory path: $source_vm_directory_path\n" . - "clone VM directory path: $clone_vm_directory_path" - ); - - - my $source_vm_view; - my $clone_vm_view; - eval { - my $source_vm = $folder_view->CreateVM( - config => $source_vm_config, - pool => $resource_pool_view - ); - if ($source_vm) { - notify($ERRORS{'DEBUG'}, 0, "created temporary source VM which will be cloned: $source_vm_name"); - $source_vm_view = $self->_get_view($source_vm); - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to create temporary source VM which will be cloned: $source_vm_name"); - return; - } - - notify($ERRORS{'DEBUG'}, 0, "cloning VM: $source_vm_name --> $clone_vm_name"); - my $clone_vm = $source_vm_view->CloneVM( - folder => $folder_view, - name => $clone_vm_name, - spec => $clone_spec - ); - if ($clone_vm) { - $clone_vm_view = $self->_get_view($clone_vm); - notify($ERRORS{'DEBUG'}, 0, "cloned VM: $source_vm_name --> $clone_vm_name" . format_data($clone_vm_view)); - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to clone VM: $source_vm_name --> $clone_vm_name"); - return; - } - }; - - # Check if an error occurred - if (my $fault = $@) { - if ($fault =~ /No space left/i) { - # Check if the output indicates there is not enough space to copy the vmdk - # Output will contain: - # Fault string: A general system error occurred: No space left on device - # Fault detail: SystemError - notify($ERRORS{'CRITICAL'}, 0, "failed to copy vmdk on VM host $vmhost_name, no space is left on the destination device: '$destination_path'\nerror:\n$fault"); - return; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to copy vmdk on VM host $vmhost_name: '$source_path' --> '$destination_path'\nerror:\n$fault"); - } - return; - } - - notify($ERRORS{'DEBUG'}, 0, "deleting source VM: $source_vm_name"); - $self->vm_unregister($source_vm_view); - notify($ERRORS{'DEBUG'}, 0, "deleting source VM directory: $source_vm_directory_path"); - $self->delete_file($source_vm_directory_path); - - notify($ERRORS{'DEBUG'}, 0, "deleting cloned VM: $clone_vm_name"); - $self->vm_unregister($clone_vm_view); - my @clone_files = $self->find_files($clone_vm_directory_path, '*', 1); - for my $clone_file_path (grep(!/\.(vmdk)$/i, @clone_files)) { - notify($ERRORS{'DEBUG'}, 0, "deleting cloned VM file: $clone_file_path"); - $self->delete_file($clone_file_path); - } - - # Set this as a class value so that it is retrievable from within - # the calling context, i.e. capture(), routine. This way, in case - # the name changes, it is possible to update the database with the new value. - $self->{new_image_name} = $clone_vm_name; - notify($ERRORS{'OK'}, 0, "copied virtual disk on VM host $vmhost_name: '$source_path' --> '$destination_path'"); - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 move_virtual_disk - - Parameters : $source_path, $destination_path - Returns : boolean - Description : Moves or renames a virtual disk (set of vmdk files). - -=cut - -sub move_virtual_disk { - 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 source path argument in datastore path format - my $source_path = $self->_get_datastore_path(shift) || return; - my $destination_path = $self->_get_datastore_path(shift) || return; - - my $vmhost_name = $self->data->get_vmhost_hostname(); - - # Make sure the source path ends with .vmdk - if ($source_path !~ /\.vmdk$/i || $destination_path !~ /\.vmdk$/i) { - notify($ERRORS{'WARNING'}, 0, "source and destination path arguments must end with .vmdk:\nsource path argument: $source_path\ndestination path argument: $destination_path"); - return; - } - - # Make sure the source file exists - if (!$self->file_exists($source_path)) { - notify($ERRORS{'WARNING'}, 0, "source file does not exist on VM host $vmhost_name: '$source_path'"); - return; - } - - # Make sure the destination file does not exist - if ($self->file_exists($destination_path)) { - notify($ERRORS{'WARNING'}, 0, "destination file already exists on VM host $vmhost_name: '$destination_path'"); - return; - } - - # Get the destination parent directory path, make sure it exists - my $destination_parent_directory_path = $self->_get_parent_directory_datastore_path($destination_path) || return; - $self->create_directory($destination_parent_directory_path) || return; - - # Check if a virtual disk manager object is available - my $virtual_disk_manager = $self->_get_virtual_disk_manager_view() || return; - - # Create a datacenter object - my $datacenter = $self->_get_datacenter_view() || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Attempt to move the virtual disk using MoveVirtualDisk - notify($ERRORS{'DEBUG'}, 0, "attempting to move virtual disk on VM host $vmhost_name: '$source_path' --> '$destination_path'"); - eval { $virtual_disk_manager->MoveVirtualDisk(sourceName => $source_path, - sourceDatacenter => $datacenter, - destName => $destination_path, - destDatacenter => $datacenter, - force => 0); - }; - - # Check if an error occurred - if (my $fault = $@) { - # Get the source file info - my $source_file_info = $self->_get_file_info($source_path)->{$source_path}; - - # A FileNotFound fault will be generated if the source vmdk file exists but there is a problem with it - if ($fault->isa('SoapFault') && ref($fault->detail) eq 'FileNotFound' && defined($source_file_info->{type}) && $source_file_info->{type} !~ /vmdisk/i) { - notify($ERRORS{'WARNING'}, 0, "failed to move virtual disk on VM host $vmhost_name, source file is either not a virtual disk file or there is a problem with its configuration, check the 'Extent description' section of the vmdk file: '$source_path'\nsource file info:\n" . format_data($source_file_info)); - return; - } - elsif ($fault =~ /No space left/i) { - notify($ERRORS{'CRITICAL'}, 0, "failed to move virtual disk on VM host $vmhost_name, no space is left on the destination device: '$destination_path'\nerror:\n$fault"); - return; - } - elsif ($fault =~ /not implemented/i){ - notify($ERRORS{'DEBUG'}, 0, "unable to move vmdk using MoveVirtualDisk function, VM host $vmhost_name does not implement the MoveVirtualDisk function"); - $self->delete_file($destination_parent_directory_path); - } - elsif ($source_file_info) { - notify($ERRORS{'WARNING'}, 0, "failed to move virtual disk on VM host $vmhost_name:\n'$source_path' --> '$destination_path'\nsource file info:\n" . format_data($source_file_info) . "\n$fault"); - return; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to move virtual disk on VM host $vmhost_name:\n'$source_path' --> '$destination_path'\nsource file info: unavailable\n$fault"); - return; - } - - } else { - notify($ERRORS{'OK'}, 0, "moved virtual disk on VM host $vmhost_name:\n'$source_path' --> '$destination_path'"); - return 1; - } - - # This section should apply only to vCenter hosts, i.e. hosts for which the - # MoveVirtualDisk method is not implemented. Instead, use the copy_virtual_disk - # method (where the CloneVM method is used) and cleanup source files afterward. - if($self->copy_virtual_disk($source_path, $destination_path)){ - my $file_manager = $self->_get_file_manager_view() || return; - my $source_parent_directory_path = $self->_get_parent_directory_datastore_path($source_path) || return; - notify($ERRORS{'DEBUG'}, 0, "Removing source directory: $source_parent_directory_path"); - $file_manager->DeleteDatastoreFile( - name => $source_parent_directory_path, - datacenter => $datacenter); - - notify($ERRORS{'OK'}, 0, "moved virtual disk on VM host $vmhost_name:\n'$source_path' --> '$destination_path'"); - return 1; - } else { - notify($ERRORS{'WARNING'}, 0, "Unable to move virtual disk from $vmhost_name: '$source_path' --> '$destination_path'"); - return 0; - } -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 create_nfs_datastore - - Parameters : $datastore_name, $remote_host, $remote_path - Returns : boolean - Description : Creates an NFS datastore on the VM host. Note: this subroutine is - not currenly being called by anything. - -=cut - -sub create_nfs_datastore { - my $self = shift; - if (ref($self) !~ /module/i) { - notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); - return; - } - - # Get the arguments - my ($datastore_name, $remote_host, $remote_path) = @_; - if (!$datastore_name || !$remote_host || !$remote_path) { - notify($ERRORS{'WARNING'}, 0, "datastore name, remote host, and remote path arguments were not supplied"); - return; - } - - # Remove trailing slashes from the remote path - $remote_path =~ s/\/+$//g; - - # Assemble a datastore device string, used to check if existing datastore is pointing to the same remote host and path - my $datastore_device = "$remote_host:$remote_path"; - - # Get the existing datastore info - my $datastore_info = $self->_get_datastore_info(); - for my $check_datastore_name (keys(%$datastore_info)) { - my $check_datastore_type = $datastore_info->{$check_datastore_name}{type}; - - # Make sure a non-NFS datastore with the same name doesn't alreay exist - if ($check_datastore_type !~ /nfs/i) { - if ($check_datastore_name eq $datastore_name) { - notify($ERRORS{'WARNING'}, 0, "datastore named $datastore_name already exists on VM host but its type is not NFS:\n" . format_data($datastore_info->{$check_datastore_name})); - return; - } - else { - # Type isn't NFS and name doesn't match - next; - } - } - - # Get the existing datastore device string, format is: - # 10.25.0.245:/install/vmtest/datastore - my $check_datastore_device = $datastore_info->{$check_datastore_name}{datastore}{value}; - if (!$check_datastore_device) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve datastore device string from datastore info:\n" . format_data($datastore_info->{$check_datastore_name})); - next; - } - - # Remove trailing slashes from existing device string - $check_datastore_device =~ s/\/+$//g; - - # Check if datastore already exists pointing to the same remote path - if ($check_datastore_name eq $datastore_name) { - # Datastore names match, check if existing datastore is pointing the the requested device path - if ($check_datastore_device eq $datastore_device) { - notify($ERRORS{'DEBUG'}, 0, "$check_datastore_type datastore '$datastore_name' already exists on VM host, remote path: $check_datastore_device"); - return 1; - } - else { - notify($ERRORS{'WARNING'}, 0, "$check_datastore_type datastore '$datastore_name' already exists on VM host but it is pointing to a different remote path: - requested remote path: $datastore_device - existing remote path: $check_datastore_device"); - return; - } - } - else { - # Datastore names don't match, make sure an existing datastore with a different name isn't pointing to the requested device path - if ($check_datastore_device eq $datastore_device) { - notify($ERRORS{'WARNING'}, 0, "$check_datastore_type datastore with a different name already exists on VM host pointing to '$check_datastore_device': - requested datastore name: $datastore_name - existing datastore name: $check_datastore_name"); - return; - } - else { - # Datastore name doesn't match, datastore remote path doesn't match - next; - } - } - } - - # Get the datastore system object - my $datastore_system = $self->_get_view($self->_get_datastore_view->configManager->datastoreSystem); - if (!$datastore_system) { - notify($ERRORS{'WARNING'}, 0, "failed to retrieve datastore system object"); - return; - } - - # Create a HostNasVolumeSpec object to store the datastore configuration - my $host_nas_volume_spec = HostNasVolumeSpec->new(accessMode => 'readWrite', - localPath => $datastore_name, - remoteHost => $remote_host, - remotePath => $remote_path, - ); - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Attempt to cretae the NAS datastore - notify($ERRORS{'DEBUG'}, 0, "attempting to create NAS datastore:\n" . format_data($host_nas_volume_spec)); - eval { $datastore_system->CreateNasDatastore(spec => $host_nas_volume_spec); }; - if (my $fault = $@) { - notify($ERRORS{'WARNING'}, 0, "failed to create NAS datastore on VM host:\ndatastore name: $datastore_name\nremote host: $remote_host\nremote path: $remote_path\nerror:\n$@"); - return; - } - - notify($ERRORS{'OK'}, 0, "created NAS datastore on VM host: $datastore_name"); - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - -=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. A string is - returned containing one of the following values: - -lsiLogic - -busLogic - -ide - -=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 = $self->_get_datastore_path(shift) || return; - if ($vmdk_file_path !~ /\.vmdk$/) { - notify($ERRORS{'WARNING'}, 0, "file path argument must end with .vmdk: $vmdk_file_path"); - return; - } - - # Get the vmdk file info - my $vmdk_file_info = $self->_get_file_info($vmdk_file_path)->{$vmdk_file_path}; - if (!$vmdk_file_info) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve info for file: $vmdk_file_path"); - return; - } - - # Check if the controllerType key exists in the vmdk file info - if (!defined($vmdk_file_info->{controllerType}) || !$vmdk_file_info->{controllerType}) { - notify($ERRORS{'DEBUG'}, 0, "unable to retrieve controllerType value from file info: $vmdk_file_path\n" . format_data($vmdk_file_info)); - return; - } - - my $controller_type = $vmdk_file_info->{controllerType}; - - my $return_controller_type; - if ($controller_type =~ /lsi/i) { - $return_controller_type = 'lsiLogic'; - } - elsif ($controller_type =~ /bus/i) { - $return_controller_type = 'busLogic'; - } - elsif ($controller_type =~ /ide/i) { - $return_controller_type = 'ide'; - } - else { - $return_controller_type = $controller_type; - } - - notify($ERRORS{'DEBUG'}, 0, "retrieved controllerType value from vmdk file info: $return_controller_type ($controller_type)"); - return $return_controller_type; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_virtual_disk_type - - Parameters : $vmdk_file_path - Returns : string - 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 = $self->_get_datastore_path(shift) || return; - if ($vmdk_file_path !~ /\.vmdk$/) { - notify($ERRORS{'WARNING'}, 0, "file path argument must end with .vmdk: $vmdk_file_path"); - return; - } - - # Get the vmdk file info - my $vmdk_file_info = $self->_get_file_info($vmdk_file_path)->{$vmdk_file_path}; - if (!$vmdk_file_info) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve info for file: $vmdk_file_path"); - return; - } - - # Check if the diskType key exists in the vmdk file info - if (!defined($vmdk_file_info->{diskType}) || !$vmdk_file_info->{diskType}) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve diskType value from file info: $vmdk_file_path\n" . format_data($vmdk_file_info)); - return; - } - - my $disk_type = $vmdk_file_info->{diskType}; - - if ($disk_type =~ /VirtualDisk(.+)BackingInfo/) { - $disk_type = $1; - } - notify($ERRORS{'DEBUG'}, 0, "retrieved diskType value from vmdk file info: $disk_type"); - return $disk_type; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_virtual_disk_hardware_version - - Parameters : $vmdk_file_path - Returns : string - Description : Retrieves the virtual disk hardware version configured for the - virtual disk specified by the vmdk file path argument. - -=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 = $self->_get_datastore_path(shift) || return; - if ($vmdk_file_path !~ /\.vmdk$/) { - notify($ERRORS{'WARNING'}, 0, "file path argument must end with .vmdk: $vmdk_file_path"); - return; - } - - # Get the vmdk file info - my $vmdk_file_info = $self->_get_file_info($vmdk_file_path)->{$vmdk_file_path}; - if (!$vmdk_file_info) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve info for file: $vmdk_file_path"); - return; - } - - # Check if the hardwareVersion key exists in the vmdk file info - my $hardware_version = $vmdk_file_info->{hardwareVersion}; - if (!$hardware_version) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve hardwareVersion value from file info: $vmdk_file_path\n" . format_data($vmdk_file_info)); - return; - } - - notify($ERRORS{'DEBUG'}, 0, "retrieved hardwareVersion value from vmdk file info: $hardware_version"); - return $hardware_version; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_vmware_product_name - - Parameters : none - Returns : string - Description : Returns the full VMware product name installed on the VM host. - Examples: - VMware Server 2.0.2 build-203138 - VMware ESXi 4.0.0 build-208167 - -=cut - -sub get_vmware_product_name { - 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 $self->{product_name} if $self->{product_name}; - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - my $service_content = $self->_get_service_content(); - my $product_name = $service_content->{about}->{fullName}; - if ($product_name) { - notify($ERRORS{'DEBUG'}, 0, "VMware product being used on VM host $vmhost_hostname: '$product_name'"); - $self->{product_name} = $product_name; - return $self->{product_name}; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve VMware product name being used on VM host $vmhost_hostname"); - return; - } -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_vmware_product_version - - Parameters : none - Returns : string - Description : Returns the VMware product version installed on the VM host. - Example: '4.0.0' - -=cut - -sub get_vmware_product_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; - } - - return $self->{product_version} if $self->{product_version}; - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - my $datacenter_view = $self->_get_datacenter_view(); - my $product_version = $datacenter_view->config->product->version; - - if ($product_version) { - notify($ERRORS{'DEBUG'}, 0, "retrieved product version for VM host $vmhost_hostname: $product_version"); - $self->{product_version} = $product_version; - return $self->{product_version}; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve product version for VM host $vmhost_hostname"); - 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 $datacenter_view = $self->_get_datacenter_view(); - - # Retrieve the network info, check if each network is accessible - my @network_names; - for my $network (@{Vim::get_views(mo_ref_array => $datacenter_view->network)}) { - push @network_names, $network->name; - } - - notify($ERRORS{'DEBUG'}, 0, "retrieved network names:\n" . join("\n", @network_names)); - return @network_names; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 is_restricted - - Parameters : none - Returns : boolean - Description : Determines if remote access to the VM host via the vSphere SDK is - restricted due to the type of VMware license being used on the - host. 0 is returned if remote access is not restricted. 1 is - returned if remote access is restricted and the access to the VM - host is read-only. - -=cut - -sub is_restricted { - my $self = shift; - if (ref($self) !~ /module/i) { - notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); - return; - } - - my $service_content = $self->_get_service_content(); - if (!$service_content) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve vSphere SDK service content object, assuming access to the VM host via the vSphere SDK is restricted"); - return 1; - } - - # Attempt to get a virtual disk manager object - # This is required to copy virtual disks and perform other operations - if (!$service_content->{virtualDiskManager}) { - notify($ERRORS{'OK'}, 0, "access to the VM host is restricted, virtual disk manager is not available through the vSphere SDK"); - return 1; - } - - # Get a fileManager object - my $file_manager = $self->_get_view($service_content->{fileManager}) || return; - if (!$file_manager) { - notify($ERRORS{'WARNING'}, 0, "unable to determine if access to the VM host via the vSphere SDK is restricted due to the license, failed to retrieve file manager object"); - return 1; - } - - # Override the die handler because MakeDirectory may call it - local $SIG{__DIE__} = sub{}; - - # Attempt to create the test directory, check if RestrictedVersion fault occurs - eval { $file_manager->DeleteDatastoreFile(name => ''); } ; - if (my $fault = $@) { - if ($fault->isa('SoapFault') && ref($fault->detail) eq 'RestrictedVersion') { - notify($ERRORS{'OK'}, 0, "access to the VM host via the vSphere SDK is restricted due to the license: " . $fault->name); - return 1; - } - elsif ($fault->isa('SoapFault') && (ref($fault->detail) eq 'InvalidDatastorePath' || ref($fault->detail) eq 'InvalidArgument')) { - # Do nothing, expected since empty path was passed to DeleteDatastoreFile - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to determine if access to the VM host via the vSphere SDK is restricted due to the license, error:\n$@"); - return 1; - } - } - - notify($ERRORS{'OK'}, 0, "access to the VM host via the vSphere SDK is NOT restricted due to the license"); - - return 0; -} - -############################################################################## - -=head1 OS FUNCTIONALITY OBJECT METHODS - -=cut - -#///////////////////////////////////////////////////////////////////////////// - -=head2 create_directory - - Parameters : $directory_path - Returns : boolean - Description : Creates a directory on a datastore on the VM host using the - vSphere SDK. - -=cut - -sub create_directory { - 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 and check the directory path argument - my $directory_path = $self->_get_datastore_path(shift) || return; - - # Check if the directory already exists - return 1 if $self->file_exists($directory_path); - - # Get the VM host name - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - # Get a fileManager object - my $file_manager = $self->_get_file_manager_view() || return; - - # Override the die handler because MakeDirectory may call it - local $SIG{__DIE__} = sub{}; - - # Attempt to create the directory - eval { $file_manager->MakeDirectory(name => $directory_path, - datacenter => $self->_get_datacenter_view(), - createParentDirectories => 1); - }; - - if ($@) { - if ($@->isa('SoapFault') && ref($@->detail) eq 'FileAlreadyExists') { - notify($ERRORS{'DEBUG'}, 0, "directory already exists: '$directory_path'"); - return 1; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to create directory: '$directory_path'\nerror:\n$@"); - return; - } - } - else { - notify($ERRORS{'OK'}, 0, "created directory: '$directory_path'"); - return 1; - } -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 delete_file - - Parameters : $file_path - Returns : boolean - Description : Deletes the file from a datastore on the VM host. Wildcards may - not be used in the file path argument. - -=cut - -sub delete_file { - 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 and check the file path argument - my $path_argument = shift; - if (!$path_argument) { - notify($ERRORS{'WARNING'}, 0, "path argument was not specified"); - return; - } - - my $datastore_path = $self->_get_datastore_path($path_argument); - if (!$datastore_path) { - notify($ERRORS{'WARNING'}, 0, "failed to convert path argument to datastore path: $path_argument"); - return; - } - - # Sanity check, make sure the file path argument is not the root of a datastore - # Otherwise everything in the datastore would be deleted - if ($datastore_path =~ /^\[.+\]$/) { - notify($ERRORS{'CRITICAL'}, 0, "subroutine was called with the file path argument pointing to the root of a datastore, this would cause all datastore contents to be deleted\nfile path argument: '$path_argument'\ndatastore path: '$datastore_path'"); - return; - } - - # Get a fileManager object - my $file_manager = $self->_get_file_manager_view() || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Attempt to delete the file - notify($ERRORS{'OK'}, 0, "attempting to delete file: $datastore_path"); - eval { $file_manager->DeleteDatastoreFile(name => $datastore_path, datacenter => $self->_get_datacenter_view()); }; - if ($@) { - if ($@->isa('SoapFault') && ref($@->detail) eq 'FileNotFound') { - notify($ERRORS{'DEBUG'}, 0, "file does not exist: $datastore_path"); - return 1; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to delete file: $datastore_path, error:\n$@"); - return; - } - } - else { - notify($ERRORS{'OK'}, 0, "deleted file: $datastore_path"); - return 1; - } -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 copy_file - - Parameters : $source_file_path, $destination_file_path - Returns : boolean - Description : Copies a file from one datastore location on the VM host to - another datastore location on the VM host. Wildcards may not be - used in the file path argument. - -=cut - -sub copy_file { - 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 and check the file path arguments - my $source_file_path = $self->_get_datastore_path(shift) || return; - my $destination_file_path = $self->_get_datastore_path(shift) || return; - - # Get the VM host name - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - # Get the destination directory path and create the directory if it doesn't exit - my $destination_directory_path = $self->_get_parent_directory_datastore_path($destination_file_path) || return; - $self->create_directory($destination_directory_path) || return; - - # Get a fileManager object - my $file_manager = $self->_get_file_manager_view() || return; - my $datacenter = $self->_get_datacenter_view() || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Attempt to copy the file - notify($ERRORS{'DEBUG'}, 0, "attempting to copy file on VM host $vmhost_hostname: '$source_file_path' --> '$destination_file_path'"); - eval { $file_manager->CopyDatastoreFile(sourceName => $source_file_path, - sourceDatacenter => $datacenter, - destinationName => $destination_file_path, - destinationDatacenter => $datacenter, - force => 0); - }; - - # Check if an error occurred - if ($@) { - if ($@->isa('SoapFault') && ref($@->detail) eq 'FileNotFound') { - notify($ERRORS{'WARNING'}, 0, "source file does not exist on VM host $vmhost_hostname: '$source_file_path'"); - return 0; - } - elsif ($@->isa('SoapFault') && ref($@->detail) eq 'FileAlreadyExists') { - notify($ERRORS{'WARNING'}, 0, "destination file already exists on VM host $vmhost_hostname: '$destination_file_path'"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to copy file on VM host $vmhost_hostname: '$source_file_path' --> '$destination_file_path'\nerror:\n$@"); - return; - } - } - - notify($ERRORS{'OK'}, 0, "copied file on VM host $vmhost_hostname: '$source_file_path' --> '$destination_file_path'"); - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 copy_file_to - - Parameters : $source_file_path, $destination_file_path - Returns : boolean - Description : Copies a file from the management node to a datastore on the VM - host. The complete source and destination file paths must be - specified. Wildcards may not be used. - -=cut - -sub copy_file_to { - 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 source and destination arguments - my $source_file_path = normalize_file_path(shift) || return; - my $destination_file_path = $self->_get_datastore_path(shift) || return; - - # Make sure the source file exists on the management node - if (!-f $source_file_path) { - notify($ERRORS{'WARNING'}, 0, "source file does not exist on the management node: '$source_file_path'"); - return; - } - - # Make sure the destination directory path exists - my $destination_directory_path = $self->_get_parent_directory_datastore_path($destination_file_path) || return; - $self->create_directory($destination_directory_path) || return; - sleep 2; - - # Get the VM host name - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - my $datacenter_name = $self->_get_datacenter_name(); - - # Get the destination datastore name and relative datastore path - my $destination_datastore_name = $self->_get_datastore_name($destination_file_path); - my $destination_relative_datastore_path = $self->_get_relative_datastore_path($destination_file_path); - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Attempt to copy the file -- make a few attempts since this can sometimes fail - return $self->code_loop_timeout( - sub{ - my $response; - eval { $response = VIExt::http_put_file("folder" , $source_file_path, $destination_relative_datastore_path, $destination_datastore_name, $datacenter_name); }; - if ($response->is_success) { - notify($ERRORS{'DEBUG'}, 0, "copied file from management node to VM host: '$source_file_path' --> $vmhost_hostname:'[$destination_datastore_name] $destination_relative_datastore_path'"); - return 1; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to copy file from management node to VM host: '$source_file_path' --> $vmhost_hostname($datacenter_name):'$destination_file_path'\nerror: " . $response->message); - return; - } - }, [], "attempting to copy file from management node to VM host: '$source_file_path' --> $vmhost_hostname:'[$destination_datastore_name] $destination_relative_datastore_path'", 50, 5); - -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 copy_file_from - - Parameters : $source_file_path, $destination_file_path - Returns : boolean - Description : Copies file from a datastore on the VM host to the management - node. The complete source and destination file paths must be - specified. Wildcards may not be used. - -=cut - -sub copy_file_from { - 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 source and destination arguments - my $source_file_path = $self->_get_datastore_path(shift) || return; - my $destination_file_path = normalize_file_path(shift) || return; - - # Get the destination directory path and make sure the directory exists - my $destination_directory_path = $self->_get_parent_directory_normal_path($destination_file_path) || return; - if (!-d $destination_directory_path) { - # Attempt to create the directory - my $command = "mkdir -p -v \"$destination_directory_path\" 2>&1 && ls -1d \"$destination_directory_path\""; - my ($exit_status, $output) = run_command($command, 1); - if (!defined($output)) { - notify($ERRORS{'WARNING'}, 0, "failed to run command to create directory on management node: '$destination_directory_path'\ncommand: '$command'"); - return; - } - elsif (grep(/created directory/i, @$output)) { - notify($ERRORS{'OK'}, 0, "created directory on management node: '$destination_directory_path'"); - } - elsif (grep(/mkdir: /i, @$output)) { - notify($ERRORS{'WARNING'}, 0, "error occurred attempting to create directory on management node: '$destination_directory_path':\ncommand: '$command'\nexit status: $exit_status\noutput:\n" . join("\n", @$output)); - return; - } - elsif (grep(/^$destination_directory_path/, @$output)) { - notify($ERRORS{'OK'}, 0, "directory already exists on management node: '$destination_directory_path'"); - } - else { - notify($ERRORS{'WARNING'}, 0, "unexpected output returned from command to create directory on management node: '$destination_directory_path':\ncommand: '$command'\nexit status: $exit_status\noutput:\n" . join("\n", @$output)); - return; - } - } - - # Get the VM host name - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - my $datacenter_name = $self->_get_datacenter_name(); - - # Get the source datastore name - my $source_datastore_name = $self->_get_datastore_name($source_file_path) || return; - - # Get the source file relative datastore path - my $source_file_relative_datastore_path = $self->_get_relative_datastore_path($source_file_path) || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Attempt to copy the file -- make a few attempts since this can sometimes fail - return $self->code_loop_timeout( - sub{ - my $response; - eval { $response = VIExt::http_get_file("folder", $source_file_relative_datastore_path, $source_datastore_name, $datacenter_name, $destination_file_path); }; - if ($response->is_success) { - notify($ERRORS{'DEBUG'}, 0, "copied file from VM host to management node: $vmhost_hostname:'[$source_datastore_name] $source_file_relative_datastore_path' --> '$destination_file_path'"); - return 1; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to copy file from VM host to management node: $vmhost_hostname:'[$source_datastore_name] $source_file_relative_datastore_path' --> '$destination_file_path'\nerror: " . $response->message); - return; - } - }, [], "attempting to copy file from VM host to management node: $vmhost_hostname:'[$source_datastore_name] $source_file_relative_datastore_path' --> '$destination_file_path'", 50, 5); - -} - - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_file_contents - - Parameters : $file_path - Returns : array - Description : Returns an array containing the contents of the file on the VM - host specified by the file path argument. Each array element - contains a line in the file. - -=cut - -sub get_file_contents { - 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; - } - - # TODO: add file size check before retrieving file in case file is huge - - # Get the source and destination arguments - my ($path) = shift; - if (!$path) { - notify($ERRORS{'WARNING'}, 0, "file path argument was not specified"); - return; - } - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - # Create a temp directory to store the file and construct the temp file path - # The temp directory is automatically deleted then this variable goes out of scope - my $temp_directory_path = tempdir( CLEANUP => 1 ); - my $source_file_name = $self->_get_file_name($path); - my $temp_file_path = "$temp_directory_path/$source_file_name"; - - $self->copy_file_from($path, $temp_file_path) || return; - - # Run cat to retrieve the contents of the file - my $command = "cat \"$temp_file_path\""; - my ($exit_status, $output) = VCL::utils::run_command($command, 1); - if (!defined($output)) { - notify($ERRORS{'WARNING'}, 0, "failed to run command to read file: '$temp_file_path'\ncommand: '$command'"); - return; - } - elsif (grep(/^cat: /, @$output)) { - notify($ERRORS{'WARNING'}, 0, "failed to read contents of file: '$temp_file_path', exit status: $exit_status, output:\n" . join("\n", @$output)); - return; - } - else { - notify($ERRORS{'DEBUG'}, 0, "retrieved " . scalar(@$output) . " lines from file: '$temp_file_path'"); - } - - # Output lines contain trailing newlines, remove them - @$output = map { chomp; $_; } @$output; - return @$output; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 move_file - - Parameters : $source_path, $destination_path - Returns : boolean - Description : Moves or renames a file from one datastore location on the VM - host to another datastore location on the VM host. Wildcards may - not be used in the file path argument. - -=cut - -sub move_file { - 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 and check the file path arguments - my $source_file_path = $self->_get_datastore_path(shift) || return; - my $destination_file_path = $self->_get_datastore_path(shift) || return; - - # Get the VM host name - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - # Get the destination directory path and create the directory if it doesn't exit - my $destination_directory_path = $self->_get_parent_directory_datastore_path($destination_file_path) || return; - $self->create_directory($destination_directory_path) || return; - - # Get a fileManager and Datacenter object - my $file_manager = $self->_get_file_manager_view() || return; - my $datacenter = $self->_get_datacenter_view() || return; - - # Override the die handler - local $SIG{__DIE__} = sub{}; - - # Attempt to copy the file - notify($ERRORS{'DEBUG'}, 0, "attempting to move file on VM host $vmhost_hostname: '$source_file_path' --> '$destination_file_path'"); - eval { $file_manager->MoveDatastoreFile(sourceName => $source_file_path, - sourceDatacenter => $datacenter, - destinationName => $destination_file_path, - destinationDatacenter => $datacenter - ); - }; - - if ($@) { - if ($@->isa('SoapFault') && ref($@->detail) eq 'FileNotFound') { - notify($ERRORS{'WARNING'}, 0, "source file does not exist on VM host $vmhost_hostname: '$source_file_path'"); - return 0; - } - elsif ($@->isa('SoapFault') && ref($@->detail) eq 'FileAlreadyExists') { - notify($ERRORS{'WARNING'}, 0, "destination file already exists on VM host $vmhost_hostname: '$destination_file_path'"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to move file on VM host $vmhost_hostname: '$source_file_path' --> '$destination_file_path', error:\n$@"); - return; - } - } - - notify($ERRORS{'OK'}, 0, "moved file on VM host $vmhost_hostname: '$source_file_path' --> '$destination_file_path'"); - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 file_exists - - Parameters : $file_path - Returns : boolean - Description : Determines if a file exists on a datastore on the VM host. - -=cut - -sub file_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 and check the file path argument - my $file_path = $self->_get_datastore_path(shift) || return; - - # Check if the path argument is the root of a datastore - if ($file_path =~ /^\[(.+)\]$/) { - my $datastore_name = $1; - (my @datastore_names = $self->_get_datastore_names()) || return; - - if (grep(/^$datastore_name$/, @datastore_names)) { - notify($ERRORS{'DEBUG'}, 0, "file (datastore root) exists: $file_path"); - return 1; - } - else { - notify($ERRORS{'DEBUG'}, 0, "file (datastore root) does not exist: $file_path, datastores on VM host:\n" . join("\n", @datastore_names)); - return 0; - } - } - - # Take the path apart, get the filename and parent directory path - my $base_directory_path = $self->_get_parent_directory_datastore_path($file_path) || return; - my $file_name = $self->_get_file_name($file_path) || return; - - my $result = $self->find_files($base_directory_path, $file_name); - if ($result) { - notify($ERRORS{'DEBUG'}, 0, "file exists: $file_path"); - return 1; - } - elsif (defined($result)) { - notify($ERRORS{'DEBUG'}, 0, "file does not exist: $file_path"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to determine if file exists: $file_path"); - return; - } -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_file_size - - Parameters : $file_path - Returns : integer - Description : Determines the size of a file of a datastore in bytes. Wildcards - may be used in the file path argument. The total size of all - files found will be returned. Subdirectories are not searched. - -=cut - -sub get_file_size { - 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 and check the file path argument - my $file_path_argument = shift; - if (!$file_path_argument) { - notify($ERRORS{'WARNING'}, 0, "file path argument was not specified"); - return; - } - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - # Get the file info - my $file_info = $self->_get_file_info($file_path_argument); - if (!defined($file_info)) { - notify($ERRORS{'WARNING'}, 0, "unable to get file size, failed to get file info for: $file_path_argument"); - return; - } - - # Make sure the file info is not null or else an error occurred - if (!$file_info) { - notify($ERRORS{'WARNING'}, 0, "unable to retrieve info for file on $vmhost_hostname: $file_path_argument"); - return; - } - - # Check if there are any keys in the file info hash - no keys indicates no files were found - if (!keys(%{$file_info})) { - notify($ERRORS{'DEBUG'}, 0, "unable to determine size of file on $vmhost_hostname because it does not exist: $file_path_argument"); - return; - } - - # Loop through the files, add their sizes to the total - my $total_size_bytes = 0; - for my $file_path (keys(%{$file_info})) { - my $file_size_bytes = $file_info->{$file_path}{fileSize}; - notify($ERRORS{'DEBUG'}, 0, "size of '$file_path': " . format_number($file_size_bytes) . " bytes"); - $total_size_bytes += $file_size_bytes; - } - - my $total_size_bytes_string = format_number($total_size_bytes); - my $total_size_mb_string = format_number(($total_size_bytes / 1024 / 1024), 2); - my $total_size_gb_string = format_number(($total_size_bytes / 1024 / 1024 /1024), 2); - - notify($ERRORS{'DEBUG'}, 0, "total file size of '$file_path_argument': $total_size_bytes_string bytes ($total_size_mb_string MB, $total_size_gb_string GB)"); - return $total_size_bytes; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 find_files - - Parameters : $base_directory_path, $search_pattern, $search_subdirectories (optional) - Returns : array - Description : Finds files in a datastore on the VM host stored under the base - directory path argument. The search pattern may contain - wildcards. Subdirectories will be searched if the 3rd argument is - not supplied. - -=cut - -sub find_files { - 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 arguments - my ($base_directory_path, $search_pattern, $search_subdirectories) = @_; - if (!$base_directory_path || !$search_pattern) { - notify($ERRORS{'WARNING'}, 0, "base directory path and search pattern arguments were not specified"); - return; - } - - $search_subdirectories = 1 if !defined($search_subdirectories); - - $base_directory_path = $self->_get_normal_path($base_directory_path) || return; - - # Get the file info - my $file_info = $self->_get_file_info("$base_directory_path/$search_pattern", $search_subdirectories); - if (!defined($file_info)) { - notify($ERRORS{'WARNING'}, 0, "unable to find files, failed to get file info for: $base_directory_path/$search_pattern"); - return; - } - - # Loop through the keys of the file info hash - my @file_paths; - for my $file_path (keys(%{$file_info})) { - # Add the file path to the return array - push @file_paths, $self->_get_normal_path($file_path); - - # vmdk files will have a diskExtents key - # The extents must be added to the return array - if (defined($file_info->{$file_path}->{diskExtents})) { - for my $disk_extent (@{$file_info->{$file_path}->{diskExtents}}) { - # Convert the datastore file paths to normal file paths - $disk_extent = $self->_get_normal_path($disk_extent); - push @file_paths, $self->_get_normal_path($disk_extent); - } - } - } - - @file_paths = sort @file_paths; - notify($ERRORS{'DEBUG'}, 0, "matching file count: " . scalar(@file_paths)); - return @file_paths; -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_total_space - - Parameters : $path - Returns : integer - Description : Returns the total size (in bytes) of the volume specified by the - argument. - -=cut - -sub get_total_space { - 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 path argument - my $path = shift; - if (!$path) { - notify($ERRORS{'WARNING'}, 0, "path argument was not specified"); - return; - } - - # Get the datastore name - my $datastore_name = $self->_get_datastore_name($path) || return; - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - # Get the datastore info hash - my $datastore_info = $self->_get_datastore_info() || return; - - my $total_bytes = $datastore_info->{$datastore_name}{capacity}; - if (!defined($total_bytes)) { - notify($ERRORS{'WARNING'}, 0, "datastore $datastore_name capacity key does not exist in datastore info:\n" . format_data($datastore_info)); - return; - } - - notify($ERRORS{'DEBUG'}, 0, "capacity of $datastore_name datastore on $vmhost_hostname: " . get_file_size_info_string($total_bytes)); - return $total_bytes; -} - - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_available_space - - Parameters : $path - Returns : integer - Description : Returns the bytes available in the path specified by the - argument. - -=cut - -sub get_available_space { - my $self = shift; - if (ref($self) !~ /module/i) { - notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); - return; - } - - # Get the path argument - my $path = shift; - if (!$path) { - notify($ERRORS{'WARNING'}, 0, "path argument was not specified"); - return; - } - - # Get the datastore name - my $datastore_name = $self->_get_datastore_name($path) || return; - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - # Get the datastore info hash - my $datastore_info = $self->_get_datastore_info(1) || return; - - my $available_bytes = $datastore_info->{$datastore_name}{freeSpace}; - if (!defined($available_bytes)) { - notify($ERRORS{'WARNING'}, 0, "datastore $datastore_name freeSpace key does not exist in datastore info:\n" . format_data($datastore_info)); - return; - } - - notify($ERRORS{'DEBUG'}, 0, "space available in $datastore_name datastore on $vmhost_hostname: " . get_file_size_info_string($available_bytes)); - return $available_bytes; -} - -#///////////////////////////////////////////////////////////////////////////// - -=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) !~ /module/i) { - notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); - return; - } - - return $self->{cpu_core_count} if $self->{cpu_core_count}; - - my $cpu_core_count; - if (my $host_system_view = $self->_get_host_system_view()) { - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - $cpu_core_count = $host_system_view->{hardware}->{cpuInfo}->{numCpuCores}; - notify($ERRORS{'DEBUG'}, 0, "retrieved CPU core count for VM host '$vmhost_hostname': $cpu_core_count"); - } - elsif (my $cluster = $self->_get_cluster_view()) { - # Try to get CPU core count of cluster if cluster is being used - my $cluster_name = $cluster->{name}; - $cpu_core_count = $cluster->{summary}->{numCpuCores}; - notify($ERRORS{'DEBUG'}, 0, "retrieved CPU core count for '$cluster_name' cluster: $cpu_core_count"); - - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to determine CPU core count of VM host"); - return; - } - - $self->{cpu_core_count} = $cpu_core_count; - return $self->{cpu_core_count}; -} - -#///////////////////////////////////////////////////////////////////////////// - -=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) !~ /module/i) { - notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); - return; - } - - return $self->{cpu_speed} if $self->{cpu_speed}; - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - # Try to get CPU speed of resource pool - if (my $resource_pool = $self->_get_resource_pool_view()) { - my $resource_pool_name = $resource_pool->{name}; - - my $mhz = $resource_pool->{runtime}{cpu}{maxUsage}; - - # maxUsage reports sum of all CPUs - divide by core count - # This isn't exact - will be lower than acutal clock rate of CPUs in host - if (my $cpu_core_count = $self->get_cpu_core_count()) { - $mhz = int($mhz / $cpu_core_count); - } - - $self->{cpu_speed} = $mhz; - notify($ERRORS{'DEBUG'}, 0, "retrieved total CPU speed of '$resource_pool_name' resource pool: $self->{cpu_speed} MHz"); - return $self->{cpu_speed}; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to determine CPU speed of VM host, resource pool view object could not be retrieved"); - 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) !~ /module/i) { - notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); - return; - } - - return $self->{total_memory} if $self->{total_memory}; - - my $vmhost_hostname = $self->data->get_vmhost_hostname(); - - - # Try to get total memory of resource pool - if (my $resource_pool = $self->_get_resource_pool_view()) { - my $resource_pool_name = $resource_pool->{name}; - - my $memory_bytes = $resource_pool->{runtime}{memory}{maxUsage}; - my $memory_mb = int($memory_bytes / 1024 / 1024); - - $self->{total_memory} = $memory_mb; - notify($ERRORS{'DEBUG'}, 0, "retrieved total memory of '$resource_pool_name' resource pool: $self->{total_memory} MB"); - return $self->{total_memory}; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to determine total memory on VM host, resource pool view object could not be retrieved"); - return; - } -} - -#///////////////////////////////////////////////////////////////////////////// - -=head2 get_license_info - - Parameters : none
[... 5038 lines stripped ...]
