Author: arkurth
Date: Mon Mar 19 20:18:44 2012
New Revision: 1302636

URL: http://svn.apache.org/viewvc?rev=1302636&view=rev
Log:
VCL-563
Added call to enable_dhcp in pre_capture.

Updated enable_dhcp. It was not working correctly if DHCP was enabled before it 
was called but the interface didn't have an IP address.


VCL-562
Added disable_user subroutine. Added get_user_names subroutine.

Added optional $user_password_only argument to set_password. This allows a 
password to be set without attempting to update passwords of any scheduled 
tasks or services which are configured to run as the user.


VCL-564
Updated run_script. It wasn't correctly retrieving the exit status of the 
script that was executed.

Added run_scripts subroutine. It searches directories under tools which match 
the OS and architecture of the image being loaded and then executes any scripts 
it finds.


VCL-561
Added check_image and update_imagerevision_info subroutines. These will 
eventually retrieve various inventory information from the image being loaded 
and store the information in the database. These aren't currently called.


Other
Update set_static_public_address to run the commands to set the address and 
configure DNS in a single command.

Modified:
    incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
URL: 
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=1302636&r1=1302635&r2=1302636&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Mon Mar 19 
20:18:44 2012
@@ -293,6 +293,17 @@ sub pre_capture {
 
 =item *
 
+ Enable DHCP on the private and public interfaces
+
+=cut
+
+       if (!$self->enable_dhcp()) {
+               notify($ERRORS{'WARNING'}, 0, "failed to enable DHCP");
+               return;
+       }
+
+=item *
+
  Copy the capture configuration files to the computer (scripts, utilities, 
drivers...)
 
 =cut
@@ -1968,6 +1979,7 @@ sub set_password {
        # Attempt to get the username from the arguments
        my $username = shift;
        my $password = shift;
+       my $user_password_only = shift;
 
        # If no argument was supplied, use the user specified in the 
DataStructure
        if (!defined($username)) {
@@ -1998,6 +2010,8 @@ sub set_password {
                return 0;
        }
        
+       return 1 if $user_password_only;
+       
        # Get the list of services
        my @services = $self->get_services_using_login_id($username);
        if ($username eq 'root' && !@services) {
@@ -2085,6 +2099,50 @@ sub enable_user {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 disable_user
+
+ Parameters  : $username
+ Returns     : boolean
+ Description : Disables the user account specified by the argument.
+
+=cut
+
+sub disable_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 $computer_node_name = $self->data->get_computer_node_name();
+       
+       # Attempt to get the username from the arguments
+       my $username = shift;
+       if (!defined($username)) {
+               notify($ERRORS{'WARNING'}, 0, "username argument was not 
supplied");
+               return;
+       }
+
+       # Attempt to enable the user account (set ACTIVE=NO)
+       notify($ERRORS{'DEBUG'}, 0, "disbling user $username on 
$computer_node_name");
+       my ($exit_status, $output) = $self->execute("net user $username 
/ACTIVE:NO");
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command to disable 
user $username on $computer_node_name");
+               return;
+       }
+       elsif (grep(/ successfully/, @$output)) {
+               notify($ERRORS{'OK'}, 0, "user $username disabled on 
$computer_node_name");
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to disable user $username 
on $computer_node_name, exit status: $exit_status, output:\n" . join("\n", 
@$output));
+               return;
+       }
+
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 disable_pagefile
 
  Parameters  :
@@ -3587,7 +3645,7 @@ sub shutdown {
        }
        
        # Get the argument that determines whether or not to disable DHCP 
before shutting down computer
-       my $disable_dhcp = shift;
+       my $enable_dhcp = shift;
 
        my $management_node_keys = $self->data->get_management_node_keys();
        my $computer_node_name   = $self->data->get_computer_node_name();
@@ -3601,7 +3659,7 @@ sub shutdown {
        
        my $shutdown_command = "/bin/cygstart.exe \$SYSTEMROOT/system32/cmd.exe 
/c \"";
        
-       if ($disable_dhcp) {
+       if ($enable_dhcp) {
                notify($ERRORS{'DEBUG'}, 0, "enabling DHCP and shutting down 
$computer_node_name");
                
                my $private_interface_name = 
$self->get_private_interface_name();
@@ -5691,30 +5749,43 @@ sub enable_dhcp {
        else {
                push(@interface_names, $interface_name_argument);
        }
-
+       
+       # Delete cached network configuration information so it is retrieved 
next time it is needed
+       delete $self->{network_configuration};
+       
+       # Delete any default routes
+       $self->delete_default_routes();
+       
        for my $interface_name (@interface_names) {
                # Use netsh.exe to set the NIC to use DHCP
-               my $set_dhcp_command = $system32_path . '/netsh.exe interface 
ip set address name="' . $interface_name . '" source=dhcp';
+               my $set_dhcp_command;
+               $set_dhcp_command .= "$system32_path/ipconfig.exe /release 
\"$interface_name\" 2>&1";
+               $set_dhcp_command .= " ; $system32_path/netsh.exe interface ip 
set address name=\"$interface_name\" source=dhcp 2>&1";
+               $set_dhcp_command .= " ; $system32_path/netsh.exe interface ip 
set dns name=\"$interface_name\" source=dhcp register=none 2>&1";
+               
                my ($set_dhcp_status, $set_dhcp_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $set_dhcp_command);
-               if (defined($set_dhcp_status) && $set_dhcp_status == 0) {
-                       notify($ERRORS{'OK'}, 0, "set interface 
'$interface_name' to use dhcp");
-               }
-               elsif (defined($set_dhcp_output) && grep(/dhcp is already 
enabled/i, @{$set_dhcp_output})) {
-                       notify($ERRORS{'OK'}, 0, "dhcp is already enabled on 
interface '$interface_name'");
+               if (!defined($set_dhcp_output)) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to execute 
command to enable DHCP for interface '$interface_name'");
+                       return 0;
                }
-               elsif (defined($set_dhcp_status)) {
-                       notify($ERRORS{'WARNING'}, 0, "unable to set interface 
'$interface_name' to use dhcp, exit status: $set_dhcp_status, 
output:\n@{$set_dhcp_output}");
+               elsif ($set_dhcp_status ne '0') {
+                       notify($ERRORS{'WARNING'}, 0, "failed to enable DHCP 
for interface '$interface_name', exit status: $set_dhcp_status, output:\n" . 
join("\n", @$set_dhcp_output));
                        return 0;
                }
                else {
-                       notify($ERRORS{'WARNING'}, 0, "unable to run ssh 
command to set interface '$interface_name' to use dhcp");
-                       return 0;
+                       notify($ERRORS{'OK'}, 0, "enabled DHCP for interface 
'$interface_name', exit status: $set_dhcp_status, output:\n" . join("\n", 
@$set_dhcp_output));
+               }
+               
+               # Run ipconfig /renew
+               if (!$self->ipconfig_renew()) {
+                       return;
                }
        } ## end for my $interface_name (@interface_names)
        
-       # Run ipconfig /renew after setting the adapters to use DHCP
-       # The default gateway gets lost otherwise
-       return $self->ipconfig_renew();
+       # Add persistent static public default route
+       $self->set_public_default_route();
+       
+       return 1;
 } ## end sub enable_dhcp
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -5734,7 +5805,6 @@ sub ipconfig_renew {
                return;
        }
 
-       my $management_node_keys = $self->data->get_management_node_keys();
        my $computer_node_name   = $self->data->get_computer_node_name();
        my $system32_path        = $self->get_system32_path() || return;
        
@@ -5746,24 +5816,30 @@ sub ipconfig_renew {
                $ipconfig_command .= " \"$interface_name_argument\"";
        }
        
-       # Run ipconfig
-       my ($ipconfig_status, $ipconfig_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $ipconfig_command);
-       if (defined($ipconfig_status) && $ipconfig_status == 0) {
-               notify($ERRORS{'OK'}, 0, "ran ipconfig /renew");
+       # Undefined previously retrieved network configuration so that it is 
retrieved again
+       $self->{network_configuration} = undef;
+       
+       my $attempt_limit = 3;
+       my $attempt = 0;
+       
+       while ($attempt < $attempt_limit) {
+               $attempt++;
                
-               # Undefined previously retrieved network configuration so that 
it is retrieved again
-               $self->{network_configuration} = undef;
-       }
-       elsif (defined($ipconfig_status)) {
-               notify($ERRORS{'WARNING'}, 0, "unable to run ipconfig /renew, 
exit status: $ipconfig_status, output:\n@{$ipconfig_output}");
-               return 0;
-       }
-       else {
-               notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to to 
run ipconfig /renew");
-               return 0;
+               # Run ipconfig
+               my ($ipconfig_status, $ipconfig_output) = 
$self->execute($ipconfig_command, 1, 65);
+               if (!defined($ipconfig_output)) {
+                       notify($ERRORS{'WARNING'}, 0, "attempt 
$attempt/$attempt_limit: failed to execute command to renew IP configuration");
+               }
+               elsif ($ipconfig_status ne '0' || grep(/error occurred/i, 
@$ipconfig_output) || !grep(/IP Address/i, @$ipconfig_output)) {
+                       notify($ERRORS{'WARNING'}, 0, "attempt 
$attempt/$attempt_limit: failed to renew IP configuration, exit status: 
$ipconfig_status, output:\n" . join("\n", @$ipconfig_output));
+               }
+               else {
+                       notify($ERRORS{'OK'}, 0, "renewed IP configuration, 
output:\n" . join("\n", @$ipconfig_output));
+                       return 1;
+               }
        }
        
-       return 1;
+       return;
 } 
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -7083,7 +7159,7 @@ sub get_installed_applications {
 
        # Attempt to query the registry for installed applications
        my $reg_query_command = $system32_path . '/reg.exe QUERY 
"HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall" /s';
-       my ($reg_query_exit_status, $reg_query_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $reg_query_command, 
'', '', 1);
+       my ($reg_query_exit_status, $reg_query_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $reg_query_command, 
'', '', 0);
        if (defined($reg_query_exit_status) && $reg_query_exit_status == 0) {
                notify($ERRORS{'DEBUG'}, 0, "queried Uninstall registry keys");
        }
@@ -8054,67 +8130,34 @@ EOF
                notify($ERRORS{'OK'}, 0, "attempting to set static public IP 
address on $computer_name:\n$configuration_info_string");
        }
        
-       # Set the static public IP address
-       my $address_command = "$system32_path/netsh.exe interface ip set 
address name=\"$interface_name\" source=static addr=$ip_address 
mask=$subnet_mask gateway=$default_gateway gwmetric=0";
-       
-       # Set number of attempts to try netsh.exe commands
-       my $max_attempts = 3;
-       my $address_attempts = 0;
-       while ($address_attempts < $max_attempts) {
-               $address_attempts++;
-               my ($address_exit_status, $address_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $address_command);
-               if (defined($address_exit_status) && $address_exit_status == 0) 
{
-                       notify($ERRORS{'DEBUG'}, 0, "set static public IP 
address to $ip_address");
-                       last;
-               }
-               elsif (defined($address_exit_status)) {
-                       notify($ERRORS{'WARNING'}, 0, "attempt 
$address_attempts/$max_attempts: failed to set static public IP address to 
$ip_address, exit status: $address_exit_status, output:\n@{$address_output}");
-               }
-               else {
-                       notify($ERRORS{'WARNING'}, 0, "attempt 
$address_attempts/$max_attempts: failed to run ssh command to set static public 
IP address to $ip_address");
-               }
-               
-               # Check if max attempts has been reached.
-               if ($address_attempts >= $max_attempts) {
-                       notify($ERRORS{'WARNING'}, 0, "failed to set static 
public IP address after making $address_attempts attempts");
-                       return 0;
-               }
-               
-               sleep 2;
-       }
-       
        my $primary_dns_server_address = shift @dns_servers;
        notify($ERRORS{'DEBUG'}, 0, "primary DNS server address: 
$primary_dns_server_address\nalternate DNS server address(s):\n" . (join("\n", 
@dns_servers) || '<none>'));
        
+       # Delete any default routes
+       $self->delete_default_routes();
+       
+       # Set the static public IP address
+       my $command = "$system32_path/netsh.exe interface ip set address 
name=\"$interface_name\" source=static addr=$ip_address mask=$subnet_mask 
gateway=$default_gateway gwmetric=0";
+       
        # Set the static DNS server address
-       my $dns_command = "$system32_path/netsh.exe interface ip set dns 
name=\"$interface_name\" source=static addr=$primary_dns_server_address 
register=none";
-       my ($dns_exit_status, $dns_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $dns_command);
-       if (!defined($dns_output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to set 
static primary DNS server address to $primary_dns_server_address");
+       $command .= " && $system32_path/netsh.exe interface ip set dns 
name=\"$interface_name\" source=static addr=$primary_dns_server_address 
register=none";
+       
+       # Add commands to set the alternate DNS server addresses
+       for my $alternate_dns_server_address (@dns_servers) {
+               $command .= " && $system32_path/netsh.exe interface ip add dns 
name=\"$interface_name\" addr=$alternate_dns_server_address";
+       }
+       
+       my ($exit_status, $output) = $self->execute($command, 1, 60, 3);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to set 
static public IP address");
                return;
        }
-       elsif ($dns_exit_status) {
-               notify($ERRORS{'WARNING'}, 0, "failed to set static primary DNS 
server address to $primary_dns_server_address, exit status: $dns_exit_status, 
output:\n@{$dns_output}");
-               return 0;
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to set static public IP 
address, exit status: $exit_status, output:\n" . join("\n", @$output));
+               return;
        }
        else {
-               notify($ERRORS{'DEBUG'}, 0, "set static primary DNS server 
address to $primary_dns_server_address");
-       }
-       
-       
-       # We are only going to set up alternate dns server              
-       for my $alternate_dns_server_address (@dns_servers) {
-               my $alternate_dns_command = "$system32_path/netsh.exe interface 
ip add dns name=\"$interface_name\" addr=$alternate_dns_server_address";
-               my ($alternate_dns_exit_status, $alternate_dns_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $dns_command);
-               if (!defined($alternate_dns_output)) {
-                       notify($ERRORS{'WARNING'}, 0, "failed to run ssh 
command to set static alternate DNS server address to 
$alternate_dns_server_address");
-               }
-               elsif ($alternate_dns_exit_status) {
-                       notify($ERRORS{'WARNING'}, 0, "failed to set static 
alternate DNS server address to $alternate_dns_server_address, exit status: 
$alternate_dns_exit_status, output:\n" . join("\n", @$alternate_dns_output));
-               }
-               else {
-                       notify($ERRORS{'DEBUG'}, 0, "set static alternate DNS 
server address to $alternate_dns_server_address");
-               }
+               notify($ERRORS{'OK'}, 0, "set static public IP address");
        }
        
        # Add persistent static public default route
@@ -8220,16 +8263,16 @@ sub set_public_default_route {
        # Add a persistent route to the public default gateway
        my $route_add_command    = "route -p ADD 0.0.0.0 MASK 0.0.0.0 
$default_gateway METRIC 1";
        my ($route_add_exit_status, $route_add_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $route_add_command);
-       if (defined($route_add_exit_status) && $route_add_exit_status == 0) {
-               notify($ERRORS{'OK'}, 0, "added persistent route to public 
default gateway: $default_gateway");
+       if (!defined($route_add_output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
persistent route to public default gateway: $default_gateway");
+               return;
        }
-       elsif (defined($route_add_exit_status)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to add persistent route 
to public default gateway: $default_gateway, exit status: 
$route_add_exit_status, output:\n@{$route_add_output}");
+       elsif ($route_add_exit_status ne '0' || grep(/failed/i, 
@$route_add_output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to add persistent route 
to public default gateway: $default_gateway, exit status: 
$route_add_exit_status, output:\n" . join("\n", @$route_add_output));
                return;
        }
        else {
-               notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to add 
persistent route to public default gateway: $default_gateway");
-               return;
+               notify($ERRORS{'OK'}, 0, "added persistent route to public 
default gateway: $default_gateway");
        }
        
        return 1;
@@ -10857,51 +10900,211 @@ sub run_script {
        
        # Determine the script name
        my ($script_name, $script_directory_path, $script_extension) = 
fileparse($script_path, qr/\.[^.]*/);
-       
-       notify($ERRORS{'DEBUG'}, 0, "script name: '$script_name', directory: 
'$script_directory_path', extension: '$script_extension'");
+       (my $script_path_escaped = $script_path) =~ s/( )/\^$1/g;
        
        # Get the node configuration directory, make sure it exists, create if 
necessary
        my $node_log_directory = $self->get_node_configuration_directory() . 
'/Logs';
-       if (!$self->create_directory($node_log_directory)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to create node log file 
directory: $node_log_directory");
-               return;
-       }
        
        # Assemble the log file path
        my $log_file_path = $node_log_directory . "/$script_name.log";
        
-       # Assemble the command
-       my $command;
-       $command .= "chmod +rx \"$script_path\"";
-       $command .= "&& echo \"" . '=' x 80 . "\" >> \"$log_file_path\"";
-       
        my $timestamp = makedatestring();
-       $command .= "&& echo \"$timestamp - $script_path executed by vcld\" >> 
\"$log_file_path\"";
        
-       $command .= "&& \"$script_path\" >> \"$log_file_path\" 2>&1";
+       # Assemble the command
+       my $command = "cmd.exe /c \"$script_path_escaped & exit %ERRORLEVEL%\"";
        
        # Execute the command
+       notify($ERRORS{'DEBUG'}, 0, "executing command: '$command'");
        my ($exit_status, $output) = $self->execute($command);
        if (!defined($output)) {
                notify($ERRORS{'WARNING'}, 0, "failed to execute script on 
$computer_node_name: '$script_path', command: '$command'");
                return;
        }
        
-       # Format the line breaks in the log file for the OS
-       $self->format_text_file($log_file_path);
+       # Create a log file containing the output
+       my $logfile_contents = "$timestamp - $script_path executed by vcld";
+       my $header_line_length = length($logfile_contents);
+       $logfile_contents = '=' x $header_line_length . 
"\r\n$logfile_contents\r\n" . '=' x $header_line_length . "\r\n";
+       $logfile_contents .= join("\r\n", @$output) . "\r\n";
+       $self->create_text_file($log_file_path, $logfile_contents, 1);
        
        if ($exit_status == 0) {
-               notify($ERRORS{'OK'}, 0, "successfully executed script on 
$computer_node_name: '$script_path', output saved to '$log_file_path'");
+               notify($ERRORS{'OK'}, 0, "successfully executed script on 
$computer_node_name: '$script_path', output saved to '$log_file_path', 
command:\n$command, output:\n" . join("\n". @$output));
                return 1;
        }
        else {
-               notify($ERRORS{'WARNING'}, 0, "script '$script_path' returned a 
non-zero exit status: $exit_status, output saved to '$log_file_path'\ncommand: 
'$command'\noutput:\n" . join("\n", @$output));
+               notify($ERRORS{'WARNING'}, 0, "script '$script_path' returned a 
non-zero exit status: $exit_status\nlogfile path: '$log_file_path'\ncommand: 
'$command'\noutput:\n" . join("\n", @$output));
                return 0;
        }
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 run_scripts
+
+ Parameters  : $stage
+ Returns     : boolean
+ Description : Runs scripts on the computer intended for the state specified by
+               the argument. The stage argument may be any of the following:
+               -pre_capture
+               -post_load
+               -post_reserve
+               
+               Scripts are stored in various directories under tools matching
+               the OS of the image being loaded. For example, scripts residing
+               in any of the following directories would be executed if the
+               stage argument is 'post_load' and the OS of the image being
+               loaded is Windows XP 32-bit:
+               -tools/Windows/Scripts/post_load
+               -tools/Windows/Scripts/post_load/x86
+               -tools/Windows_Version_5/Scripts/post_load
+               -tools/Windows_Version_5/Scripts/post_load/x86
+               -tools/Windows_XP/Scripts/post_load
+               -tools/Windows_XP/Scripts/post_load/x86
+               
+               The order the scripts are executed is determined by the script
+               file names. The directory where the script resides has no affect
+               on the order. Script files can be named beginning with a number.
+               The scripts sorted numerically and processed from the lowest
+               number to the highest:
+               -1.cmd
+               -50.cmd
+               -100.cmd
+               
+               Scripts which do not begin with a number are sorted
+               alphabetically and processed after any scripts which begin with 
a
+               number:
+               -1.cmd
+               -50.cmd
+               -100.cmd
+               -Blah.cmd
+               -foo.cmd
+
+=cut
+
+sub run_scripts {
+       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 stage argument
+       my $stage = shift;
+       if (!$stage) {
+               notify($ERRORS{'WANING'}, 0, "unable to run scripts, stage 
argument was not supplied");
+               return;
+       }
+       elsif ($stage !~ /(pre_capture|post_load|post_reserve)/) {
+               notify($ERRORS{'WANING'}, 0, "invalid stage argument was 
supplied: $stage");
+               return;
+       }
+       
+       my $computer_node_name = $self->data->get_computer_node_name();
+       
+       my @source_configuration_directories = 
$self->get_source_configuration_directories();
+       if (!@source_configuration_directories) {
+               notify($ERRORS{'WARNING'}, 0, "unable to retrieve source 
configuration directories");
+               return;
+       }
+       
+       my $architecture = $self->is_64_bit() ? 'x86_64' : 'x86';
+       
+       # Find any script files already on the computer residing in the 
Scripts/<stage> directory
+       my $computer_directory_path = 
"$NODE_CONFIGURATION_DIRECTORY/Scripts/$stage";
+       my $computer_files = $self->find_files($computer_directory_path, '*');
+
+       my $mn_files = {};
+       
+       # Loop through the directories on the management node
+       DIRECTORY: for my $source_configuration_directory 
(@source_configuration_directories) {
+               # Find script files on the managment node intended for the 
computer
+               my $mn_directory_path = 
"$source_configuration_directory/Scripts/$stage";
+               my $mn_directory_files = 
$self->mn_os->find_files($mn_directory_path, '*');
+               
+               # Loop through the files found on the management node
+               MN_FILE: for my $mn_file_path (keys(%$mn_directory_files)) {
+                       # Determine the relative path - this is used to match 
up files on the MN and files on the computer
+                       # The base directories differ but the path proceeding 
'Scripts' is the same
+                       # Example:
+                       #    MN file path:       
/usr/local/vcl/tools/Windows_Version_5/Scripts/post_load/x86/myscript.cmd
+                       #    Relative file path:                                
                          x86/myscript.cmd
+                       #    Computer file path:                
C:/cygwin/home/root/VCL/Scripts/post_load/x86/myscript.cmd
+                       my ($relative_file_path) = $mn_file_path =~ 
/$mn_directory_path\/(.+)/;
+                       
+                       # Add the file to the hash containing all script files 
found in all directories
+                       # This is used later to determine if any extra files 
exist on the computer but not on the MN
+                       $mn_files->{$relative_file_path} = 
$mn_directory_files->{$mn_file_path};
+                       
+                       # Check if the script file resides in an 
architecture-specific directory (/x86*/) not matching the architecture of the 
computer
+                       if ($relative_file_path !~ /^$architecture\// && 
$relative_file_path =~ /^x86/) {
+                               notify($ERRORS{'DEBUG'}, 0, "ignoring file 
intended for different computer architecture: $mn_file_path");
+                               next MN_FILE;
+                       }
+                       
+                       # Retrieve the checksum of the file on the management 
node from the file info returned by find_files
+                       my $mn_file_checksum = 
$mn_directory_files->{$mn_file_path}{checksum};
+                       
+                       notify($ERRORS{'DEBUG'}, 0, "checking if file on 
management node needs to be copied to $computer_node_name: $mn_file_path");
+                       
+                       # Assemble the script file path on the computer
+                       my $computer_file_path = 
"$computer_directory_path/$relative_file_path";
+                       
+                       # Check if the file already exists on the computer
+                       if ($computer_files->{$computer_file_path}) {
+                               # Check if the file already on the computer is 
exactly the same as the one on the MN by comparing checksums
+                               my $computer_file_checksum = 
$computer_files->{$computer_file_path}{checksum};
+                               if ($computer_file_checksum eq 
$mn_file_checksum) {
+                                       notify($ERRORS{'DEBUG'}, 0, "identical 
file exists on $computer_node_name: $computer_file_path");
+                                       next MN_FILE;
+                               }
+                               else {
+                                       notify($ERRORS{'DEBUG'}, 0, "file 
exists on $computer_node_name but checksum is different: $computer_file_path\n" 
.
+                                               "MN file checksum: 
$mn_file_checksum\n" .
+                                               "computer file checksum: 
$computer_file_checksum"
+                                       );
+                               }
+                       }
+                       else {
+                               notify($ERRORS{'DEBUG'}, 0, "file does not 
exist on $computer_node_name: $computer_file_path");
+                       }
+                       
+                       # File either doesn't already exist on the computer or 
file on computer is different than file on MN
+                       if (!$self->copy_file_to($mn_file_path, 
$computer_file_path)) {
+                               notify($ERRORS{'WARNING'}, 0, "file could not 
be copied from management node to $computer_node_name: $mn_file_path --> 
$computer_file_path");
+                               next MN_FILE;
+                       }
+                       
+                       # Add the file to the hash of files on the computer
+                       $computer_files->{$computer_file_path} = 
$mn_directory_files->{$mn_file_path};
+               }
+       }
+       
+       # Loop through all files found and/or copied to the computer
+       COMPUTER_FILE: for my $computer_file_path 
(sort_by_file_name(keys(%$computer_files))) {
+               my ($relative_file_path) = $computer_file_path =~ 
/$computer_directory_path\/(.+)/;
+               
+               # Check if file on the computer was not found on the management 
node
+               # If so, delete it from the computer
+               if (!defined($mn_files->{$relative_file_path})) {
+                       notify($ERRORS{'DEBUG'}, 0, "file exists on 
$computer_node_name but not on management node: $computer_file_path");
+                       $self->delete_file($computer_file_path);
+                       delete($computer_files->{$computer_file_path});
+                       next COMPUTER_FILE;
+               }
+               elsif ($computer_file_path !~ /\.(cmd|bat)$/i) {
+                       notify($ERRORS{'DEBUG'}, 0, "file on 
$computer_node_name not executed because extension is not .cmd or .bat: 
$computer_file_path");
+                       next COMPUTER_FILE;
+               }
+               
+               $self->run_script($computer_file_path);
+       }
+       
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 format_text_file
 
  Parameters  : $file_path
@@ -10927,7 +11130,7 @@ sub format_text_file {
        my $computer_node_name = $self->data->get_computer_node_name();
        
        # Execute the command
-       my $command = "unix2dos $file_path";
+       my $command = "unix2dos \"$file_path\"";
        my ($exit_status, $output) = $self->execute($command);
        if (!defined($output)) {
                notify($ERRORS{'WARNING'}, 0, "failed to format text file for 
Windows on $computer_node_name: command: '$command'");
@@ -10945,6 +11148,278 @@ sub format_text_file {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_user_names
+
+ Parameters  : none
+ Returns     : array
+ Description : Retrieves the user account names which exist on the computer.
+
+=cut
+
+sub get_user_names {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my $image_name = $self->data->get_image_name();
+       
+       my $command = "net user";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
retrieve user info from OS");
+               return;
+       }
+       
+       my $found_dash_line;
+       my @user_names;
+       for my $line (@$output) {
+               if ($line =~ /^---/) {
+                       $found_dash_line = 1;
+                       next;
+               }
+               elsif (!$found_dash_line || $line =~ /command completed/) {
+                       next;
+               }
+               
+               my @line_user_names = split(/[ \t]{2,}/, $line);
+               push @user_names, @line_user_names if @line_user_names;
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, "retrieved user names from $image_name: " . 
join(", ", sort { lc($a) cmp lc($b) } @user_names));
+       return @user_names;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 check_image
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Checks the image currently loaded on the computer and updates 
the
+               imagerevision table if necessary.
+                                       Note: This feature is not complete and 
is not currently called.
+
+=cut
+
+sub check_image {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       #$self->data->set_variable('ignore_users', 
'Administrator,cyg_server,Guest,root,sshd,HelpAssistant,SUPPORT_388945a0,ASPNET');
+       #$self->data->set_variable('disable_users', 'test');
+       
+       my $imagerevision_id = $self->data->get_imagerevision_id();
+       my $image_name = $self->data->get_image_name();
+       my $computer_node_name = $self->data->get_computer_node_name();
+       
+       # Get list of user names from loaded image
+       my @image_user_names = $self->get_user_names();
+       if (!@image_user_names) {
+               notify($ERRORS{'DEBUG'}, 0, "skipping image check, unable to 
retrieve user names from $computer_node_name");
+               return;
+       }
+       
+       # Get the list of reservation users - includes imagemeta users and 
server profile users
+       my $reservation_user_hashref = $self->data->get_reservation_users();
+       my @reservation_user_names = sort {lc($a) cmp lc($b)} (map { 
$reservation_user_hashref->{$_}{unityid} } (keys %$reservation_user_hashref));
+       my $reservation_user_names_regex = join("|", @reservation_user_names);
+
+       # Get list of user names which should be ignored in images (safe, 
normal users: Administrator, guest...)
+       my $ignore_user_names_variable = 
$self->data->get_variable('ignore_users') || '';
+       my @ignore_user_names = sort {lc($a) cmp lc($b)} (split(/[,;]+/, 
$ignore_user_names_variable));
+       my $ignore_user_names_regex = join("|", @ignore_user_names);
+       
+       # Get list of user names which should be disabled in images - known 
bad, unsafe
+       my $disable_user_names_variable = 
$self->data->get_variable('disable_users') || '';
+       my @disable_user_names = sort {lc($a) cmp lc($b)} (split(/[,;]+/, 
$disable_user_names_variable));
+       my $disable_user_names_regex = join("|", @disable_user_names);
+       
+       notify($ERRORS{'DEBUG'}, 0, "image users:\n" .
+                        "users on $image_name: " . join(", ", 
@image_user_names) . "\n" .
+                        "reservation users: " . join(", ", 
@reservation_user_names) . "\n" .
+                        "users which should be disabled for all images: " . 
join(", ", @disable_user_names) . "\n" .
+                        "users which can be ignored for all images: " . 
join(", ", @ignore_user_names) . "\n"
+       );
+       
+       my @image_user_names_reservation = ();
+       my @image_user_names_ignore = ();
+       my @image_user_names_report = ();
+       
+       OS_USER_NAME: for my $image_user_name (sort {lc($a) cmp lc($b)} 
@image_user_names) {
+               for my $disable_user_name_pattern (@disable_user_names) {
+                       if ($image_user_name =~ /$disable_user_name_pattern/i) {
+                               notify($ERRORS{'DEBUG'}, 0, "found user on 
$image_name which should be disabled: '$image_user_name' (matches pattern: 
'$disable_user_name_pattern')");
+                               
+                               my $random_password = getpw(11);
+                               if (!$self->set_password($image_user_name, 
$random_password, 1)) {
+                                       notify($ERRORS{'WARNING'}, 0, "failed 
to set random password for user: '$image_user_name'");
+                               }
+                               else {
+                                       notify($ERRORS{'OK'}, 0, "set random 
password for user: '$image_user_name', '$random_password'");
+                               }
+                               
+                               $self->disable_user($image_user_name);
+                       }
+               }
+               
+               if ($image_user_name =~ /^($reservation_user_names_regex)$/i) {
+                       notify($ERRORS{'DEBUG'}, 0, "ignoring reservation user 
on image: '$image_user_name'");
+                       push @image_user_names_reservation, $image_user_name;
+               }
+               elsif ($image_user_name =~ /^($ignore_user_names_regex)$/i) {
+                       notify($ERRORS{'DEBUG'}, 0, "ignoring user on image: 
'$image_user_name'");
+                       push @image_user_names_ignore, $image_user_name;
+               }
+               else {
+                       notify($ERRORS{'DEBUG'}, 0, "reporting user on image: 
'$image_user_name'");
+                       push @image_user_names_report, $image_user_name;
+               }
+       }
+       
+       my $firewall_state = $self->get_firewall_state();
+       
+       if (scalar(@image_user_names_report) > 0 || !$firewall_state || 
$firewall_state !~ /(1|yes|on|enabled)/i) {
+               notify($ERRORS{'DEBUG'}, 0, "reporting $image_name image to 
imagerevisioninfo table (imagerevision ID: $imagerevision_id):\n" .
+                                "firewall state: $firewall_state\n" .
+                                "reservation users found on image: " . join(", 
", @image_user_names_reservation) . "\n" .
+                                "ignored users found on image: " . join(", ", 
@image_user_names_ignore) . "\n" .
+                                "users which might not belong on image: " . 
join(", ", @image_user_names_report)
+               );
+               
+               $self->update_imagerevision_info($imagerevision_id, join(",", 
@image_user_names_report), $firewall_state);
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 update_imagerevision_info
+
+ Parameters  : $imagerevision_id, $usernames, $firewall_enabled
+ Returns     : boolean
+ Description : Updates the imagerevisioninfo table in the database.
+
+=cut
+
+sub update_imagerevision_info {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my $imagerevision_id = shift;
+       my $usernames = shift;
+       my $firewall_enabled = shift;
+       
+       if (!defined($imagerevision_id)) {
+               notify($ERRORS{'WARNING'}, 0, "imagerevision ID argument was 
not specified");
+               return;
+       }
+       elsif (!defined($usernames) && !defined($firewall_enabled)) {
+               notify($ERRORS{'WARNING'}, 0, "usernames or firewall_enabled 
argument was not specified");
+               return;
+       }
+       
+       $usernames = '' if !$usernames;
+       
+       if (!defined($firewall_enabled)) {
+               $firewall_enabled = 'unknown';
+       }
+       elsif ($firewall_enabled =~ /^(1|yes|on|enabled)$/i) {
+               $firewall_enabled = 'yes';
+       }
+       elsif ($firewall_enabled =~ /^(0|no|off|disabled)$/i) {
+               $firewall_enabled = 'no';
+       }
+       else {
+               $firewall_enabled = 'unknown';
+       }
+       
+       my $update_statement = <<EOF;
+INSERT INTO imagerevisioninfo
+(
+imagerevisionid,
+usernames,
+firewallenabled
+)
+VALUES
+(
+'$imagerevision_id',
+'$usernames',
+'$firewall_enabled'
+)
+ON DUPLICATE KEY UPDATE
+imagerevisionid=VALUES(imagerevisionid),
+usernames=VALUES(usernames),
+firewallenabled=VALUES(firewallenabled)
+EOF
+       
+       return database_execute($update_statement);
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_firewall_state
+
+ Parameters  : None
+ Returns     : If successful: string "ON" or "OFF"
+ Description : Determines if the Windows firewall is on or off.
+
+=cut
+
+sub get_firewall_state {
+       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();
+       my $system32_path        = $self->get_system32_path() || return;
+       
+       # Run netsh.exe to get the state of the current firewall profile
+       my $command = "$system32_path/netsh.exe firewall show state";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
retrieve firewall state");
+               return;
+       }
+       
+       # Get the lines containing 'Operational mode'
+       # Operational mode                  = Enable
+       my @mode_lines = grep(/Operational mode/i, @$output);
+       if (!@mode_lines) {
+               notify($ERRORS{'WARNING'}, 0, "unable to find 'Operational 
mode' line in output:\n" . join("\n", @$output));
+               return;
+       }
+       
+       # Loop through lines, if any contain "ON", return "ON"
+       for my $mode_line (@mode_lines) {
+               if ($mode_line =~ /on/i) {
+                       notify($ERRORS{'OK'}, 0, "firewall state: ON");
+                       return "ON";
+               }
+               elsif ($mode_line =~ /off/i) {
+                       notify($ERRORS{'OK'}, 0, "firewall state: OFF");
+                       return "OFF";
+               }
+               else {
+                       notify($ERRORS{'WARNING'}, 0, "firewall state line does 
not contain ON or OFF: '$mode_line'");
+               }
+       }
+       
+       # No lines were found containing "ON", return "OFF"
+       notify($ERRORS{'WARNING'}, 0, "unable to determine firewall state, 
output:\n" . join("\n", @$output));
+       return;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 1;
 __END__
 


Reply via email to