vcl-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From arku...@apache.org
Subject svn commit: r755360 [2/3] - /incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows_mod.pm
Date Tue, 17 Mar 2009 19:31:47 GMT

Added: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows_mod.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows_mod.pm?rev=755360&view=auto
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows_mod.pm (added)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows_mod.pm Tue Mar 17 19:31:47 2009
@@ -0,0 +1,4598 @@
+#!/usr/bin/perl -w
+
+# 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.
+
+##############################################################################
+# $Id$
+##############################################################################
+
+=head1 NAME
+
+VCL::Module::OS::Windows::Desktop
+
+=head1 SYNOPSIS
+
+ Needs to be written
+
+=head1 DESCRIPTION
+
+ This module provides...
+
+=cut
+
+##############################################################################
+package VCL::Module::OS::Windows_mod;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../..";
+
+# Configure inheritance
+#use base qw(VCL::Module::OS::Windows);
+use base qw(VCL::Module::OS);
+
+# Specify the version of this module
+our $VERSION = '2.00';
+
+# Specify the version of Perl to use
+use 5.008000;
+
+use strict;
+use warnings;
+use diagnostics;
+
+use VCL::utils;
+use File::Basename;
+
+# Use Data::Dumper to print variables
+use Data::Dumper;
+$Data::Dumper::Indent = 0;
+$Data::Dumper::Terse  = 1;
+$Data::Dumper::Pair   = "=>";
+
+
+##############################################################################
+
+=head1 CLASS VARIABLES
+
+=cut
+
+=head2 $SOURCE_CONFIGURATION_DIRECTORY
+
+ Data type   : Scalar
+ Description : Location on management node of script/utilty/configuration
+               files needed to configure the OS. This is normally the
+					directory under the 'tools' directory specific to this OS.
+
+=cut
+
+our $SOURCE_CONFIGURATION_DIRECTORY = "$TOOLS/Windows";
+
+
+=head2 $NODE_CONFIGURATION_DIRECTORY
+
+ Data type   : Scalar
+ Description : Destination location on computer of
+               script/utilty/configuration files needed to configure the OS.
+
+=cut
+
+our $NODE_CONFIGURATION_DIRECTORY = 'C:/Cygwin/home/root/VCL';
+
+##############################################################################
+
+=head1 INTERFACE OBJECT METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 pre_capture
+
+ Parameters  : None, but must be called as an object method
+ Returns     : 1 if successful, 0 if failed
+ Description : Performs the steps necessary to prepare a Windows OS to be captured.
+               Called by provisioning module's capture() subroutine.
+
+=over 3
+
+=cut
+
+sub pre_capture {
+	my $self = shift;
+	my $args = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $computer_node_name = $self->data->get_computer_node_name();
+
+	notify($ERRORS{'OK'}, 0, "beginning Windows image capture preparation tasks on $computer_node_name, end state: $self->{end_state}");
+	
+=item 1
+
+Log off all currently logged in users
+
+=cut
+
+	if (!$self->logoff_users()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to log off all currently logged in users on $computer_node_name");
+		return 0;
+	}
+
+=item *
+
+Set root account password to known value
+
+=cut
+
+	if (!$self->set_password('root', $WINDOWS_ROOT_PASSWORD)) {
+		notify($ERRORS{'WARNING'}, 0, "unable to set root password");
+		return 0;
+	}
+
+=item *
+
+Delete the users assigned to this reservation
+
+=cut
+
+	if (!$self->delete_users()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to delete users");
+		return 0;
+	}
+
+=item *
+
+Disable all RDP access by removing firewall exceptions
+
+=cut
+
+	if (!$self->firewall_disable_rdp()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to disable RDP");
+		return 0;
+	}
+
+=item *
+
+Enable RDP access from private IP addresses by adding a firewall exception
+
+=cut
+
+	if (!$self->firewall_enable_rdp('10.0.0.0/8')) {
+		notify($ERRORS{'WARNING'}, 0, "unable to enable RDP from private IP addresses");
+		return 0;
+	}
+
+=item *
+
+Enable SSH access from private IP addresses by adding a firewall exception
+
+=cut
+
+	if (!$self->firewall_enable_ssh_private()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to enable SSH from private IP addresses");
+		return 0;
+	}
+
+=item *
+
+Enable ping access from private IP addresses by adding a firewall exception
+
+=cut
+
+	if (!$self->firewall_enable_ping_private()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to enable ping from private IP addresses");
+		return 0;
+	}
+
+=item *
+
+Delete existing capture configuration files from the computer (scripts, utilities, drivers...)
+
+=cut
+
+	if (!$self->delete_capture_configuration_files()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to delete existing capture configuration files to $computer_node_name");
+		return 0;
+	}
+
+=item *
+
+Copy the capture configuration files to the computer (scripts, utilities, drivers...)
+
+=cut
+
+	if (!$self->copy_capture_configuration_files($SOURCE_CONFIGURATION_DIRECTORY)) {
+		notify($ERRORS{'WARNING'}, 0, "unable to copy general Windows capture configuration files to $computer_node_name");
+		return 0;
+	}
+
+=item *
+
+Disable autoadminlogon before disabling the pagefile and rebooting
+
+=cut
+
+	if (!$self->disable_autoadminlogon()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to disable autoadminlogon");
+		return 0;
+	}
+
+=item *
+
+Disable IPv6
+
+=cut
+
+	if (!$self->disable_ipv6()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to disable IPv6");
+	}
+
+=item *
+
+Disable dynamic DNS
+
+=cut
+
+	if (!$self->disable_dynamic_dns()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to disable dynamic dns");
+	}
+
+=item *
+
+Call script to clean up the hard drive
+
+=cut
+
+	if (!$self->clean_hard_drive()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to clean unnecessary files the hard drive");
+	}
+	
+=item *
+
+Delete the 'VCL Startup Script' scheduled task
+
+=cut
+	
+	# This task must be deleted because it will conflict with the vcl_first_boot.cmd Run command that is added
+	# It also may cause problems after the reboot that occurs when the pagefile is disabled
+	# SSH commands may fail while the networking and Cygwin scripts are running
+	if (!$self->delete_scheduled_task('VCL Startup Script')) {
+		notify($ERRORS{'WARNING'}, 0, "unable to delete 'VCL Startup Script' scheduled task");
+		return 0;
+	}
+	
+=item *
+
+Delete 'VCL First Boot' Run registry key
+
+=cut
+	
+	if (!$self->delete_hklm_run_registry_key('VCL First Boot')) {
+		notify($ERRORS{'WARNING'}, 0, "unable to delete 'VCL First Boot' run registry key");
+		return 0;
+	}
+	
+=item *
+
+Disable the pagefile
+
+ ********* node reboots *********
+
+=item *
+
+Disable the pagefile, reboot, and delete pagefile.sys
+
+=cut
+
+	# This will set the registry key to disable the pagefile, reboot, then delete pagefile.sys
+	# Calls the reboot() subroutine, which makes sure ssh service is set to auto and firewall is open for ssh
+	if (!$self->disable_pagefile()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to disable pagefile");
+		return 0;
+	}
+
+=item *
+
+Use netsh to set the public NIC to use DHCP
+
+=cut
+
+	if (!$self->enable_dhcp('public')) {
+		notify($ERRORS{'WARNING'}, 0, "unable to enable DHCP on the public interface");
+		return 0;
+	}
+
+=item *
+
+Reenable the pagefile, this will take effect when the saved image boots
+
+=cut
+
+	if (!$self->enable_pagefile()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to reenable pagefile");
+		return 0;
+	}
+
+=item *
+
+Enable autoadminlogon
+
+=cut
+
+	if (!$self->enable_autoadminlogon()) {
+		notify($ERRORS{'WARNING'}, 0, "unable to enable autoadminlogon");
+		return 0;
+	}
+
+=item *
+
+Create HKLM run vcl_first_boot.cmd registry key
+
+=cut
+	
+	# Wrap entire command in cmd.exe /c start so command window has a title letting onlooker know what is running
+	if (!$self->add_hklm_run_registry_key('VCL First Boot', 'cmd.exe /c start "vcl_first_boot.cmd" cmd.exe /c "' . $NODE_CONFIGURATION_DIRECTORY . '/Scripts/vcl_first_boot.cmd  >> ' . $NODE_CONFIGURATION_DIRECTORY . '/Logs/vcl_first_boot.log 2>&1"')) {
+		notify($ERRORS{'WARNING'}, 0, "unable to add run vcl_first_boot.cmd key");
+		return 0;
+	}
+
+=item *
+
+Set sshd service startup mode to manual
+
+=cut
+
+	if (!$self->set_service_startup_mode('sshd', 'manual')) {
+		notify($ERRORS{'WARNING'}, 0, "unable to set sshd service startup mode to manual");
+		return 0;
+	}
+
+=back
+
+=cut
+
+	notify($ERRORS{'OK'}, 0, "returning 1");
+	return 1;
+
+} ## end sub pre_capture
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 post_load
+
+ Parameters  : reference to an object of this class
+ Returns     : 1 if successful, 0 if failed
+ Description : Performs the steps necessary to configure a Windows OS after an image has been loaded.
+               Called by provisioning module's load() subroutine.
+
+=over 3
+
+=cut
+
+sub post_load {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $imagemeta_postoption = $self->data->get_imagemeta_postoption();
+
+	notify($ERRORS{'OK'}, 0, "beginning Windows post-load tasks");
+
+=item 1
+
+Set the "My Computer" description to the image pretty name
+
+=cut
+
+	if (!$self->set_my_computer_name()) {
+		notify($ERRORS{'WARNING'}, 0, "Windows post-load failed, unable to rename my computer");
+		return 0;
+	}
+
+=item *
+
+Disable NetBIOS
+
+=cut
+
+	if (!$self->disable_netbios()) {
+		notify($ERRORS{'WARNING'}, 0, "Windows post-load failed, failed to disable NetBIOS");
+		return 0;
+	}
+
+=item *
+
+Disable dynamic DNS
+
+=cut
+
+	if (!$self->disable_dynamic_dns()) {
+		notify($ERRORS{'WARNING'}, 0, "Windows post-load failed, failed to disable dynamic DNS");
+		return 0;
+	}
+
+=item *
+
+Disable RDP firewall exceptions from all addresses
+
+=cut
+
+	if (!$self->firewall_disable_rdp()) {
+		notify($ERRORS{'WARNING'}, 0, "Windows post-load failed, failed to disable RDP");
+		return 0;
+	}
+
+=item *
+
+Randomize root password
+
+=cut
+
+	my $root_random_password = getpw();
+	if (!$self->set_password('root', $root_random_password)) {
+		notify($ERRORS{'WARNING'}, 0, "Windows post-load failed, failed to set random root password");
+		return 0;
+	}
+
+=item *
+
+Randomize Administrator password
+
+=cut
+
+	my $administrator_random_password = getpw();
+	if (!$self->set_password('Administrator', $administrator_random_password)) {
+		notify($ERRORS{'WARNING'}, 0, "Windows post-load failed, failed to set random Administrator password");
+		return 0;
+	}
+	
+=item *
+
+Create scheduled task to run script at computer startup
+
+=cut
+
+	if (!$self->create_startup_scheduled_task('VCL Startup Script', 'cmd.exe /c start "vcl_startup.cmd" /MIN ' . $NODE_CONFIGURATION_DIRECTORY . '/Scripts/vcl_startup.cmd  >> ' . $NODE_CONFIGURATION_DIRECTORY . '/Logs/vcl_startup.log 2>&1', 'root', $root_random_password)) {
+		notify($ERRORS{'WARNING'}, 0, "Windows post-load failed, failed to create scheduled task to run vcl_startup.cmd at computer startup");
+		return 0;
+	}
+
+=item *
+
+Check if imagemeta postoption is set to reboot
+
+=cut
+
+	if ($imagemeta_postoption =~ /reboot/i) {
+		notify($ERRORS{'OK'}, 0, "imagemeta postoption reboot is set for image, rebooting computer");
+		if (!$self->reboot()) {
+			notify($ERRORS{'WARNING'}, 0, "Windows post-load failed, failed to reboot the computer");
+			return 0;
+		}
+	}
+
+=back
+
+=cut
+
+	notify($ERRORS{'OK'}, 0, "returning 1");
+	return 1;
+} ## end sub post_load
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 reserve
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub reserve {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $request_forimaging   = $self->data->get_request_forimaging();
+	my $reservation_password = $self->data->get_reservation_password();
+
+	notify($ERRORS{'OK'}, 0, "beginning Windows reserve tasks");
+
+	# Check if this is an imaging request or not
+	if ($request_forimaging) {
+		# Imaging request, don't create account, set the Administrator password
+		if (!$self->set_password('Administrator', $reservation_password)) {
+			notify($ERRORS{'WARNING'}, 0, "unable to set password for Administrator account");
+			return 0;
+		}
+	}
+	else {
+		# Add the users to the computer
+		# The add_users() subroutine will add the primary reservation user and any imagemeta group users
+		if (!$self->add_users()) {
+			notify($ERRORS{'WARNING'}, 0, "unable to add users");
+			return 0;
+		}
+	}
+
+	notify($ERRORS{'OK'}, 0, "returning 1");
+	return 1;
+} ## end sub reserve
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 sanitize
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub sanitize {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $computer_node_name = $self->data->get_computer_node_name();
+
+	# Revoke access
+	if (!$self->revoke_access()) {
+		notify($ERRORS{'WARNING'}, 0, "failed to revoke access to $computer_node_name");
+		return 0;
+	}
+
+	# Delete all users associated with the reservation
+	# This includes the primary reservation user and users listed in imagemeta group if it's configured
+	if ($self->delete_users()) {
+		notify($ERRORS{'OK'}, 0, "users have been deleted from $computer_node_name");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete users from $computer_node_name");
+		return 0;
+	}
+
+	notify($ERRORS{'OK'}, 0, "$computer_node_name has been sanitized");
+	return 1;
+} ## end sub sanitize
+
+##############################################################################
+
+=head1 AUXILIARY OBJECT METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_directory
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub create_directory {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my @paths;
+
+	# Get 1 or more paths from the subroutine arguments
+	while (my $path = shift) {
+		push @paths, $path;
+	}
+
+	# Make sure at least 1 path was specified
+	if (!@paths) {
+		notify($ERRORS{'WARNING'}, 0, "directory path was not specified as an argument");
+		return;
+	}
+
+	notify($ERRORS{'DEBUG'}, 0, "attempting to create " . scalar @paths . " directories:\n" . join("\n", @paths));
+
+	# Keep a count of paths which couldn't be deleted
+	my $directories_not_created = 0;
+
+	# Loop through the paths
+	for my $path (@paths) {
+		notify($ERRORS{'DEBUG'}, 0, "attempting to create directory: $path");
+
+		# Assemble the Windows shell mkdir command and execute it
+		my $mkdir_command = '$SYSTEMROOT/System32/cmd.exe /c "mkdir \\"' . $path . '\\""';
+		my ($mkdir_exit_status, $mkdir_output) = run_ssh_command($computer_node_name, $management_node_keys, $mkdir_command, '', '', 1);
+		if (defined($mkdir_exit_status) && $mkdir_exit_status == 0) {
+			notify($ERRORS{'OK'}, 0, "directory created on $computer_node_name: $path, output:\n@{$mkdir_output}");
+		}
+		elsif (defined($mkdir_exit_status) && $mkdir_exit_status == 1 && grep(/already exists/i, @{$mkdir_output})) {
+			notify($ERRORS{'OK'}, 0, "directory already exists on $computer_node_name: $path, exit status: $mkdir_exit_status, output:\n@{$mkdir_output}");
+		}
+		elsif (defined($mkdir_exit_status)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to create directory on $computer_node_name: $path, exit status: $mkdir_exit_status, output:\n@{$mkdir_output}");
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to delete file on $computer_node_name: $path");
+			$directories_not_created++;
+			next;
+		}
+
+		# Make sure directory was created
+		if (!$self->filesystem_entry_exists($path)) {
+			notify($ERRORS{'WARNING'}, 0, "filesystem entry does not exist on $computer_node_name: $path");
+			$directories_not_created++;
+			next;
+		}
+		else {
+			notify($ERRORS{'DEBUG'}, 0, "verified that filesystem entry exists on $computer_node_name: $path");
+		}
+	} ## end for my $path (@paths)
+
+	# Check if any paths couldn't be created
+	if ($directories_not_created) {
+		notify($ERRORS{'WARNING'}, 0, "some paths could not be created");
+		return 0;
+	}
+	else {
+		return 1;
+	}
+} ## end sub create_directory
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_filesystem_entry
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub delete_filesystem_entry {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my @paths;
+
+	# Get 1 or more paths from the subroutine arguments
+	while (my $path = shift) {
+		push @paths, $path;
+	}
+
+	# Make sure at least 1 path was specified
+	if (!@paths) {
+		notify($ERRORS{'WARNING'}, 0, "filesystem path was not specified as an argument");
+		return;
+	}
+
+	notify($ERRORS{'DEBUG'}, 0, "attempting to delete " . scalar @paths . " paths:\n" . join("\n", @paths));
+
+	# Keep a count of paths which couldn't be deleted
+	my $paths_not_deleted = 0;
+
+	# Loop through the paths
+	for my $path (@paths) {
+		notify($ERRORS{'DEBUG'}, 0, "attempting to delete: $path");
+
+		# Assemble the Windows shell del command and execute it
+		my $del_command = '$SYSTEMROOT/System32/cmd.exe /c "del /s /q /f /a \\"' . $path . '\\""';
+		my ($del_exit_status, $del_output) = run_ssh_command($computer_node_name, $management_node_keys, $del_command, '', '', 1);
+		if (defined($del_exit_status) && $del_exit_status == 0) {
+			notify($ERRORS{'DEBUG'}, 0, "file either deleted or does not exist on $computer_node_name: $path, output:\n@{$del_output}");
+		}
+		elsif ($del_exit_status) {
+			notify($ERRORS{'WARNING'}, 0, "failed to delete file on $computer_node_name: $path, exit status: $del_exit_status, output:\n@{$del_output}");
+			$paths_not_deleted++;
+			next;
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to delete file on $computer_node_name: $path");
+			$paths_not_deleted++;
+			next;
+		}
+
+		# Assemble the Windows shell rmdir command and execute it
+		my $rmdir_command = '$SYSTEMROOT/System32/cmd.exe /c "rmdir /s /q \\"' . $path . '\\""';
+		my ($rmdir_exit_status, $rmdir_output) = run_ssh_command($computer_node_name, $management_node_keys, $rmdir_command, '', '', 1);
+		if (defined($rmdir_exit_status) && $rmdir_exit_status == 0) {
+			notify($ERRORS{'DEBUG'}, 0, "directory deleted on $computer_node_name: $path, output:\n@{$rmdir_output}");
+		}
+		elsif (defined($rmdir_output) && grep(/cannot find the/, @{$rmdir_output})) {
+			# Exit status 2 should mean the directory was not found
+			notify($ERRORS{'DEBUG'}, 0, "directory to be deleted was not found on $computer_node_name: $path, exit status: $rmdir_exit_status, output:\n@{$rmdir_output}");
+		}
+		elsif ($rmdir_exit_status) {
+			notify($ERRORS{'WARNING'}, 0, "failed to delete directory on $computer_node_name: $path, exit status: $rmdir_exit_status, output:\n@{$rmdir_output}");
+			$paths_not_deleted++;
+			next;
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to delete directory on $computer_node_name: $path");
+			$paths_not_deleted++;
+			next;
+		}
+
+		# Make sure directory was deleted
+		if ($self->filesystem_entry_exists($path)) {
+			notify($ERRORS{'WARNING'}, 0, "filesystem entry still exists on $computer_node_name: $path");
+			$paths_not_deleted++;
+			next;
+		}
+		else {
+			notify($ERRORS{'OK'}, 0, "file or directory has been deleted: $path");
+		}
+	} ## end for my $path (@paths)
+
+	# Check if any paths couldn't be deleted
+	if ($paths_not_deleted) {
+		notify($ERRORS{'WARNING'}, 0, "some paths could not be deleted");
+		return 0;
+	}
+	else {
+		return 1;
+	}
+} ## end sub delete_filesystem_entry
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_directory_contents
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub delete_files_by_pattern {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $base_directory = shift;
+	my $pattern        = shift;
+
+	# Make sure base directory and pattern were specified
+	if (!($base_directory && $pattern)) {
+		notify($ERRORS{'WARNING'}, 0, "base directory and pattern must be specified as arguments");
+		return;
+	}
+
+	notify($ERRORS{'DEBUG'}, 0, "attempting to delete files under $base_directory matching pattern $pattern");
+
+
+	# Assemble command
+	my $command = "/bin/find.exe \"$base_directory\" -mindepth 1 -iregex \"$pattern\" -print0 | xargs -0 rm -rvf";
+	my ($exit_status, $output) = run_ssh_command($computer_node_name, $management_node_keys, $command, '', '', 0);
+	if (defined($exit_status) && $exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "files have been deleted under '$base_directory' matching pattern '$pattern'");
+	}
+	elsif ($exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete files under $base_directory matching pattern $pattern, exit status: $exit_status, output:\n@{$output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to delete files under $base_directory matching pattern $pattern");
+		return;
+	}
+
+	return 1;
+} ## end sub delete_files_by_pattern
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 filesystem_entry_exists
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub filesystem_entry_exists {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Get the path from the subroutine arguments and make sure it was passed
+	my $path = shift;
+	if (!$path) {
+		notify($ERRORS{'WARNING'}, 0, "unable to detmine if file exists, path was not specified as an argument");
+		return;
+	}
+
+	# Assemble the ls command and execute it
+	my $ls_command = "ls -la '$path'";
+	my ($ls_exit_status, $ls_output) = run_ssh_command($computer_node_name, $management_node_keys, $ls_command, '', '', 1);
+	if (defined($ls_exit_status) && $ls_exit_status == 0) {
+		notify($ERRORS{'DEBUG'}, 0, "filesystem entry exists on $computer_node_name: $path");
+		return 1;
+	}
+	elsif (defined($ls_exit_status) && $ls_exit_status == 2) {
+		notify($ERRORS{'DEBUG'}, 0, "filesystem entry does NOT exist on $computer_node_name: $path");
+		return 0;
+	}
+	elsif ($ls_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to determine if filesystem entry exists on $computer_node_name: $path, exit status: $ls_exit_status, output:\n@{$ls_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to determine if filesystem entry exists on $computer_node_name: $path");
+		return;
+	}
+} ## end sub filesystem_entry_exists
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 logoff_users
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub logoff_users {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my ($exit_status, $output) = run_ssh_command($computer_node_name, $management_node_keys, "qwinsta.exe");
+	if ($exit_status > 0) {
+		notify($ERRORS{'WARNING'}, 0, "failed to run qwinsta.exe on $computer_node_name, exit status: $exit_status, output:\n@{$output}");
+		return;
+	}
+	elsif (!defined($exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to run qwinsta.exe SSH command on $computer_node_name");
+		return;
+	}
+
+	my @active_user_lines = grep(/Active/, @{$output});
+
+	return 1 if !@active_user_lines;
+
+	notify($ERRORS{'OK'}, 0, "users are currently logged in on $computer_node_name:\n@active_user_lines");
+	#>rdp-tcp#1 root 0 Active rdpwd
+	foreach my $active_user_line (@active_user_lines) {
+		$active_user_line =~ /[\s>]*(\S+)\s+(.*\w)\s*(\d+)\s+Active.*/;
+		my $session_name = $1;
+		my $username     = $2;
+		my $session_id   = $3;
+
+		notify($ERRORS{'DEBUG'}, 0, "user logged in: $username, session name: $session_name, session id: $session_id");
+		#$ logoff /?
+		#Terminates a session.
+		#LOGOFF [sessionname | sessionid] [/SERVER:servername] [/V]
+		#  sessionname         The name of the session.
+		#  sessionid           The ID of the session.
+		#  /SERVER:servername  Specifies the Terminal server containing the user
+		#							 session to log off (default is current).
+		#  /V                  Displays information about the actions performed.
+
+		# Call logoff.exe, pass it the session name
+		# Session ID fails if the ID is 0
+		my ($logoff_exit_status, $logoff_output) = run_ssh_command($computer_node_name, $management_node_keys, "logoff.exe $session_name /V");
+		if ($logoff_exit_status == 0) {
+			notify($ERRORS{'OK'}, 0, "logged off user: $username");
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to log off user: $username, exit status: $logoff_exit_status, output:\n@{$logoff_output}");
+		}
+	} ## end foreach my $active_user_line (@active_user_lines)
+	return 1;
+} ## end sub logoff_users
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 add_users
+
+ Parameters  : 
+ Returns     : 
+ Description : 
+
+=cut
+
+sub add_users {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the user array from the arguments
+	# If no argument was supplied, use the users specified in the DataStructure
+	my $user_array_ref = shift;
+	my @users;
+	if ($user_array_ref) {
+		$user_array_ref = $self->data->get_imagemeta_usergroupmembers();
+		@users          = @{$user_array_ref};
+	}
+	else {
+		# User list was not specified as an argument
+		# Use the imagemeta group members and the primary reservation user
+		my $user_login_id      = $self->data->get_user_login_id();
+		my $user_group_members = $self->data->get_imagemeta_usergroupmembers();
+
+		push @users, $user_login_id;
+
+		foreach my $user_group_member_uid (keys(%{$user_group_members})) {
+			my $user_group_member_login_id = $user_group_members->{$user_group_member_uid};
+			push @users, $user_group_member_login_id;
+		}
+
+		# Remove duplicate users
+		@users = keys %{{map {$_, 1} @users}};
+	} ## end else [ if ($user_array_ref)
+
+	notify($ERRORS{'DEBUG'}, 0, "attempting to add " . scalar @users . " users to $computer_node_name: " . join(", ", @users));
+
+	# Attempt to get the password from the arguments
+	# If no argument was supplied, use the password specified in the DataStructure
+	my $password = shift;
+	if (!$password) {
+		$password = $self->data->get_reservation_password();
+	}
+
+	# Loop through the users in the imagemeta group and attempt to add them
+	for my $username (@users) {
+		if (!$self->create_user($username, $password)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to add users to $computer_node_name");
+			return 0;
+		}
+	}
+
+	notify($ERRORS{'OK'}, 0, "added " . scalar @users . " users to $computer_node_name");
+	return 1;
+} ## end sub add_users
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_users
+
+ Parameters  : 
+ Returns     : 
+ Description : 
+
+=cut
+
+sub delete_users {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the user array from the arguments
+	# If no argument was supplied, use the users specified in the DataStructure
+	my $user_array_ref = shift;
+	my @users;
+	if ($user_array_ref) {
+		$user_array_ref = $self->data->get_imagemeta_usergroupmembers();
+		@users          = @{$user_array_ref};
+	}
+	else {
+		# User list was not specified as an argument
+		# Use the imagemeta group members and the primary reservation user
+		my $user_login_id      = $self->data->get_user_login_id();
+		my $user_group_members = $self->data->get_imagemeta_usergroupmembers();
+
+		push @users, $user_login_id;
+
+		foreach my $user_group_member_uid (keys(%{$user_group_members})) {
+			my $user_group_member_login_id = $user_group_members->{$user_group_member_uid};
+			push @users, $user_group_member_login_id;
+		}
+
+		# Remove duplicate users
+		@users = keys %{{map {$_, 1} @users}};
+	} ## end else [ if ($user_array_ref)
+
+	# Loop through the users and attempt to delete them
+	for my $username (@users) {
+		if (!$self->delete_user($username)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to delete user $username from $computer_node_name");
+			return 0;
+		}
+	}
+
+	notify($ERRORS{'OK'}, 0, "deleted " . scalar @users . " users from $computer_node_name");
+	return 1;
+} ## end sub delete_users
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 user_exists
+
+ Parameters  : 
+ Returns     : 
+ Description : 
+
+=cut
+
+sub user_exists {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the username from the arguments
+	# If no argument was supplied, use the user specified in the DataStructure
+	my $username = shift;
+	if (!$username) {
+		$username = $self->data->get_user_login_id();
+	}
+
+	notify($ERRORS{'DEBUG'}, 0, "checking if user $username exists on $computer_node_name");
+
+	# Attempt to query the user account
+	my $query_user_command = "net user \"$username\"";
+	my ($query_user_exit_status, $query_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $query_user_command);
+	if (defined($query_user_exit_status) && $query_user_exit_status == 0) {
+		notify($ERRORS{'DEBUG'}, 0, "user $username exists on $computer_node_name");
+		return 1;
+	}
+	elsif (defined($query_user_exit_status) && $query_user_exit_status == 2) {
+		notify($ERRORS{'DEBUG'}, 0, "user $username does not exist on $computer_node_name");
+		return 0;
+	}
+	elsif (defined($query_user_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to determine if user $username exists on $computer_node_name, exit status: $query_user_exit_status, output:\n@{$query_user_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to determine if user $username exists on $computer_node_name");
+		return;
+	}
+} ## end sub user_exists
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_user
+
+ Parameters  : 
+ Returns     : 
+ Description : 
+
+=cut
+
+sub create_user {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the username from the arguments
+	# If no argument was supplied, use the user specified in the DataStructure
+	my $username = shift;
+	my $password = shift;
+	if (!$username) {
+		$username = $self->data->get_user_login_id();
+	}
+	if (!$password) {
+		$password = $self->data->get_user_password();
+	}
+
+	# Check if user already exists
+	if ($self->user_exists($username)) {
+		notify($ERRORS{'OK'}, 0, "user $username already exists on $computer_node_name, attempting to delete user");
+
+		# Attempt to delete the user
+		if (!$self->delete_user($username)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to add user $username to $computer_node_name, user already exists and could not be deleted");
+			return 0;
+		}
+	}
+
+	notify($ERRORS{'DEBUG'}, 0, "attempting to add user $username to $computer_node_name ($password)");
+
+	# Attempt to add the user account
+	my $add_user_command = "net user \"$username\" \"$password\" /ADD  /EXPIRES:NEVER /COMMENT:\"Account created by VCL\"";
+	$add_user_command .= " && net localgroup \"Administrators\" \"$username\" /ADD";
+	$add_user_command .= " && net localgroup \"Remote Desktop Users\" \"$username\" /ADD";
+
+	my ($add_user_exit_status, $add_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $add_user_command);
+	if (defined($add_user_exit_status) && $add_user_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "added user $username ($password) to $computer_node_name");
+	}
+	elsif (defined($add_user_exit_status) && $add_user_exit_status == 2) {
+		notify($ERRORS{'OK'}, 0, "user $username was not added, user already exists");
+		return 1;
+	}
+	elsif (defined($add_user_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to add user $username to $computer_node_name, exit status: $add_user_exit_status, output:\n@{$add_user_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command add user $username to $computer_node_name");
+		return;
+	}
+
+	return 1;
+} ## end sub create_user
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 add_user_to_group
+
+ Parameters  : 
+ Returns     : 
+ Description : 
+
+=cut
+
+sub add_user_to_group {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the username from the arguments
+	# If no argument was supplied, use the user specified in the DataStructure
+	my $username = shift;
+	my $group    = shift;
+	if (!$username || !$group) {
+		notify($ERRORS{'WARNING'}, 0, "unable to add user to group, arguments were not passed correctly");
+		return;
+	}
+
+	# Attempt to add the user to the group using net localgroup
+	my $localgroup_user_command = "net localgroup \"$group\" $username /ADD";
+	my ($localgroup_user_exit_status, $localgroup_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $localgroup_user_command);
+	if (defined($localgroup_user_exit_status) && $localgroup_user_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "added user $username to \"$group\" group on $computer_node_name");
+	}
+	elsif (defined($localgroup_user_exit_status) && $localgroup_user_exit_status == 2) {
+		# Exit status is 2, this could mean the user is already a member or that the group doesn't exist
+		# Check the output to determine what happened
+		if (grep(/error 1378/, @{$localgroup_user_output})) {
+			# System error 1378 has occurred.
+			# The specified account name is already a member of the group.
+			notify($ERRORS{'OK'}, 0, "user $username was not added to $group group because user already a member");
+			return 1;
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to add user $username to $group group on $computer_node_name, exit status: $localgroup_user_exit_status, output:\n@{$localgroup_user_output}");
+			return 0;
+		}
+	} ## end elsif (defined($localgroup_user_exit_status) ... [ if (defined($localgroup_user_exit_status) ...
+	elsif (defined($localgroup_user_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to add user $username to $group group on $computer_node_name, exit status: $localgroup_user_exit_status, output:\n@{$localgroup_user_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to add user $username to $group group on $computer_node_name");
+		return;
+	}
+
+	return 1;
+} ## end sub add_user_to_group
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_user
+
+ Parameters  : $node, $user, $type, $osname
+ Returns     : 1 success 0 failure
+ Description : removes user account and profile directory from specificed node
+
+=cut
+
+sub delete_user {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the username from the arguments
+	# If no argument was supplied, use the user specified in the DataStructure
+	my $username = shift;
+	if (!(defined($username))) {
+		$username = $self->data->get_user_login_id();
+	}
+
+	notify($ERRORS{'OK'}, 0, "attempting to delete user $username from $computer_node_name");
+
+	# Attempt to delete the user account
+	my $delete_user_command = "net user $username /DELETE";
+	my ($delete_user_exit_status, $delete_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $delete_user_command);
+	if (defined($delete_user_exit_status) && $delete_user_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "deleted user $username from $computer_node_name");
+	}
+	elsif (defined($delete_user_exit_status) && $delete_user_exit_status == 2) {
+		notify($ERRORS{'OK'}, 0, "user $username was not deleted because user does not exist");
+	}
+	elsif (defined($delete_user_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete user $username from $computer_node_name, exit status: $delete_user_exit_status, output:\n@{$delete_user_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command delete user $username from $computer_node_name");
+		return;
+	}
+
+	# Delete the user's home directory
+	if ($self->delete_filesystem_entry("C:/Documents and Settings/$username")) {
+		notify($ERRORS{'OK'}, 0, "deleted profile for user $username from $computer_node_name");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete profile for user $username from $computer_node_name");
+		return 0;
+	}
+
+	return 1;
+} ## end sub delete_user
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 set_password
+
+ Parameters  : $username, $password
+ Returns     : 
+ Description : 
+
+=cut
+
+sub set_password {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the username from the arguments
+	my $username = shift;
+	my $password = shift;
+
+	# If no argument was supplied, use the user specified in the DataStructure
+	if (!defined($username)) {
+		$username = $self->data->get_user_logon_id();
+	}
+	if (!defined($password)) {
+		$password = $self->data->get_reservation_password();
+	}
+
+	# Make sure both the username and password were determined
+	if (!defined($username) || !defined($password)) {
+		notify($ERRORS{'WARNING'}, 0, "username and password could not be determined");
+		return 0;
+	}
+
+	# Attempt to set the password
+	notify($ERRORS{'DEBUG'}, 0, "setting password of $username to $password on $computer_node_name");
+	my ($set_password_exit_status, $set_password_output) = run_ssh_command($computer_node_name, $management_node_keys, "net user $username '$password'");
+	if ($set_password_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "password changed to '$password' for user '$username' on $computer_node_name");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to change password to '$password' for user '$username' on $computer_node_name, exit status: $set_password_exit_status, output:\n@{$set_password_output}");
+		return 0;
+	}
+
+	# Check if root user, must set sshd service password too
+	if ($username eq 'root') {
+		notify($ERRORS{'DEBUG'}, 0, "root account password changed, must also change sshd service credentials");
+		if (!$self->set_service_credentials('sshd', $username, $password)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to set sshd service credentials to $username ($password)");
+			return 0;
+		}
+	}
+
+	# Attempt to change scheduled task passwords
+	notify($ERRORS{'DEBUG'}, 0, "changing passwords for scheduled tasks");
+	my ($schtasks_query_exit_status, $schtasks_query_output) = run_ssh_command($computer_node_name, $management_node_keys, "schtasks.exe /Query /V /FO LIST");
+	if (defined($schtasks_query_exit_status) && $schtasks_query_exit_status == 0) {
+		notify($ERRORS{'DEBUG'}, 0, "queried scheduled tasks on $computer_node_name");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to query scheduled tasks on $computer_node_name, exit status: $schtasks_query_exit_status, output:\n@{$schtasks_query_output}");
+		return 0;
+	}
+
+	# Find scheduled tasks configured to run as this user
+	my $task_name;
+	my @task_names_to_update;
+	for my $schtasks_output_line (@{$schtasks_query_output}) {
+		if ($schtasks_output_line =~ /TaskName:\s+([ \S]+)/i) {
+			$task_name = $1;
+			notify($ERRORS{'DEBUG'}, 0, "found task: " . string_to_ascii($task_name));
+		}
+		if ($schtasks_output_line =~ /Run As User.*\\$username/i) {
+			notify($ERRORS{'DEBUG'}, 0, "password needs to be updated for scheduled task: $task_name\n$schtasks_output_line");
+			push @task_names_to_update, $task_name;
+		}
+	} ## end for my $schtasks_output_line (@{$schtasks_query_output...
+
+	# Loop through the scheduled tasks configured to run as the user, update the password
+	for my $task_name_to_update (@task_names_to_update) {
+		my ($schtasks_change_exit_status, $schtasks_change_output) = run_ssh_command($computer_node_name, $management_node_keys, "schtasks.exe /Change /RP \"$password\" /TN \"$task_name_to_update\"");
+		if (defined($schtasks_change_exit_status) && $schtasks_change_exit_status == 0) {
+			notify($ERRORS{'OK'}, 0, "changed password for scheduled task: $task_name_to_update");
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to change password for scheduled task: $task_name_to_update, exit status: $schtasks_change_exit_status, output:\n@{$schtasks_change_output}");
+			return 0;
+		}
+	} ## end for my $task_name_to_update (@task_names_to_update)
+
+	return 1;
+} ## end sub set_password
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 enable_user
+
+ Parameters  : $username (optional
+ Returns     : 
+ Description : 
+
+=cut
+
+sub enable_user {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the username from the arguments
+	my $username = shift;
+
+	# If no argument was supplied, use the user specified in the DataStructure
+	if (!defined($username)) {
+		$username = $self->data->get_user_logon_id();
+	}
+
+	# Make sure the username was determined
+	if (!defined($username)) {
+		notify($ERRORS{'WARNING'}, 0, "username could not be determined");
+		return 0;
+	}
+
+	# Attempt to enable the user account (set ACTIVE=YES)
+	notify($ERRORS{'DEBUG'}, 0, "enabling user $username on $computer_node_name");
+	my ($enable_exit_status, $enable_output) = run_ssh_command($computer_node_name, $management_node_keys, "net user $username /ACTIVE:YES");
+	if ($enable_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "user $username enabled on $computer_node_name");
+	}
+	elsif ($enable_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to enable user $username on $computer_node_name, exit status: $enable_exit_status, output:\n@{$enable_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to enable user $username on $computer_node_name");
+		return 0;
+	}
+
+	return 1;
+} ## end sub enable_user
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 disable_pagefile
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub disable_pagefile {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $registry_string .= <<"EOF";
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management]
+"PagingFiles"=""
+EOF
+
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "set the registry key to disable the pagefile");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to set the registry key to disable the pagefile");
+		return 0;
+	}
+
+	# Attempt to reboot the computer in order to delete the pagefile
+	if ($self->reboot()) {
+		notify($ERRORS{'DEBUG'}, 0, "computer was rebooted after disabling pagefile in the registry");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to reboot computer after disabling pagefile");
+		return;
+	}
+
+	# Attempt to delete the pagefile
+	if (!$self->delete_filesystem_entry("C:/pagefile.sys")) {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete pagefile.sys");
+		return;
+	}
+
+	return 1;
+} ## end sub disable_pagefile
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 enable_pagefile
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub enable_pagefile {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $registry_string .= <<'EOF';
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management]
+"PagingFiles"="?:\\\\pagefile.sys"
+EOF
+
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "set registry key to enable the pagefile");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to set registry key to enable the pagefile");
+		return 0;
+	}
+
+	return 1;
+} ## end sub enable_pagefile
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 disable_ipv6
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub disable_ipv6 {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $registry_string .= <<'EOF';
+Windows Registry Editor Version 5.00
+
+; This registry file contains the entries to disable all IPv6 components 
+; http://support.microsoft.com/kb/929852
+
+[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters]
+"DisabledComponents"=dword:ffffffff
+EOF
+
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "set registry keys to disable IPv6");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to set the registry keys to disable IPv6");
+		return 0;
+	}
+
+	return 1;
+} ## end sub disable_ipv6
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 import_registry_file
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub import_registry_file {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $registry_file_path = shift;
+	if (!defined($registry_file_path) || !$registry_file_path) {
+		notify($ERRORS{'WARNING'}, 0, "registry file path was not passed correctly as an argument");
+		return;
+	}
+
+	my $registry_file_contents = `cat $registry_file_path`;
+	notify($ERRORS{'DEBUG'}, 0, "registry file contents:\n$registry_file_contents");
+
+	$registry_file_contents =~ s/([\"])/\\$1/gs;
+	$registry_file_contents =~ s/\\+"/\\"/gs;
+
+	# Specify where on the node the temporary registry file will reside
+	my $temp_registry_file_path = 'C:/Cygwin/tmp/vcl_import.reg';
+
+	# Echo the registry string to a file on the node
+	my $echo_registry_command = "/usr/bin/echo.exe -E \"$registry_file_contents\" > " . $temp_registry_file_path;
+	my ($echo_registry_exit_status, $echo_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $echo_registry_command, '', '', 1);
+	if (defined($echo_registry_exit_status) && $echo_registry_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "registry file contents echoed to $temp_registry_file_path");
+	}
+	elsif ($echo_registry_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to echo registry file contents to $temp_registry_file_path, exit status: $echo_registry_exit_status, output:\n@{$echo_registry_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to echo registry file contents to $temp_registry_file_path");
+		return;
+	}
+
+	# Run reg.exe IMPORT
+	my $import_registry_command .= '"$SYSTEMROOT/System32/reg.exe" IMPORT ' . $temp_registry_file_path;
+	my ($import_registry_exit_status, $import_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $import_registry_command, '', '', 1);
+	if (defined($import_registry_exit_status) && $import_registry_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "registry file contents imported from $temp_registry_file_path");
+	}
+	elsif ($import_registry_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to import registry file contents from $temp_registry_file_path, exit status: $import_registry_exit_status, output:\n@{$import_registry_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to import registry file contents from $temp_registry_file_path");
+		return;
+	}
+
+	return 1;
+} ## end sub import_registry_file
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 import_registry_string
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub import_registry_string {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $registry_string = shift;
+	if (!defined($registry_string) || !$registry_string) {
+		notify($ERRORS{'WARNING'}, 0, "registry file path was not passed correctly as an argument");
+		return;
+	}
+
+	notify($ERRORS{'DEBUG'}, 0, "registry string:\n" . $registry_string);
+
+	# Escape special characters with a backslash:
+	# \
+	# "
+	#notify($ERRORS{'DEBUG'}, 0, "registry string:\n$registry_string");
+	#$registry_string =~ s/\\+/\\\\\\\\/gs;
+	$registry_string =~ s/\\/\\\\/gs;
+	$registry_string =~ s/"/\\"/gs;
+
+	# Replace \\" with \"
+	#$registry_string =~ s/\\+(")/\\\\$1/gs;
+
+	# Replace regular newlines with Windows newlines
+	$registry_string =~ s/\r?\n/\r\n/gs;
+
+	# Specify where on the node the temporary registry file will reside
+	my $temp_registry_file_path = 'C:/Cygwin/tmp/vcl_import.reg';
+
+	# Echo the registry string to a file on the node
+	my $echo_registry_command = "/usr/bin/echo.exe -E \"$registry_string\" > " . $temp_registry_file_path;
+	my ($echo_registry_exit_status, $echo_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $echo_registry_command, '', '', 1);
+	if (defined($echo_registry_exit_status) && $echo_registry_exit_status == 0) {
+		notify($ERRORS{'DEBUG'}, 0, "registry string contents echoed to $temp_registry_file_path");
+	}
+	elsif ($echo_registry_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to echo registry string contents to $temp_registry_file_path, exit status: $echo_registry_exit_status, output:\n@{$echo_registry_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to echo registry string contents to $temp_registry_file_path");
+		return;
+	}
+
+	# Run reg.exe IMPORT
+	my $import_registry_command .= '"$SYSTEMROOT/System32/reg.exe" IMPORT ' . $temp_registry_file_path;
+	my ($import_registry_exit_status, $import_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $import_registry_command, '', '', 1);
+	if (defined($import_registry_exit_status) && $import_registry_exit_status == 0) {
+		notify($ERRORS{'DEBUG'}, 0, "registry string contents imported from $temp_registry_file_path");
+	}
+	elsif ($import_registry_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to import registry string contents from $temp_registry_file_path, exit status: $import_registry_exit_status, output:\n@{$import_registry_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to import registry string contents from $temp_registry_file_path");
+		return;
+	}
+
+	return 1;
+} ## end sub import_registry_string
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 add_hklm_run_registry_key
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub add_hklm_run_registry_key {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $command_name = shift;
+	my $command      = shift;
+
+	notify($ERRORS{'DEBUG'}, 0, "command name: " . $command_name);
+	notify($ERRORS{'DEBUG'}, 0, "command: " . $command);
+
+	# Replace forward slashes with backslashes, unless a space precedes the forward slash
+	$command =~ s/([^ ])\//$1\\/g;
+	notify($ERRORS{'DEBUG'}, 0, "forward to backslash: " . $command);
+
+	# Escape backslashes, can never have enough...
+	$command =~ s/\\/\\\\/g;
+	notify($ERRORS{'DEBUG'}, 0, "escape backslashes: " . $command);
+
+	# Escape quotes
+	$command =~ s/"/\\"/g;
+	notify($ERRORS{'DEBUG'}, 0, "escaped quotes: " . $command);
+
+	# Make sure arguments were supplied
+	if (!defined($command_name) && !defined($command)) {
+		notify($ERRORS{'WARNING'}, 0, "HKLM run registry key not added, arguments were not passed correctly");
+		return 0;
+	}
+
+	my $registry_string .= <<"EOF";
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run]
+"$command_name"="$command"
+EOF
+
+	notify($ERRORS{'DEBUG'}, 0, "registry string:\n" . $registry_string);
+
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "added HKLM run registry value, name: $command_name, command: $command");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to add HKLM run registry value, name: $command_name, command: $command");
+		return 0;
+	}
+	
+	# Attempt to query the registry key to make sure it was added
+	my $reg_query_command = '$SYSTEMROOT/System32/reg.exe query "HKLM\\SOFTWARE\\Microsoft\Windows\\\CurrentVersion\\Run" /v "' . $command_name . '"';
+	my ($reg_query_exit_status, $reg_query_output) = run_ssh_command($computer_node_name, $management_node_keys, $reg_query_command, '', '', 1);
+	if (defined($reg_query_exit_status) && $reg_query_exit_status == 0) {
+		notify($ERRORS{'DEBUG'}, 0, "queried '$command_name' registry key:\n" . join("\n", @{$reg_query_output}));
+	}
+	elsif (defined($reg_query_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to query '$command_name' registry key, exit status: $reg_query_exit_status, output:\n@{$reg_query_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to query '$command_name' registry key");
+		return;
+	}
+	
+	return 1;
+} ## end sub add_hklm_run_registry_key
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_hklm_run_registry_value
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub delete_hklm_run_registry_key {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $key_name = shift;
+	
+	# Make sure argument was supplied
+	if (!defined($key_name) && !defined($key_name)) {
+		notify($ERRORS{'WARNING'}, 0, "HKLM run registry key not deleted, argument was not passed correctly");
+		return 0;
+	}
+	
+	# Attempt to query the registry key to make sure it was added
+	my $reg_delete_command = '$SYSTEMROOT/System32/reg.exe delete "HKLM\\SOFTWARE\\Microsoft\Windows\\\CurrentVersion\\Run" /v "' . $key_name . '" /F';
+	my ($reg_delete_exit_status, $reg_delete_output) = run_ssh_command($computer_node_name, $management_node_keys, $reg_delete_command, '', '', 1);
+	if (defined($reg_delete_exit_status) && $reg_delete_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "deleted '$key_name' run registry key:\n" . join("\n", @{$reg_delete_output}));
+	}
+	elsif (defined($reg_delete_output) && grep(/unable to find/i, @{$reg_delete_output})) {
+		notify($ERRORS{'OK'}, 0, "'$key_name' run registry key was not deleted, it does not exist");
+	}
+	elsif (defined($reg_delete_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete '$key_name' run registry key, exit status: $reg_delete_exit_status, output:\n@{$reg_delete_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to delete '$key_name' run registry key");
+		return;
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_scheduled_task
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub delete_scheduled_task {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $task_name     = shift;
+	
+	# Run schtasks.exe to delete any existing task
+	my $delete_task_command = 'schtasks.exe /Delete /F /TN "' . $task_name . '"';
+	my ($delete_task_exit_status, $delete_task_output) = run_ssh_command($computer_node_name, $management_node_keys, $delete_task_command);
+	if (defined($delete_task_exit_status) && $delete_task_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "deleted existing scheduled task '$task_name' on $computer_node_name");
+	}
+	elsif (defined($delete_task_output) && grep(/task.*does not exist/i, @{$delete_task_output})) {
+		notify($ERRORS{'DEBUG'}, 0, "scheduled task '$task_name' does not already exist on $computer_node_name");
+	}
+	elsif (defined($delete_task_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to deleted existing scheduled task '$task_name' on $computer_node_name, exit status: $delete_task_exit_status, output:\n@{$delete_task_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command deleted existing scheduled task '$task_name' on $computer_node_name");
+		return;
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_startup_scheduled_task
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub create_startup_scheduled_task {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $task_name     = shift;
+	my $task_command  = shift;
+	my $task_user     = shift;
+	my $task_password = shift;
+
+	# Escape backslashes, can never have enough...
+	$task_command =~ s/\\/\\\\/g;
+
+	# Replace forward slashes with backslashes
+	$task_command =~ s/([^\s])\//$1\\\\/g;
+	
+	# Escape quote characters
+	$task_command =~ s/"/\\"/g;
+
+	# Make sure arguments were supplied
+	if (!defined($task_name) || !defined($task_command) || !defined($task_user) || !defined($task_password)) {
+		notify($ERRORS{'WARNING'}, 0, "startup scheduled task not added, arguments were not passed correctly");
+		return;
+	}
+
+	# You cannot create a task if one with the same name already exists
+	# Vista's version of schtasks.exe has a /F which forces a new task to be created if one with the same name already exists
+	# This option isn't supported with XP and other older versions of Windows
+	if (!$self->delete_scheduled_task($task_name)) {
+		notify($ERRORS{'WARNING'}, 0, "unable to delete existing scheduled task '$task_name' on $computer_node_name");
+	}
+	
+	# Run schtasks.exe to add the task
+	# Occasionally see this error even though it schtasks.exe returns exit status 0:
+	# WARNING: The Scheduled task "VCL Startup Script" has been created, but may not run because the account information could not be set.
+	my $create_task_command = "schtasks.exe /Create /RU \"$task_user\" /RP \"$task_password\" /SC ONSTART /TN \"$task_name\" /TR \"$task_command\"";
+	my ($create_task_exit_status, $create_task_output) = run_ssh_command($computer_node_name, $management_node_keys, $create_task_command);
+	if (defined($create_task_output) && grep(/could not be set/i, @{$create_task_output})) {
+		notify($ERRORS{'WARNING'}, 0, "created scheduled task '$task_name' on $computer_node_name but error occurred: " . join("\n", @{$create_task_output}));
+		return 0;
+	}
+	elsif (defined($create_task_exit_status) && $create_task_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "created scheduled task '$task_name' on $computer_node_name");
+	}
+	elsif (defined($create_task_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to create scheduled task '$task_name' on $computer_node_name, exit status: $create_task_exit_status, output:\n@{$create_task_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command created scheduled task '$task_name' on $computer_node_name");
+		return;
+	}
+
+	return 1;
+} ## end sub create_startup_scheduled_task
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 enable_autoadminlogon
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub enable_autoadminlogon {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $registry_string .= <<"EOF";
+Windows Registry Editor Version 5.00
+
+; This file enables autoadminlogon for the root account
+
+[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon]
+"AutoAdminLogon"="1"
+"DefaultUserName"="root"
+"DefaultPassword"= "$WINDOWS_ROOT_PASSWORD"
+
+EOF
+
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "enabled autoadminlogon");
+		return 1;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to enable autoadminlogon");
+		return 0;
+	}
+} ## end sub enable_autoadminlogon
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 disable_autoadminlogon
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub disable_autoadminlogon {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $registry_string .= <<EOF;
+Windows Registry Editor Version 5.00
+
+; This file disables autoadminlogon for the root account
+
+[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon]
+"AutoAdminLogon"="0"
+"AutoLogonCount"="0"
+"DefaultPassword"= ""
+EOF
+
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "disabled autoadminlogon");
+		return 1;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to disable autoadminlogon");
+		return 0;
+	}
+} ## end sub disable_autoadminlogon
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_eventlog_entry
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub create_eventlog_entry {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $message = shift;
+
+	# Make sure the message was passed as an argument
+	if (!defined($message)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to create eventlog entry, message was passed as an argument");
+		return 0;
+	}
+
+	# Run eventcreate.exe to create an event log entry
+	my $eventcreate_command = '$SYSTEMROOT/System32/eventcreate.exe /T INFORMATION /L APPLICATION /SO VCL /ID 555 /D "' . $message . '"';
+	my ($eventcreate_exit_status, $eventcreate_output) = run_ssh_command($computer_node_name, $management_node_keys, $eventcreate_command);
+	if (defined($eventcreate_exit_status) && $eventcreate_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "created event log entry on $computer_node_name: $message");
+	}
+	elsif (defined($eventcreate_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to create event log entry on $computer_node_name: $message, exit status: $eventcreate_exit_status, output:\n@{$eventcreate_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to create event log entry on $computer_node_name: $message");
+		return;
+	}
+
+	return 1;
+} ## end sub create_eventlog_entry
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 reboot
+
+ Parameters  : $wait_for_reboot
+ Returns     : 
+ Description : 
+
+=cut
+
+sub reboot {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Check if an argument was supplied
+	my $wait_for_reboot = shift;
+	if (!defined($wait_for_reboot) || $wait_for_reboot !~ /0/) {
+		notify($ERRORS{'DEBUG'}, 0, "rebooting $computer_node_name and waiting for ssh to become active");
+		$wait_for_reboot = 1;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "rebooting $computer_node_name and NOT waiting");
+		$wait_for_reboot = 0;
+	}
+
+	my $reboot_start_time = time();
+	notify($ERRORS{'DEBUG'}, 0, "reboot will be attempted on $computer_node_name");
+
+	# Check if computer responds to ssh before preparing for reboot
+	if ($self->wait_for_ssh(0)) {
+		# Make sure SSH access is enabled from private IP addresses
+		if (!$self->firewall_enable_ssh_private()) {
+			notify($ERRORS{'WARNING'}, 0, "reboot not attempted, failed to enable ssh from private IP addresses");
+			return 0;
+		}
+
+		# Set sshd service startup mode to auto
+		if (!$self->set_service_startup_mode('sshd', 'auto')) {
+			notify($ERRORS{'WARNING'}, 0, "reboot not attempted, unable to set sshd service startup mode to auto");
+			return 0;
+		}
+
+		# Make sure ping access is enabled from private IP addresses
+		if (!$self->firewall_enable_ping_private()) {
+			notify($ERRORS{'WARNING'}, 0, "reboot not attempted, failed to enable ping from private IP addresses");
+			return 0;
+		}
+
+		# Initiate the shutdown.exe command to reboot the computer
+		my $shutdown_command = "C:/Windows/system32/shutdown.exe -r -t 0 -f";
+		my ($shutdown_exit_status, $shutdown_output) = run_ssh_command($computer_node_name, $management_node_keys, $shutdown_command);
+		if (defined($shutdown_exit_status) && $shutdown_exit_status == 0) {
+			notify($ERRORS{'OK'}, 0, "executed reboot command on $computer_node_name");
+		}
+		elsif (defined($shutdown_exit_status)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to execute reboot command on $computer_node_name, exit status: $shutdown_exit_status, output:\n@{$shutdown_output}");
+			return 0;
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command to reboot $computer_node_name");
+			return;
+		}
+	} ## end if ($self->wait_for_ssh(0))
+	else {
+		# Computer did not respond to ssh
+		notify($ERRORS{'WARNING'}, 0, "$computer_node_name did not respond to ssh, graceful reboot cannot be performed, attempting hard reset");
+
+		# Call provisioning module's power_reset() subroutine
+		if ($self->provisioner->power_reset()) {
+			notify($ERRORS{'OK'}, 0, "initiated power reset on $computer_node_name");
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "reboot failed, failed to initiate power reset on $computer_node_name");
+			return 0;
+		}
+	} ## end else [ if ($self->wait_for_ssh(0))
+
+	# Check if wait for reboot is set
+	if (!$wait_for_reboot) {
+		return 1;
+	}
+
+	# Wait for reboot is true
+	notify($ERRORS{'OK'}, 0, "sleeping for 30 seconds while $computer_node_name begins to boot");
+	sleep 30;
+
+	# Make multiple attempts to wait for the reboot to complete
+	my $wait_attempt_limit = 2;
+	WAIT_ATTEMPT:
+	for (my $wait_attempt = 1; $wait_attempt <= $wait_attempt_limit; $wait_attempt++) {
+		if ($wait_attempt > 1) {
+			# Computer did not become fully responsive on previous wait attempt
+			notify($ERRORS{'OK'}, 0, "$computer_node_name reboot failed to complete on previous attempt, attempting hard power reset");
+
+			# Call provisioning module's power_reset() subroutine
+			if ($self->provisioner->power_reset()) {
+				notify($ERRORS{'OK'}, 0, "reboot attempt $wait_attempt/$wait_attempt_limit: initiated power reset on $computer_node_name");
+			}
+			else {
+				notify($ERRORS{'WARNING'}, 0, "reboot failed, failed to initiate power reset on $computer_node_name");
+				return 0;
+			}
+		} ## end if ($wait_attempt > 1)
+
+		# Wait maximum of 2 minutes for the computer to become unresponsive
+		if (!$self->wait_for_no_ping(2)) {
+			# Computer never stopped responding to ping
+			notify($ERRORS{'WARNING'}, 0, "$computer_node_name never became unresponsive to ping");
+			next WAIT_ATTEMPT;
+		}
+
+		# Computer is unresponsive, reboot has begun
+		# Wait for 30 seconds before beginning to check if computer is back online
+		notify($ERRORS{'DEBUG'}, 0, "$computer_node_name reboot has begun, sleeping for 30 seconds");
+		sleep 30;
+
+		# Wait maximum of 4 minutes for the computer to come back up
+		if (!$self->wait_for_ping(4)) {
+			# Check if the computer was ever offline, it should have been or else reboot never happened
+			notify($ERRORS{'WARNING'}, 0, "$computer_node_name never responded to ping");
+			next WAIT_ATTEMPT;
+		}
+
+		notify($ERRORS{'DEBUG'}, 0, "$computer_node_name is pingable, waiting for ssh to respond");
+
+		# Wait maximum of 3 minutes for ssh to respond
+		if (!$self->wait_for_ssh(3)) {
+			notify($ERRORS{'WARNING'}, 0, "ssh never responded on $computer_node_name");
+			next WAIT_ATTEMPT;
+		}
+
+		# Wait then check ssh again in case initialization scripts are running
+		# ssh may be available when the computer first boots, then network configuration scripts may automatically run
+		# Make sure ssh is available a short time after it's first available
+		notify($ERRORS{'DEBUG'}, 0, "$computer_node_name responded to ssh, sleeping for 20 seconds then checking ssh again");
+		sleep 20;
+
+		# Wait maximum of 2 minutes for ssh to respond
+		if (!$self->wait_for_ssh(2)) {
+			notify($ERRORS{'WARNING'}, 0, "ssh responded then stopped responding on $computer_node_name");
+			next WAIT_ATTEMPT;
+		}
+
+		# Reboot was successful, calculate how long reboot took
+		my $reboot_end_time = time();
+		my $reboot_duration = ($reboot_end_time - $reboot_start_time);
+		notify($ERRORS{'OK'}, 0, "reboot complete on $computer_node_name, took $reboot_duration seconds");
+		return 1;
+	} ## end for (my $wait_attempt = 1; $wait_attempt <=...
+
+	# If loop completed, maximum number of reboot attempts was reached
+	notify($ERRORS{'WARNING'}, 0, "reboot failed on $computer_node_name, made $wait_attempt_limit attempts");
+	return 0;
+} ## end sub reboot
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 wait_for_ping
+
+ Parameters  : Maximum number of minutes to wait (optional)
+ Returns     : 1 if computer is pingable, 0 otherwise
+ Description : Attempts to ping the computer specified in the DataStructure
+               for the current reservation. It will wait up to a maximum number
+					of minutes. This can be specified by passing the subroutine an
+					integer value or the default value of 5 minutes will be used.
+
+=cut
+
+sub wait_for_ping {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $computer_node_name = $self->data->get_computer_node_name();
+
+	# Attempt to get the total number of minutes to wait from the command line
+	my $total_wait_minutes = shift;
+	if (!defined($total_wait_minutes) || $total_wait_minutes !~ /^\d+$/) {
+		$total_wait_minutes = 5;
+	}
+
+	# Looping configuration variables
+	# Seconds to wait in between loop attempts
+	my $attempt_delay = 15;
+	# Total loop attempts made
+	# Add 1 to the number of attempts because if you're waiting for x intervals, you check x+1 times including at 0
+	my $attempts = ($total_wait_minutes * 4) + 1;
+
+	notify($ERRORS{'OK'}, 0, "waiting for $computer_node_name to respond to ping, maximum of $total_wait_minutes minutes");
+
+	# Loop until computer is pingable
+	for (my $attempt = 1; $attempt <= $attempts; $attempt++) {
+		if ($attempt > 1) {
+			notify($ERRORS{'OK'}, 0, "attempt " . ($attempt - 1) . "/" . ($attempts - 1) . ": $computer_node_name is not pingable, sleeping for $attempt_delay seconds");
+			sleep $attempt_delay;
+		}
+
+		if (_pingnode($computer_node_name)) {
+			notify($ERRORS{'OK'}, 0, "$computer_node_name is pingable");
+			return 1;
+		}
+	} ## end for (my $attempt = 1; $attempt <= $attempts...
+
+	# Calculate how long this waited
+	my $total_wait = ($attempts * $attempt_delay);
+	notify($ERRORS{'WARNING'}, 0, "$computer_node_name is NOT pingable after waiting for $total_wait seconds");
+	return 0;
+} ## end sub wait_for_ping
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 wait_for_no_ping
+
+ Parameters  : Maximum number of minutes to wait (optional)
+ Returns     : 1 if computer is not pingable, 0 otherwise
+ Description : Attempts to ping the computer specified in the DataStructure
+               for the current reservation. It will wait up to a maximum number
+					of minutes for ping to fail.
+
+=cut
+
+sub wait_for_no_ping {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $computer_node_name = $self->data->get_computer_node_name();
+
+	# Attempt to get the total number of minutes to wait from the command line
+	my $total_wait_minutes = shift;
+	if (!defined($total_wait_minutes) || $total_wait_minutes !~ /^\d+$/) {
+		$total_wait_minutes = 5;
+	}
+
+	# Looping configuration variables
+	# Seconds to wait in between loop attempts
+	my $attempt_delay = 15;
+	# Total loop attempts made
+	# Add 1 to the number of attempts because if you're waiting for x intervals, you check x+1 times including at 0
+	my $attempts = ($total_wait_minutes * 4) + 1;
+
+	notify($ERRORS{'OK'}, 0, "waiting for $computer_node_name to become unresponsive, maximum of $total_wait_minutes minutes");
+
+	# Loop until computer is offline
+	for (my $attempt = 1; $attempt <= $attempts; $attempt++) {
+		if ($attempt > 1) {
+			notify($ERRORS{'OK'}, 0, "attempt " . ($attempt - 1) . "/" . ($attempts - 1) . ": $computer_node_name is still pingable, sleeping for $attempt_delay seconds");
+			sleep $attempt_delay;
+		}
+
+		if (!_pingnode($computer_node_name)) {
+			notify($ERRORS{'OK'}, 0, "$computer_node_name is not pingable, returning 1");
+			return 1;
+		}
+	} ## end for (my $attempt = 1; $attempt <= $attempts...
+
+	# Calculate how long this waited
+	my $total_wait = ($attempts * $attempt_delay);
+	notify($ERRORS{'WARNING'}, 0, "$computer_node_name is still pingable after waiting for $total_wait seconds");
+	return 0;
+} ## end sub wait_for_no_ping
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 wait_for_ssh
+
+ Parameters  : Maximum number of minutes to wait (optional)
+ Returns     : 1 if ssh succeeded to computer, 0 otherwise
+ Description : Attempts to communicate to the computer specified in the
+               DataStructure for the current reservation via SSH. It will wait
+					up to a maximum number of minutes. This can be specified by
+					passing the subroutine an integer value or the default value
+					of 5 minutes will be used.
+
+=cut
+
+sub wait_for_ssh {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Attempt to get the total number of minutes to wait from the arguments
+	# If not specified, use default value
+	my $total_wait_minutes = shift;
+	if (!defined($total_wait_minutes) || $total_wait_minutes !~ /^\d+$/) {
+		$total_wait_minutes = 5;
+	}
+
+	# Looping configuration variables
+	# Seconds to wait in between loop attempts
+	my $attempt_delay = 15;
+	# Total loop attempts made
+	# Add 1 to the number of attempts because if you're waiting for x intervals, you check x+1 times including at 0
+	my $attempts = ($total_wait_minutes * 4) + 1;
+
+	notify($ERRORS{'OK'}, 0, "waiting for $computer_node_name to respond to ssh, maximum of $total_wait_minutes minutes");
+
+	# Loop until ssh is available
+	my $ssh_result = 0;
+	for (my $attempt = 1; $attempt <= $attempts; $attempt++) {
+		if ($attempt > 1) {
+			notify($ERRORS{'OK'}, 0, "attempt " . ($attempt - 1) . "/" . ($attempts - 1) . ": $computer_node_name did not respond to ssh, sleeping for $attempt_delay seconds");
+			sleep $attempt_delay;
+		}
+
+		# Try nmap to see if any of the ssh ports are open before attempting to run a test command
+		if (!nmap_port($computer_node_name, 22) && !nmap_port($computer_node_name, 24)) {
+			notify($ERRORS{'DEBUG'}, 0, "ports 22 and 24 are closed on $computer_node_name according to nmap");
+			next;
+		}
+
+		# Run a test SSH command
+		my ($exit_status, $output) = run_ssh_command($computer_node_name, $management_node_keys, "echo testing ssh on $computer_node_name", '', '', 1);
+
+		# The exit status will be 0 if the command succeeded
+		if (defined($exit_status) && $exit_status == 0) {
+			notify($ERRORS{'OK'}, 0, "$computer_node_name is responding to ssh");
+			return 1;
+		}
+	} ## end for (my $attempt = 1; $attempt <= $attempts...
+
+	notify($ERRORS{'WARNING'}, 0, "$computer_node_name is not available via ssh");
+	return 0;
+} ## end sub wait_for_ssh
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 set_service_startup_mode
+
+ Parameters  : 
+ Returns     : 1 if succeeded, 0 otherwise
+ Description : 
+
+=cut
+
+sub set_service_startup_mode {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	my $service_name = shift;
+	my $startup_mode = shift;
+
+	# Make sure both arguments were supplied
+	if (!defined($service_name) && !defined($startup_mode)) {
+		notify($ERRORS{'WARNING'}, 0, "set service startup mode failed, service name and startup mode arguments were not passed correctly");
+		return 0;
+	}
+
+	# Make sure the startup mode is valid
+	if ($startup_mode !~ /boot|system|auto|demand|disabled|delayed-auto|manual/i) {
+		notify($ERRORS{'WARNING'}, 0, "set service startup mode failed, invalid startup mode: $startup_mode");
+		return 0;
+	}
+
+	# Set the mode to demand if manual was specified, specific to sc command
+	$startup_mode = "demand" if ($startup_mode eq "manual");
+
+	# Use sc.exe to change the start mode
+	my $service_startup_command = '"$SYSTEMROOT/System32/sc.exe" config ' . "$service_name start= $startup_mode";
+	my ($service_startup_exit_status, $service_startup_output) = run_ssh_command($computer_node_name, $management_node_keys, $service_startup_command);
+	if (defined($service_startup_exit_status) && $service_startup_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "$service_name service startup mode set to $startup_mode");
+	}
+	elsif ($service_startup_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to set $service_name service startup mode to $startup_mode, exit status: $service_startup_exit_status, output:\n@{$service_startup_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to set $service_name service startup mode to $startup_mode, exit status: $service_startup_exit_status, output:\n@{$service_startup_output}");
+		return;
+	}
+
+	return 1;
+} ## end sub set_service_startup_mode
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 defragment_hard_drive
+
+ Parameters  : 
+ Returns     : 1 if succeeded, 0 otherwise
+ Description : 
+
+=cut
+
+sub defragment_hard_drive {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+
+	# Defragment the hard drive
+	notify($ERRORS{'OK'}, 0, "beginning to defragment the hard drive on $computer_node_name");
+	my ($defrag_exit_status, $defrag_output) = run_ssh_command($computer_node_name, $management_node_keys, '$SYSTEMROOT/System32/defrag.exe $SYSTEMDRIVE -v');
+	if (defined($defrag_exit_status) && $defrag_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "hard drive defragmentation complete on $computer_node_name");
+		return 1;
+	}
+	elsif (defined($defrag_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to defragment the hard drive, exit status: $defrag_exit_status, output:\n@{$defrag_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run the SSH command to defragment the hard drive");
+		return;
+	}
+} ## end sub defragment_hard_drive
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 run_newsid
+
+ Parameters  : 
+ Returns     : 1 success 0 failure
+ Description : 
+
+=cut
+
+sub run_newsid {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+
+	my $reservation_id       = $self->data->get_reservation_id();
+	my $management_node_keys = $self->data->get_management_node_keys();
+	my $computer_node_name   = $self->data->get_computer_node_name();
+	my $computer_id          = $self->data->get_computer_id();
+
+	# Attempt to get the computer name from the arguments
+	# If no argument was supplied, use the name specified in the DataStructure
+	my $computer_name = shift;
+	if (!(defined($computer_name))) {
+		my $image_id            = $self->data->get_image_id();
+		my $computer_short_name = $self->data->get_computer_short_name();
+		$computer_name = "$computer_short_name-$image_id";
+	}
+
+	my $registry_string .= <<'EOF';
+Windows Registry Editor Version 5.00
+
+; This registry file contains the entries to bypass the license agreement when newsid.exe is run
+
+[HKEY_CURRENT_USER\\Software\\Sysinternals\\NewSID]
+"EulaAccepted"=dword:00000001
+EOF
+
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'DEBUG'}, 0, "added newsid eulaaccepted registry string");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to add newsid eulaaccepted registry string");
+		return 0;
+	}
+
+	# Attempt to run newsid.exe
+	# newsid.exe should automatically reboot the computer
+	# It isn't done when the process exits, newsid.exe starts working and immediately returns
+	# NewSid.exe [/a[[/n]|[/d <reboot delay (in seconds)>]]][<new computer name>]
+	# /a - run without prompts
+	# /n - Don't reboot after automatic run
+	my $newsid_command               = "\"$NODE_CONFIGURATION_DIRECTORY/Utilities/newsid.exe\" /a \"$computer_name\"";
+	my $newsid_start_processing_time = time();
+	my ($newsid_exit_status, $newsid_output) = run_ssh_command($computer_node_name, $management_node_keys, $newsid_command);
+	if (defined($newsid_exit_status) && $newsid_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "newsid.exe has been started on $computer_node_name, new computer name: $computer_name");
+	}
+	elsif (defined($newsid_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to start newsid.exe on $computer_node_name, exit status: $newsid_exit_status, output:\n@{$newsid_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to start newsid.exe on $computer_node_name");
+		return;
+	}
+
+	my $newsid_end_processing_time = time();
+	my $newsid_processing_duration = ($newsid_end_processing_time - $newsid_start_processing_time);
+	notify($ERRORS{'OK'}, 0, "newsid.exe complete, newsid.exe took $newsid_processing_duration seconds");

[... 1994 lines stripped ...]


Mime
View raw message