#!/usr/bin/perl -w
###############################################################################
# $Id: vbox.pm 1798488 2017-06-12 16:38:02Z arkurth $ 
###############################################################################
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###############################################################################

=head1 NAME

VCL::Provisioning::vbox - VCL module to support SUN Virtual Box Provisioning

=head1 SYNOPSIS

 Needs to be written

=head1 DESCRIPTION

 This module provides VCL support for SUN Virtual Box.

=cut

###############################################################################
package VCL::Module::Provisioning::vbox;

# 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.5';

# Specify the version of Perl to use
use 5.008000;

use strict;
use warnings;
use diagnostics;
use English qw(-no_match_vars);

use VCL::utils;
use Fcntl qw(:DEFAULT :flock);

###############################################################################

=head1 OBJECT METHODS

=cut

#//////////////////////////////////////////////////////////////////////////////

=head2 provision

 Parameters  : hash
 Returns     : 1(success) or 0(failure)
 Description : loads node with provided image

=cut

sub load {
	my $self = shift;
	if (ref($self) !~ /vbox/i) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return 0;
	}

	my $request_data = shift;
	my ($package, $filename, $line, $sub) = caller(0);

	# Store some hash variables into local variables
	my $request_id     = $self->data->get_request_id;
	my $reservation_id = $self->data->get_reservation_id;
	my $persistent     = $self->data->get_request_forimaging;
	
	my $management_node_keys     = $self->data->get_management_node_keys();

	my $image_os_type  = $self->data->get_image_os_type;

	my $vmclient_computerid = $self->data->get_computer_id;
	my $computer_shortname  = $self->data->get_computer_short_name;
	my $computer_nodename   = $computer_shortname;
	my $computer_hostname   = $self->data->get_computer_hostname;

	my $vmprofile_vmdisk  = $self->data->get_vmhost_profile_vmdisk;
	my $datastorepath     = $self->data->get_vmhost_profile_datastore_path;
	my $virtualswitch0    = $self->data->get_vmhost_profile_virtualswitch0;
	my $virtualswitch1    = $self->data->get_vmhost_profile_virtualswitch1;

	my $requestedimagename = $self->data->get_image_name;
	my $shortname          = $computer_shortname;

	my $vmhost_imagename          = $self->data->get_vmhost_image_name;

	my $vmhost_vmpath           = $self->data->get_vmhost_profile_vmpath;

	my $vmhost_hostname           = $self->data->get_vmhost_hostname;
	my $vmclient_eth0MAC          = $self->data->get_computer_eth0_mac_address;
	my $vmclient_eth1MAC          = $self->data->get_computer_eth1_mac_address;
	my $vmclient_imageminram      = $self->data->get_image_minram;
	my $vmhost_RAM                = $self->data->get_vmhost_ram;
	my $vmclient_drivetype        = $self->data->get_computer_drive_type;
	my $vmclient_privateIPaddress = $self->data->get_computer_public_ip_address;
	my $vmclient_publicIPaddress  = $self->data->get_computer_private_ip_address;
	my $vmclient_OSname           = $self->data->get_image_os_name;
	
	my $image_repository_path     = $self->_get_image_repository_path();
	
	# Assemble a consistent prefix for notify messages
	my $notify_prefix = "req=$request_id, res=$reservation_id:";
	my $vm_name;
	my @sshcmd;
	insertloadlog($reservation_id, $vmclient_computerid, "startload", "$computer_shortname $requestedimagename");
	my $starttime = convert_to_epoch_seconds;

	my ($hostnode);
	$hostnode = $vmhost_hostname;
	if (!(defined($management_node_keys))) {
		notify($ERRORS{'CRITICAL'}, 0, "identity variable not defined, setting to blade identity file vmhost variable to $vmhost_imagename");
	}

	notify($ERRORS{'OK'}, 0, "identity file set $management_node_keys  vmhost imagename $vmhost_imagename bladekey ");
	#setup flags
	my $baseisregistered = 0;
	my $baseexists   = 0;
	my $dirstructure = 0;

	#for convienence
	my ($myimagename, $myvmx, $myvmdir, $mybasedirname, $requestedimagenamebase);

	# preform cleanup
	if ($self->control_vm("remove")) {
		notify($ERRORS{'OK'}, 0, "removed node $shortname from vmhost $vmhost_hostname");
	}

	### FIX-ME: I have no freakin clue how to approach this (imaging mode) at the moment
	###         For VBox, this would require changing the disk mode from immuatable to normal
	###         which itself would be easy, the challenge for me is handeling the hypervisors that have this image registered
	###         where any VMs that are using it will have associated snapshots that will have to be delt with before the image
	###         could be un-registered and re-registered. so for now, @#$% it... (david.hutchins)
	if ($persistent) {
		$vm_name = "$requestedimagename\_IMAGING\_$shortname"; 
	} ## end if ($persistent)
	else {
		$vm_name = "$requestedimagename\_$shortname";
	}
	
	$myimagename   = $requestedimagename;

	notify($ERRORS{'DEBUG'}, 0, "persistent= NOT IMPLEMENTED");
	notify($ERRORS{'DEBUG'}, 0, "myimagename= $myimagename");
	notify($ERRORS{'OK'}, 0, "vmpath= $vmhost_vmpath");

	#does the requested base vbox image files already existed on the vmhost
	notify($ERRORS{'OK'}, 0, "checking for base image $requestedimagename on $hostnode ");
	insertloadlog($reservation_id, $vmclient_computerid, "vmround1", "checking host for requested image files");

	#check for lock file - another process might be copying the same image files to the same host server
	my $tmplockfile = "/tmp/$hostnode" . "$requestedimagename" . "lock";
	notify($ERRORS{'OK'}, 0, "trying to create exclusive lock on $tmplockfile while checking if image files exist on host");
	if (sysopen(TMPLOCK, $tmplockfile, O_RDONLY | O_CREAT)) {
		if (flock(TMPLOCK, LOCK_EX)) {
			notify($ERRORS{'OK'}, 0, "owning exclusive lock on $tmplockfile");
			notify($ERRORS{'OK'}, 0, "listing datestore $datastorepath ");

			# Check to see if the baseimage is registered with VirtualBox on this host. 
			undef @sshcmd;
			#@sshcmd = run_ssh_command($hostnode, $management_node_keys, "ls -1 $datastorepath", "root");
			@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list hdds", "root");
			notify($ERRORS{'OK'}, 0, "Hdds in VirtualBox database on vm host:\n@{ $sshcmd[1] }");
			foreach my $l (@{$sshcmd[1]}) {
				if ($l =~ /(\s*?)$datastorepath\/$myimagename/) {
					# The base is registered, so we will assume it is also present (This may not be the best approach, but for now it will do).
					notify($ERRORS{'OK'}, 0, "base image exists");
					$baseisregistered = 1;
					$baseexists = 1;
				}
			} ## end foreach my $l (@{$sshcmd[1]})

			# If the base is not registered, we will check to see if it exists
			if (!($baseisregistered)) {
				undef @sshcmd;
				@sshcmd = run_ssh_command($hostnode, $management_node_keys, "ls -1 $datastorepath", "root");
				foreach my $l (@{$sshcmd[1]}) {
					if ($l =~ /(\s*?)$myimagename/) {
						# The base exists so we just need to register it with VirtualBox.
						notify($ERRORS{'OK'}, 0, "base image exists, registering it with VirtualBox");
						$baseexists = 1;
					}
				}
			}


			if (!($baseexists)) {
				#check available disk space -- clean up if needed
				#copy vm files from local repository to vmhost
				#this could take a few minutes
				#get size of  vmdl files

				insertloadlog($reservation_id, $vmclient_computerid, "info", "image files do not exist on host server, preparing to copy");
				my $myvmdkfilesize = 0;
				if (open(SIZE, "du -k $image_repository_path\/$requestedimagename 2>&1 |")) {
					my @du = <SIZE>;
					close(SIZE);
					foreach my $d (@du) {
						if ($d =~ /No such file or directory/) {
							insertloadlog($reservation_id, $vmclient_computerid, "failed", "could not collect size of local image files");
							notify($ERRORS{'CRITICAL'}, 0, "problem checking local vm file size on $image_repository_path\/$requestedimagename");
							close(TMPLOCK);
							unlink($tmplockfile);
							return 0;
						}
						if ($d =~ /^([0-9]*)/) {
							$myvmdkfilesize += $1;
						}
					} ## end foreach my $d (@du)
				} ## end if (open(SIZE, "du -k $image_repository_path/$requestedimagename 2>&1 |"...

				notify($ERRORS{'DEBUG'}, 0, "file size $myvmdkfilesize of $requestedimagename");
				if ($vmprofile_vmdisk =~ /(local|dedicated)/) {
					notify($ERRORS{'OK'}, 0, "copying base image files $requestedimagename to $hostnode");
					if (run_scp_command("$image_repository_path\/$requestedimagename", "$hostnode:\"$datastorepath\/\"", $management_node_keys)) {
						#recheck host server for files - the  scp output is not being captured
						undef @sshcmd;
						@sshcmd = run_ssh_command($hostnode, $management_node_keys, "ls -1 $datastorepath", "root");
						foreach my $l (@{$sshcmd[1]}) {
							if ($l =~ /denied|No such/) {
								notify($ERRORS{'CRITICAL'}, 0, "node $hostnode output @{ $sshcmd[1] }");
								insertloadlog($reservation_id, $vmclient_computerid, "failed", "could not log into vmhost $hostnode @{ $sshcmd[1] }");
								close(TMPLOCK);
								unlink($tmplockfile);
								return 0;
							}
							if ($l =~ /(\s*?)$requestedimagename$/) {
								notify($ERRORS{'OK'}, 0, "base image exists");
								$baseexists = 1;
								insertloadlog($reservation_id, $vmclient_computerid, "transfervm", "copying base image files");
							}
						} ## end foreach my $l (@{$sshcmd[1]})

					} ## end if (run_scp_command("$image_repository_path/$requestedimagename"...
					else {
						notify($ERRORS{'CRITICAL'}, 0, "problems scp vm files to $hostnode $!");
						close(TMPLOCK);
						unlink($tmplockfile);
						return 0;
					}
				} ## end if ($vmprofile_vmdisk =~ /(local|dedicated)/)
				notify($ERRORS{'OK'}, 0, "confirm image exist process complete removing lock on $tmplockfile");
				close(TMPLOCK);
				unlink($tmplockfile);

			}    # start if base not exists
			# If the base exists but was not registered we just need to register it
			if ((!($baseisregistered)) && ($baseexists)) {
				undef @sshcmd;

				# So Oracle removed the method for registering an image with the server. Registration is now automated when media is attached to a VM. But a "read lock" error is given if you attempt to specify "-mtype multiattach" after the first attachment to a vm if the first vm is running. In order to avoid extra logic to determine if it is the first attachment during VM creation, a non-running VM is registered named "STORAGE_HOLDER" with a scsi controller named "STORAGE_HOLDER_SCSI". An image can be attached to port 0 in multiattach mode and any further attachments will default to multiattach when no mtype is specified, without the mtype arg no error is thrown. This feels more like a VBox bug to me, and I opened a bug report with Oracle.

				@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage storageattach STORAGE_HOLDER --storagectl STORAGE_HOLDER_SCSI --medium $datastorepath\/$myimagename --mtype multiattach --type hdd --port 0", "root");
				foreach my $l (@{$sshcmd[1]}) {
					if ($l =~ /(\s*?)ERROR:/) {
						# Registeration failed, manual intervention is probably required, send warning and die.
						notify($ERRORS{'CRITICAL'}, 0, "Registeration of image failed, output is: \n@{ $sshcmd[1] }");
						close(TMPLOCK);
						unlink($tmplockfile);
						return 0;
					} 
					else {
						# Registeration success.
						notify($ERRORS{'OK'}, 0, "IMG REGISTRATION-> $l");
						$baseisregistered = 1;
					}
					
				}
			} else {
				#base exists
				notify($ERRORS{'OK'}, 0, "confirm image exist process complete removing lock on $tmplockfile");
				close(TMPLOCK);
				unlink($tmplockfile);
			}
		}    #flock
	}    #sysopen
	#ok good base vm files exist on hostnode
	#if guest dirstructure exists check state of vm, else create sturcture and new vmx file
	#check for dependent settings ethX
	if (!(defined($vmclient_eth0MAC))) {
		#complain
		notify($ERRORS{'CRITICAL'}, 0, "eth0MAC is not defined for $computer_shortname can not continue");
		insertloadlog($reservation_id, $vmclient_computerid, "failed", "eth0MAC address is not defined");
		return 0;

	}

	#check for memory settings
	my $dynamicmemvalue = "128";
	if (defined($vmclient_imageminram)) {
		#preform some sanity check
		if (($dynamicmemvalue < $vmclient_imageminram) && ($vmclient_imageminram < $vmhost_RAM)) {
			$dynamicmemvalue = $vmclient_imageminram;
			notify($ERRORS{'OK'}, 0, "setting memory to $dynamicmemvalue");
		}
		else {
			notify($ERRORS{'WARNING'}, 0, "image memory value $vmclient_imageminram out of the expected range in host machine $vmhost_RAM setting to 128");
		}
	} ## end if (defined($vmclient_imageminram))
	
	VBOXCREATE:
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q setproperty machinefolder $vmhost_vmpath", "root");
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q createvm --name $vm_name --register", "root");
	$vmclient_eth0MAC =~ tr/://d;
	$vmclient_eth1MAC =~ tr/://d;
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --memory $dynamicmemvalue", "root");
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --ioapic on", "root");
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --nic1 bridged --bridgeadapter1 $virtualswitch0 --macaddress1 $vmclient_eth0MAC --nictype1 82540EM", "root");
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --nic2 bridged --bridgeadapter2 $virtualswitch1 --macaddress2 $vmclient_eth1MAC --nictype2 82540EM", "root");
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q storagectl $vm_name --name $shortname\_stor --add ide", "root");
	if ($persistent) {
		notify($ERRORS{'OK'}, 0, "Cloning image, this could take a while.");
		undef @sshcmd;
		@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q clonehd $datastorepath\/$requestedimagename $datastorepath\/$requestedimagename\_IMAGING\_$shortname ", "root");
		undef @sshcmd;
		@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q storageattach $vm_name --storagectl $shortname\_stor --port 0 --device 0 --type hdd --medium $datastorepath\/$requestedimagename\_IMAGING\_$shortname", "root"); 
	} ## end if ($persistent)
	else {
		undef @sshcmd;
		@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q storageattach $vm_name --storagectl $shortname\_stor --port 0 --device 0 --type hdd --medium $datastorepath\/$requestedimagename", "root");
	}
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --pae on", "root");

	#turn on vm
	#set loop control
	my $vbox_starts = 0;

	VBOXSTART:

	$vbox_starts++;
	notify($ERRORS{'OK'}, 0, "starting vm $vm_name - pass $vbox_starts");
	if ($vbox_starts > 2) {
		notify($ERRORS{'CRITICAL'}, 0, "VirtualBox starts exceeded limit vbox_starts= $vbox_starts hostnode= $hostnode vm= $computer_shortname");
		insertloadlog($reservation_id, $vmclient_computerid, "failed", "could not load machine on $hostnode exceeded attempts");
		return 0;
	}

	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q startvm $vm_name --type headless", "root");
	for my $l (@{$sshcmd[1]}) {
		next if ($l =~ /Waiting/);
		#if successful -- this cmd does not appear to return any ouput so anything could be a failure
		if ($l =~ /successfully started/) {
			notify($ERRORS{'OK'}, 0, "started $vm_name on $hostnode: $l");
		}
		else {
			notify($ERRORS{'OK'}, 0, "Unknown output when trying to start $vm_name on $hostnode \n@{ $sshcmd[1] }");
		}
	} ## end for my $l (@{$sshcmd[1]})
	insertloadlog($reservation_id, $vmclient_computerid, "startvm", "started vm on $hostnode");

	my $sshd_attempts = 0;

	VBOXROUND2:
	
	insertloadlog($reservation_id, $vmclient_computerid, "vmround2", "waiting for ssh to become active");
	
	if ($self->os->can("post_load")) {
		notify($ERRORS{'DEBUG'}, 0, "calling " . ref($self->os) . "->post_load()");
		if ($self->os->post_load()) {
			notify($ERRORS{'DEBUG'}, 0, "successfully ran OS post_load subroutine");
		}
		else {
			my $vm_state = $self->power_status() || 'unknown';
			notify($ERRORS{'WARNING'}, 0, "failed to run OS post_load subroutine, VM state: $vm_state");
			return;
		}
	}
	else {
		notify($ERRORS{'DEBUG'}, 0, ref($self->os) . "::post_load() has not been implemented");
	}
	
	insertloadlog($reservation_id, $vmclient_computerid, "info", "starting post configurations on node");
	
	#clear ssh public keys from /root/.ssh/known_hosts
	my $known_hosts = "/root/.ssh/known_hosts";
	my $ssh_keyscan = "/usr/bin/ssh-keyscan";
	my $port        = "22";
	my @file;
	if (open(FILE, $known_hosts)) {
		@file = <FILE>;
		close FILE;
	
		foreach my $line (@file) {
			if ($line =~ s/$computer_shortname.*\n//) {
				notify($ERRORS{'OK'}, 0, "removing $computer_shortname ssh public key from $known_hosts");
			}
			if ($line =~ s/$vmclient_privateIPaddress}.*\n//) {
				notify($ERRORS{'OK'}, 0, "removing $vmclient_privateIPaddress ssh public key from $known_hosts");
			}
		}
	
		if (open(FILE, ">$known_hosts")) {
			print FILE @file;
			close FILE;
		}
		#sync new keys
		if (open(KEYSCAN, "$ssh_keyscan -t rsa -p $port $computer_shortname >> $known_hosts 2>&1 |")) {
			my @ret = <KEYSCAN>;
			close(KEYSCAN);
			foreach my $r (@ret) {
				notify($ERRORS{'OK'}, 0, "$r");
			}
		}
	} ## end if (open(FILE, $known_hosts))
	else {
		notify($ERRORS{'OK'}, 0, "could not open $known_hosts for editing the $computer_shortname public ssh key");
	}

	insertloadlog($reservation_id, $vmclient_computerid, "vboxready", "preformed post config on node");
	return 1;

} ## end sub load


#//////////////////////////////////////////////////////////////////////////////

=head2 capture

 Parameters  : $request_data_hash_reference
 Returns     : 1 if sucessful, 0 if failed
 Description : Creates a new vbox image.

=cut

sub capture { ## This is going to need to be implemented before the module is complete, but at the moment the focus is on complete VM provisioning.
	my $self = shift;
	if (ref($self) !~ /vbox/i) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return 0;
	}
	my $request_id         = $self->data->get_request_id;
	my $request_state_name = $self->data->get_request_state_name();
	my $reservation_id     = $self->data->get_reservation_id;
	my $management_node_keys     = $self->data->get_management_node_keys();

	my $requestedimagename = $self->data->get_image_name;

	my $datastorepath     = $self->data->get_vmhost_profile_datastore_path;

	my $image_id       = $self->data->get_image_id;
	my $image_os_name  = $self->data->get_image_os_name;
	my $image_os_type  = $self->data->get_image_os_type;
	my $image_name     = $self->data->get_image_name();

	my $computer_id        = $self->data->get_computer_id;
	my $computer_shortname = $self->data->get_computer_short_name;
	my $computer_nodename  = $computer_shortname;
	my $computer_hostname  = $self->data->get_computer_hostname;
	my $computer_type      = $self->data->get_computer_type;
	
	my $vmhost_vmpath           = $self->data->get_vmhost_profile_vmpath;
	my $vmprofile_vmdisk        = $self->data->get_vmhost_profile_vmdisk;
	my $vmprofile_datastorepath = $self->data->get_vmhost_profile_datastore_path;
	my $vmhost_hostname         = $self->data->get_vmhost_hostname;
	my $host_type               = $self->data->get_vmhost_type;
	my $vmhost_imagename        = $self->data->get_vmhost_image_name;

	my $image_repository_path   = $self->_get_image_repository_path();
	my $hostnodename = $vmhost_hostname;
	# Assemble a consistent prefix for notify messages
	my $notify_prefix = "req=$request_id, res=$reservation_id:";
	my $image_filename;


	# Print some preliminary information
	notify($ERRORS{'OK'}, 0, "$notify_prefix new name: $image_name");
	notify($ERRORS{'OK'}, 0, "$notify_prefix computer_name: $computer_shortname");
	notify($ERRORS{'OK'}, 0, "$notify_prefix vmhost_hostname: $vmhost_hostname");

	my @sshcmd;

	# Check if pre_capture() subroutine has been implemented by the OS module
	if ($self->os->can("pre_capture")) {
		# Call OS pre_capture() - it should perform all OS steps necessary to capture an image
		# pre_capture() should shut down the computer when it is done
		notify($ERRORS{'OK'}, 0, "calling OS module's pre_capture() subroutine");

		if (!$self->os->pre_capture({end_state => 'off'})) {
			notify($ERRORS{'WARNING'}, 0, "OS module pre_capture() failed");
			return 0;
		}

		# Get the power status, make sure computer is off
		my $power_status = $self->power_status();
		notify($ERRORS{'DEBUG'}, 0, "retrieved power status: $power_status");
		if ($power_status eq 'off') {
			notify($ERRORS{'OK'}, 0, "verified $computer_nodename power is off");
		}
		elsif ($power_status eq 'on') {
			notify($ERRORS{'WARNING'}, 0, "$computer_nodename power is still on, turning computer off");

			# Attempt to power off computer
			if ($self->power_off()) {
				notify($ERRORS{'OK'}, 0, "$computer_nodename was powered off");
			}
			else {
				notify($ERRORS{'WARNING'}, 0, "failed to power off $computer_nodename");
				return 0;
			}
		}
		else {
			notify($ERRORS{'WARNING'}, 0, "failed to determine power status of $computer_nodename");
			return 0;
		}
	}
	
	if ($vmprofile_vmdisk =~ /(local|dedicated)/) {
		# copy vdi files
		# confirm they were copied

		undef @sshcmd;
		@sshcmd = run_ssh_command($hostnodename, $management_node_keys, "ls $datastorepath/*_IMAGING_$computer_shortname", "root");
		for my $l (@{$sshcmd[1]}) {
			if ($l =~ /\/(.*_IMAGING_$computer_shortname)/) {
				$image_filename = $l;
				notify($ERRORS{'OK'}, 0, "Image filename is: $image_filename");
			}
		} ## end for my $l (@{$sshcmd[1]})

		notify($ERRORS{'OK'}, 0, "attemping to copy vdi file to $image_repository_path");
		if (run_scp_command("$hostnodename:\"$image_filename\"", "$image_repository_path\/$image_name", $management_node_keys)) {

		# set file premissions on images to 644
		# to allow for other management nodes to fetch image if neccessary
		# useful in a large distributed framework
		if (open(CHMOD, "/bin/chmod -R 644 $image_repository_path\/$image_name 2>&1 |")) {
			close(CHMOD);
			notify($ERRORS{'DEBUG'}, 0, "$notify_prefix recursive update file permssions 644 on $image_repository_path\/$image_name");
		}
		undef @sshcmd;
		@sshcmd = run_ssh_command($hostnodename, $management_node_keys, "VBoxManage closemedium disk $datastorepath/$image_filename --delete", "root");
		return 1;
		} ## end if (run_scp_command("$hostnodename:\"$vmhost_vmpath/$vmx_directory/*.vmdk\""...
		else {
			notify($ERRORS{'CRITICAL'}, 0, "failed to copy .vdi file to image repository");
			return 0;
		}
		
		if ($request_state_name !~ /^(image)$/) {
			notify($ERRORS{'OK'}, 0, "VM will NOT be deleted because the request state is '$request_state_name'");
		}
		else {
			# Image has been captured, remove the VM
			$self->control_vm("remove");
		}
	} elsif ($vmprofile_vmdisk =~ /shared/) { ## end if ($vmprofile_vmdisk =~ /(local|dedicated)/)
		
		if (open(FILELIST, "/bin/ls $image_repository_path/*_IMAGING_$computer_shortname 2>&1 |")) {
			my @filelist = <FILELIST>;
			close(FILELIST);
			my $size = 0;
			foreach my $f (@filelist) {
				if ($f =~ /\/(.*_IMAGING_$computer_shortname)/) {
					$image_filename = $f;
					$image_filename =~ s/(\r|\n)//g;
					notify($ERRORS{'OK'}, 0, "Image filename is: $image_filename");
				}
			}	
		} 
		
		if (open(MV, "/bin/mv $image_filename $image_repository_path\/$image_name 2>&1 |")) {
			close(MV);
			notify($ERRORS{'OK'}, 0, "renamed $image_filename to $image_repository_path\/$image_name");
			if (open(CHMOD, "/bin/chmod -R 644 $image_repository_path\/$image_name 2>&1 |")) {
				close(CHMOD);
				notify($ERRORS{'DEBUG'}, 0, "$notify_prefix recursive update file permssions 644 on $image_repository_path\/$image_name");
			}
			#undef @sshcmd;
			#@sshcmd = run_ssh_command($hostnodename, $management_node_keys, "VBoxManage closemedium disk $datastorepath/$image_filename --delete", "root");
			return 1;
		}
	}

} ## end sub capture

#//////////////////////////////////////////////////////////////////////////////

=head2 remove_snapshots

 Parameters  : n/a
 Returns     : 1 if sucessful, 0 if failed
 Description : removes any unused snapshot hdds from specified host.

=cut

sub remove_snapshots {
	my $self = shift;
	if (ref($self) !~ /vbox/i) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return 0;
	}
	my $vmhost_fullhostname = $self->data->get_vmhost_hostname;
	my $hostnode = $1 if ($vmhost_fullhostname =~ /([-_a-zA-Z0-9]*)(\.?)/);
	my $management_node_keys = $self->data->get_management_node_keys();
	my @sshcmd;
	my @sshcmd2;
	my $is_snapshot = 0;
	my $delete_flag = 0;
	my $current_uuid;
	notify($ERRORS{'OK'}, 0, "Removing unused snapshots");
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list hdds | sed s/^\$/----/g", "root");
	foreach my $l (@{$sshcmd[1]}) {
		if ($l =~ m/^UUID/) { # UUID line is the start of a section
			$l =~ m/(........-....-....-....-............)/;
			$current_uuid = $1;
			notify($ERRORS{'OK'}, 0, "Checking hdd with UUID $1");
			$is_snapshot = 0; # reset this flag
			$delete_flag = 0; # reset this flag
		}
		if ($l =~ m/^Parent/) {
			if ($l =~ m/Parent UUID: base/) { # This is a base image, not a snapshot
				notify($ERRORS{'OK'}, 0, "UUID $current_uuid is not a snapshot");
				$is_snapshot = 0; # Mark as a snapshot.
				$delete_flag = 0; # Default is to remove this snapshot, unless it is found to be in use.
			} else { # This is a snapshot
				$l =~ m/(........-....-....-....-............)/;
				notify($ERRORS{'OK'}, 0, "UUID $current_uuid is a snapshot of $1");
				$is_snapshot = 1; # Mark as a snapshot.
				$delete_flag = 1; # Default is to remove this snapshot, unless it is found to be in use.
			}
		}
		if ($l =~ m/^Usage/) { # This image is still in use
			notify($ERRORS{'OK'}, 0, "UUID $current_uuid is in use, will not be removed");
			$delete_flag = 0; #Will not delete as this is still in use 
		}
		if ($l eq '----') { # end of one section, time to remove the image if it is an unused snapshot.
			if ($is_snapshot && $delete_flag) {
				notify($ERRORS{'OK'}, 0, "UUID $current_uuid is not in use, will be removed");
				@sshcmd2 = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q closemedium disk $current_uuid --delete", "root");
			} 
		}
	}
} ## end sub remove_snapshots 

#//////////////////////////////////////////////////////////////////////////////

=head2 controlVM

 Parameters  : control command hash
 Returns     : 0 or 1
 Description : controls VM, stop,remove, etc

=cut

### This section will be next.

sub control_vm {
	my $self = shift;
	my $ret = 0;

	# Check if subroutine was called as a class method
	if (ref($self) !~ /vbox/i) {
		notify($ERRORS{'DEBUG'}, 0, "subroutine was called as a function, it must be called as a class method");
	}

	my $control = shift;
	#my (%vm) = shift;
	#notify($ERRORS{'CRITICAL'}, 0, "debugging", %vm);

	my ($package, $filename, $line, $sub) = caller(0);

	if (!(defined($control))) {
		notify($ERRORS{'WARNING'}, 0, "control is not defined");
		return 0;
	}
	else {
		notify($ERRORS{'DEBUG'}, 0, "control $control is defined ");
	}

	# Store hash var into local var

	my $shortname  = $self->data->get_computer_short_name;
	my $vmhost_fullhostname = $self->data->get_vmhost_hostname;
	my $hostnode    = $1 if ($vmhost_fullhostname =~ /([-_a-zA-Z0-9]*)(\.?)/);
	my $management_node_keys     = $self->data->get_management_node_keys();
	my $requestedimagename = $self->data->get_image_name;
	my $image_repository_path     = $self->_get_image_repository_path();
	my $vm_name = "$requestedimagename\_$shortname ";
	my @sshcmd;

	if ($control =~ /off|remove/) {
		undef @sshcmd;
		@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list vms", "root");
		foreach my $l (@{$sshcmd[1]}) {
			if ($l =~ m/\_$shortname\"/) {
				$l =~ m/{(.*)}/;         
				notify($ERRORS{'OK'}, 0, "VM $shortname has UUID  $1");
				notify($ERRORS{'OK'}, 0, "UUID  $1 - POWER OFF");
				undef @sshcmd;
				@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q controlvm $1 poweroff", "root");
				if ($control eq 'remove') {
					notify($ERRORS{'OK'}, 0, "UUID  $1 - REMOVE");
					undef @sshcmd;
					@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q unregistervm $1 --delete", "root");
					notify($ERRORS{'OK'}, 0, "Waiting 30 seconds to allow unregister to settle");
					sleep 30
				}
				$ret = 1;
			}
		}
		
	}
	if ($control eq 'pause') {
		undef @sshcmd;
		@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list vms", "root");
		foreach my $l (@{$sshcmd[1]}) {
			if ($l =~ m/\_$shortname\"/) {
				$l =~ m/{(.*)}/;
				notify($ERRORS{'OK'}, 0, "VM $shortname has UUID  $1");
				notify($ERRORS{'OK'}, 0, "UUID  $1 - PAUSE");
				undef @sshcmd;
				@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q controlvm $1 pause", "root");
			}
		}
	}
	if ($control eq 'resume') {
		undef @sshcmd;
		@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list vms", "root");
		foreach my $l (@{$sshcmd[1]}) {
			if ($l =~ m/\_$shortname\"/) {
				$l =~ m/{(.*)}/;
				notify($ERRORS{'OK'}, 0, "VM $shortname has UUID  $1");
				notify($ERRORS{'OK'}, 0, "UUID  $1 - RESUME");
				undef @sshcmd;
				@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q controlvm $1 resume", "root");
			}
		}
	}

	my $baseexists   = 0;
	my $dirstructure = 0;
	my $vmison       = 0;
	return $ret;
} ## end sub control_vm

#//////////////////////////////////////////////////////////////////////////////

=head2  get_image_size

 Parameters  : imagename
 Returns     : 0 failure or size of image
 Description : in size of Kilobytes

=cut

sub get_image_size {
	my $self = shift;
	if (ref($self) !~ /vbox/i) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return 0;
	}

	# Either use a passed parameter as the image name or use the one stored in this object's DataStructure
	my $image_name = shift;
	my $image_repository_path = $self->_get_image_repository_path();
	$image_name = $self->data->get_image_name() if !$image_name;
	if (!$image_name) {
		notify($ERRORS{'CRITICAL'}, 0, "image name could not be determined");
		return 0;
	}
	notify($ERRORS{'DEBUG'}, 0, "getting size of image: $image_name");
	

	my $IMAGEREPOSITORY = "$image_repository_path\/$image_name";

	#list files in image directory, account for main .gz file and any .gz.00X files
	if (open(FILELIST, "/bin/ls -s1 $IMAGEREPOSITORY 2>&1 |")) {
		my @filelist = <FILELIST>;
		close(FILELIST);
		my $size = 0;
		foreach my $f (@filelist) {
			if ($f =~ /$image_name/) {
				my ($presize, $blah) = split(" ", $f);
				$size += $presize;
			}
		}
		if ($size == 0) {
			#strange imagename not found
			return 0;
		}
		return int($size / 1024);
	} ## end if (open(FILELIST, "/bin/ls -s1 $IMAGEREPOSITORY 2>&1 |"...

	return 0;
} ## end sub get_image_size

#//////////////////////////////////////////////////////////////////////////////

=head2 does_image_exist

 Parameters  : imagename
 Returns     : 0 or 1
 Description : scans  our image local image library for requested image
               returns 1 if found or 0 if not
               attempts to scp image files from peer management nodes

=cut

sub does_image_exist {
	my $self = shift;
	unless (ref($self) && $self->isa('VCL::Module')) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
		return;	
	}

	# Get the image name, first try passed argument, then data
	my $image_name = shift;
	$image_name = $self->data->get_image_name() if !$image_name;
	if (!$image_name) {
		notify($ERRORS{'WARNING'}, 0, "unable to determine image name");
		return;
	}

	# Get the image repository path
	my $image_repository_path = $self->_get_image_repository_path();
	if (!$image_repository_path) {
		notify($ERRORS{'WARNING'}, 0, "image repository path could not be determined");
		return;
	}
	else {
		notify($ERRORS{'DEBUG'}, 0, "image repository path: $image_repository_path");
	}
	
	# Run du to get the size of the image files if the image exists
	my $du_command = "du -c $image_repository_path\/*$image_name* 2>&1 | grep total 2>&1";
	my ($du_exit_status, $du_output) = run_command($du_command);
	notify($ERRORS{'OK'}, 0, "$du_command");
	
	# If the partner doesn't have the image, a "no such file" error should be displayed
	my $image_files_exist;
	if (defined($du_output) && grep(/no such file/i, @$du_output)) {
		notify($ERRORS{'OK'}, 0, "$image_name does NOT exist");
		$image_files_exist = 0;
	}
	elsif (defined($du_output) && !grep(/\d+\s+total/i, @$du_output)) {
		notify($ERRORS{'WARNING'}, 0, "du output does not contain a total line:\n" . join("\n", @$du_output));
		return;
	}
	elsif (!defined($du_exit_status)) {
		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to determine if image $image_name exists");
		return;
	}
	
	# Return 1 if the image size > 0
	my ($image_size) = (@$du_output[0] =~ /(\d+)\s+total/);
	if ($image_size && $image_size > 0) {
		my $image_size_mb = int($image_size / 1024);
		notify($ERRORS{'DEBUG'}, 0, "$image_name exists in $image_repository_path, size: $image_size_mb MB");
		return 1;
	}
	else {
		notify($ERRORS{'DEBUG'}, 0, "image does NOT exist: $image_name");
		return 0;
	}
} ## end sub does_image_exist

#//////////////////////////////////////////////////////////////////////////////

=head2 retrieve_image

 Parameters  :
 Returns     :
 Description : Attempts to retrieve an image from an image library partner

=cut

sub retrieve_image {
	my $self = shift;
	unless (ref($self) && $self->isa('VCL::Module')) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
		return;	
	}

	# Make sure image library functions are enabled
	my $image_lib_enable = $self->data->get_management_node_image_lib_enable();
	if (!$image_lib_enable) {
		notify($ERRORS{'OK'}, 0, "image library functions are disabled");
		return;
	}

	# If an argument was specified, use it as the image name
	# If not, get the image name from the reservation data
	my $image_name = shift || $self->data->get_image_name();
	if (!$image_name) {
		notify($ERRORS{'WARNING'}, 0, "unable to determine image name from argument or reservation data");
		return;
	}
	
	# Get the last digit of the reservation ID and sleep that number of seconds
	# This is done in case 2 reservations for the same image were started at the same time
	# Both may attempt to retrieve an image and execute the SCP command at nearly the same time
	# does_image_exist() may not catch this and allow 2 SCP retrieval processes to start
	# It's likely that the reservation IDs are consecutive and the the last digits will be different
	my ($pre_retrieval_sleep) = $self->data->get_reservation_id() =~ /(\d)$/;
	notify($ERRORS{'DEBUG'}, 0, "sleeping for $pre_retrieval_sleep seconds to prevent multiple SCP image retrieval processes");
	sleep $pre_retrieval_sleep;
	
	# Make sure image does not already exist on this management node
	if ($self->does_image_exist($image_name)) {
		notify($ERRORS{'OK'}, 0, "$image_name already exists on this management node");
		return 1;
	}

	# Get the image library partner string
	my $image_lib_partners = $self->data->get_management_node_image_lib_partners();
	if (!$image_lib_partners) {
		notify($ERRORS{'WARNING'}, 0, "image library partners could not be determined");
		return;
	}
	
	# Split up the partner list
	my @partner_list = split(/,/, $image_lib_partners);
	if ((scalar @partner_list) == 0) {
		notify($ERRORS{'WARNING'}, 0, "image lib partners variable is not listed correctly or does not contain any information: $image_lib_partners");
		return;
	}
	
	# Get the local image repository path
	my $image_repository_path_local = $self->_get_image_repository_path();
	if (!$image_repository_path_local) {
		notify($ERRORS{'WARNING'}, 0, "image repository path could not be determined");
		return;
	}
	
	# Loop through the partners
	# Find partners which have the image
	# Check size for each partner
	# Retrieve image from partner with largest image
	# It's possible that another partner (management node) is currently copying the image from another managment node
	# This should prevent copying a partial image
	my $largest_partner;
	my $largest_partner_hostname;
	my $largest_partner_image_lib_user;
	my $largest_partner_image_lib_key;
	my $largest_partner_ssh_port;
	my $largest_partner_path;
	my $largest_partner_size = 0;
	
	notify($ERRORS{'OK'}, 0, "attempting to find another management node that contains $image_name");
	foreach my $partner (@partner_list) {
		# Get the connection information for the partner management node
		my $partner_hostname = $self->data->get_management_node_hostname($partner) || '';
		my $partner_image_lib_user = $self->data->get_management_node_image_lib_user($partner) || '';
		my $partner_image_lib_key = $self->data->get_management_node_image_lib_key($partner) || '';
		my $partner_ssh_port = $self->data->get_management_node_ssh_port($partner) || '';
		my $image_repository_path_remote = $self->_get_image_repository_path($partner);
		
		notify($ERRORS{'OK'}, 0, "checking if $partner_hostname has image $image_name");
		notify($ERRORS{'DEBUG'}, 0, "remote image repository path on $partner: $image_repository_path_remote");
		
		# Run du to get the size of the image files on the partner if the image exists
		my ($du_exit_status, $du_output) = run_ssh_command($partner, $partner_image_lib_key, "du -c $image_repository_path_remote\/*$image_name* | grep total", $partner_image_lib_user, $partner_ssh_port, 1);
		
		# If the partner doesn't have the image, a "no such file" error should be displayed
		if (defined($du_output) && grep(/no such file/i, @$du_output)) {
			notify($ERRORS{'OK'}, 0, "$image_name does NOT exist on $partner_hostname");
			next;
		}
		elsif (defined($du_output) && !grep(/\d+\s+total/i, @$du_output)) {
			notify($ERRORS{'WARNING'}, 0, "du output does not contain a total line:\n" . join("\n", @$du_output));
			next;
		}
		elsif (!defined($du_exit_status)) {
			notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to determine if image $image_name exists on $partner_hostname");
			next;
		}
		
		# Extract the image size in bytes from the du total output line
		my ($partner_image_size) = (@$du_output[0] =~ /(\d+)\s+total/);
		notify($ERRORS{'OK'}, 0, "$image_name exists on $partner_hostname, size: $partner_image_size bytes");
		
		# Check if the image size is larger than any previously found, if so, save the partner info
		if ($partner_image_size > $largest_partner_size) {
			$largest_partner = $partner;
			$largest_partner_hostname = $partner_hostname;
			$largest_partner_size = $partner_image_size;
			$largest_partner_image_lib_user = $partner_image_lib_user;
			$largest_partner_image_lib_key = $partner_image_lib_key;
			$largest_partner_ssh_port = $partner_ssh_port;
			$largest_partner_path = $image_repository_path_remote;
		}
	}
	
	# Check if any partner was found
	if (!$largest_partner) {
		notify($ERRORS{'WARNING'}, 0, "unable to find $image_name on other management nodes");
		return;
	}
	
	# Attempt copy
	notify($ERRORS{'OK'}, 0, "attempting to retrieve $image_name from $largest_partner_hostname");
	if (run_scp_command("$largest_partner_image_lib_user\@$largest_partner:$largest_partner_path/$image_name*", $image_repository_path_local, $largest_partner_image_lib_key, $largest_partner_ssh_port)) {
		notify($ERRORS{'OK'}, 0, "image $image_name was copied from $largest_partner_hostname");
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "failed to copy image $image_name from $largest_partner_hostname");
		return 0;
	}
	
	# Make sure image was copied
	if (!$self->does_image_exist($image_name)) {
		notify($ERRORS{'WARNING'}, 0, "$image_name was not copied to this management node");
		return 0;
	}

	return 1;
} ## end sub retrieve_image

#//////////////////////////////////////////////////////////////////////////////

=head2 _get_image_repository_path

 Parameters  : none, must be called as an object method
 Returns     :
 Description :

=cut

sub _get_image_repository_path {
	my $self = shift;

	if (ref($self) !~ /vbox/i) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return 0;
	}

	my $return_path = $self->data->get_vmhost_profile_repository_path(0);
	return $return_path;
} ## end sub _get_image_repository_path

#//////////////////////////////////////////////////////////////////////////////

=head2 post_maintenance_action

 Parameters  : none, must be called as an object method
 Returns     : boolean
 Description : preforms any actions on node before putting in maintenance state

=cut

sub post_maintenance_action {
	my $self = shift;

	if (ref($self) !~ /vbox/i) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return 0;
	}

	#steps putting vm into maintenance
	# check state of vm
	# turn off if needed
	# unregister
	# remove vm machine directory from vmx path
	# set vmhostid to null in computer table - handled in new.pm

	my $computer_short_name   = $self->data->get_computer_short_name;
	my $computer_id = $self->data->get_computer_id();
	my $vmhost_hostname = $self->data->get_vmhost_hostname;

	if (!$self->control_vm("remove")) {
		notify($ERRORS{'WARNING'}, 0, "failed to remove node $computer_short_name from vmhost $vmhost_hostname");
		return;
	}

	# Set the computer current image in the database to 'noimage'
	if (!update_computer_imagename($computer_id, 'noimage')) {
		notify($ERRORS{'WARNING'}, 0, "failed to set computer $computer_short_name current image to 'noimage'");
	}
	
	if (!switch_vmhost_id($computer_id, 'NULL')) {
		notify($ERRORS{'WARNING'}, 0, "failed to set the vmhostid to NULL for VM $computer_short_name");
	}

	return 1;

} ## end sub post_maintenance_action

#//////////////////////////////////////////////////////////////////////////////

=head2 power_on

 Parameters  : 
 Returns     : 
 Description : 

=cut

sub power_on {
	my $self = shift;
	unless (ref($self) && $self->isa('VCL::Module')) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
		return;	
	}
	
	## Get necessary data
	my $shortname  = $self->data->get_computer_short_name;
	my $vmhost_hostname    = $self->data->get_vmhost_hostname();
	my $vmhost_fullhostname = $self->data->get_vmhost_hostname;
	my $hostnode = $1 if ($vmhost_fullhostname =~ /([-_a-zA-Z0-9]*)(\.?)/);
	my $management_node_keys = $self->data->get_management_node_keys();
	my @sshcmd;
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list vms", "root");
	foreach my $l (@{$sshcmd[1]}) {
		if ($l =~ m/\_$shortname\"/) {
			$l =~ m/{(.*)}/;
			notify($ERRORS{'OK'}, 0, "VM $shortname has UUID  $1");
			notify($ERRORS{'OK'}, 0, "UUID  $1 - POWERON");
			undef @sshcmd;
			@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q  startvm $1 --type headless", "root");
		}
	}
	
	return 1;
}

#//////////////////////////////////////////////////////////////////////////////

=head2 power_off

 Parameters  : 
 Returns     : 
 Description : 

=cut

sub power_off {
	my $self = shift;
	unless (ref($self) && $self->isa('VCL::Module')) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
		return;	
	}
	
	## Get necessary data
	my $shortname  = $self->data->get_computer_short_name;
	my $vmhost_hostname    = $self->data->get_vmhost_hostname();
	my $vmhost_fullhostname = $self->data->get_vmhost_hostname;
	my $hostnode = $1 if ($vmhost_fullhostname =~ /([-_a-zA-Z0-9]*)(\.?)/);
	my $management_node_keys = $self->data->get_management_node_keys();
	my @sshcmd;
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list vms", "root");
	foreach my $l (@{$sshcmd[1]}) {
		if ($l =~ m/\_$shortname\"/) {
			$l =~ m/{(.*)}/;
			notify($ERRORS{'OK'}, 0, "VM $shortname has UUID  $1");
			notify($ERRORS{'OK'}, 0, "UUID  $1 - POWEROFF");
			undef @sshcmd;
			@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q controlvm $1 poweroff", "root");
		}
	}
	
	return 1;
}

#//////////////////////////////////////////////////////////////////////////////

=head2 power_reset

 Parameters  : 
 Returns     : 
 Description : 

=cut

sub power_reset {
	my $self = shift;
	unless (ref($self) && $self->isa('VCL::Module')) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
		return;	
	}
	
	## Get necessary data
	my $shortname  = $self->data->get_computer_short_name;
	my $vmhost_hostname    = $self->data->get_vmhost_hostname();
	my $vmhost_fullhostname = $self->data->get_vmhost_hostname;
	my $hostnode = $1 if ($vmhost_fullhostname =~ /([-_a-zA-Z0-9]*)(\.?)/);
	my $management_node_keys = $self->data->get_management_node_keys();
	my @sshcmd;
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list vms", "root");
	foreach my $l (@{$sshcmd[1]}) {
		if ($l =~ m/\_$shortname\"/) {
			$l =~ m/{(.*)}/;
			notify($ERRORS{'OK'}, 0, "VM $shortname has UUID  $1");
			notify($ERRORS{'OK'}, 0, "UUID  $1 - RESET");
			undef @sshcmd;
			@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q controlvm $1 reset", "root");
		}
	}
	

	return 1;
}

#//////////////////////////////////////////////////////////////////////////////

=head2 power_status

 Parameters  : 
 Returns     : If successful: string containing "on", "off", "suspended", or "stuck"
               If failed: undefined
 Description : 
 

=cut

sub power_status {
	my $self = shift;
	unless (ref($self) && $self->isa('VCL::Module')) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
		return;	
	}
	## Get necessary data
	my $shortname  = $self->data->get_computer_short_name;
	my $vmhost_hostname    = $self->data->get_vmhost_hostname();
	my $vmhost_fullhostname = $self->data->get_vmhost_hostname;
	my $hostnode = $1 if ($vmhost_fullhostname =~ /([-_a-zA-Z0-9]*)(\.?)/);
	my $management_node_keys = $self->data->get_management_node_keys();
	my @sshcmd;
	my $vm_uuid;
	my $vm_status = "UNKNOWN";
	undef @sshcmd;
	@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list vms", "root");
	foreach my $l (@{$sshcmd[1]}) {
		if ($l =~ m/\_$shortname\"/) {
			$l =~ m/{(.*)}/;
			notify($ERRORS{'OK'}, 0, "VM $shortname has UUID  $1");
			$vm_uuid = $1;
		}
	}

	undef @sshcmd;
	@sshcmd = run_ssh_command($vmhost_hostname, $management_node_keys, "VBoxManage -q showvminfo $vm_uuid --machinereadable | grep VMState=", "root");
	foreach my $l (@{$sshcmd[1]}) {
		$vm_status = "on"    if ($l =~ /running/);
		$vm_status = "off"   if ($l =~ /poweroff/);
		$vm_status = "stuck" if ($l =~ /paused/);
	} ## end foreach my $l (@{$sshcmd[1]})
	notify($ERRORS{'OK'}, 0, "$shortname vmstate reports $vm_status");
	
	return $vm_status;
}

#//////////////////////////////////////////////////////////////////////////////

1;
__END__

=head1 SEE ALSO

L<http://cwiki.apache.org/VCL/>

=cut
