Author: arkurth
Date: Thu Mar 29 21:05:08 2012
New Revision: 1307108
URL: http://svn.apache.org/viewvc?rev=1307108&view=rev
Log:
VCL-569
Updated OS.pm::update_public_ip_address to set the public IP address retrieved
from the OS in the Datastructure object. This returns the correct value to
future callers of get_computer_ip_address.
Updated new.pm::reserve_computer to call get_computer_ip_address after
update_public_ip_address is called. This causes the code to use the correct,
updated IP instead of an old value stored in the database.
VCL-564
Added OS.pm::get_tools_file_paths. This is used to determine which files under
the tools directory on the management node apply to the reservation image. Also
added OS.pm::get_file_checksum. This is used to determine whether or a file on
the management node is identical to a file on the computer being loaded. If
they differ, the file on the computer is replaced.
Added optional concatenate argument to OS.pm::create_text_file. By default the
text file is overwritten. This argument is used by the run_script subroutines
when the logfile is written to.
Updated Windows.pm::run_scripts to call OS.pm::get_tools_file_paths.
VCL-572
Added subroutines to Windows.pm: install_updates, get_installed_updates,
install_exe_update, and install_msu_update.
Moved call to get_imagemeta_postoption from the beginning of
Windows.pm::post_load to the actual check that determines if the computer needs
to be rebooted. This allows any of the subroutines prior to that point to set
the reboot flag.
VCL-565
Changed call in OS.pm::manage_server_access which was directly referencing
$ENV{management_node_info} to use $self->data.
Other
Removed call to utils.pm::setstaticaddress in OS.pm::update_public_ip_address.
This subroutine was removed.
Added check to make sure the interface name was retrieved in OS.pm:
get_private_network_configuration, get_public_network_configuration
Moved copy_file_to and find_files from Linux.pm to OS.pm since they can be used
by other OS's.
Updated Linux.pm::logoff_user to check for an "invalid user name" error in the
output.
Modified:
incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm
incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm
incubator/vcl/trunk/managementnode/lib/VCL/new.pm
incubator/vcl/trunk/managementnode/lib/VCL/utils.pm
Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm
URL:
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm?rev=1307108&r1=1307107&r2=1307108&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm Thu Mar 29 21:05:08
2012
@@ -840,8 +840,10 @@ sub update_public_ip_address {
return;
}
- # Update the computer table if the retrieved IP address does
not match what is in the database
+ # Update the Datastructure and computer table if the retrieved
IP address does not match what is in the database
if ($computer_ip_address ne $public_ip_address) {
+
$self->data->set_computer_ip_address($public_ip_address);
+
if (update_computer_address($computer_id,
$public_ip_address)) {
notify($ERRORS{'OK'}, 0, "updated dynamic
public IP address in computer table for $computer_node_name,
$public_ip_address");
insertloadlog($reservation_id, $computer_id,
"dynamicDHCPaddress", "updated dynamic public IP address in computer table for
$computer_node_name, $public_ip_address");
@@ -862,13 +864,10 @@ sub update_public_ip_address {
notify($ERRORS{'DEBUG'}, 0, "IP configuration is set to
$public_ip_configuration, attempting to set public IP address");
# Try to set the static public IP address using the OS module
- if ($self->can("set_static_public_address") &&
$self->set_static_public_address()) {
- notify($ERRORS{'DEBUG'}, 0, "set static public IP
address on $computer_node_name using OS module's set_static_public_address()
method");
- }
- else {
- # Unable to set the static address using the OS module,
try using utils.pm
- if (setstaticaddress($computer_node_name,
$image_os_name, $computer_ip_address, $image_os_type)) {
- notify($ERRORS{'DEBUG'}, 0, "set static public
IP address on $computer_node_name using utils.pm::setstaticaddress()");
+ if ($self->can("set_static_public_address")) {
+ if ($self->set_static_public_address()) {
+ notify($ERRORS{'DEBUG'}, 0, "set static public
IP address on $computer_node_name using OS module's set_static_public_address()
method");
+ insertloadlog($reservation_id, $computer_id,
"staticIPaddress", "set static public IP address on $computer_node_name");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to set
static public IP address on $computer_node_name");
@@ -876,7 +875,9 @@ sub update_public_ip_address {
return;
}
}
- insertloadlog($reservation_id, $computer_id, "staticIPaddress",
"set static public IP address on $computer_node_name");
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unable to set static
public IP address on $computer_node_name, " . ref($self) . " module does not
implement a set_static_public_address subroutine");
+ }
}
else {
@@ -1270,7 +1271,13 @@ sub get_private_network_configuration {
return;
}
- return
$self->get_network_configuration()->{$self->get_private_interface_name()};
+ my $private_interface_name = $self->get_private_interface_name();
+ if (!$private_interface_name) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve private
network configuration, private interface name could not be determined");
+ return;
+ }
+
+ return $self->get_network_configuration()->{$private_interface_name};
}
#/////////////////////////////////////////////////////////////////////////////
@@ -1290,7 +1297,13 @@ sub get_public_network_configuration {
return;
}
- return
$self->get_network_configuration()->{$self->get_public_interface_name()};
+ my $public_interface_name = $self->get_public_interface_name();
+ if (!$public_interface_name) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve public
network configuration, public interface name could not be determined");
+ return;
+ }
+
+ return $self->get_network_configuration()->{$public_interface_name};
}
#/////////////////////////////////////////////////////////////////////////////
@@ -1670,7 +1683,7 @@ sub create_text_file {
return;
}
- my ($file_path, $file_contents_string) = @_;
+ my ($file_path, $file_contents_string, $concatenate) = @_;
if (!$file_contents_string) {
notify($ERRORS{'WARNING'}, 0, "file contents argument was not
supplied");
return;
@@ -1682,7 +1695,7 @@ sub create_text_file {
# Attempt to create the parent directory if it does not exist
if ($self->can('create_directory')) {
my $parent_directory_path = parent_directory_path($file_path);
- $self->create_directory($parent_directory_path);
+ $self->create_directory($parent_directory_path) if
$parent_directory_path;
}
# Remove Windows-style carriage returns if the image OS isn't Windows
@@ -1707,14 +1720,21 @@ sub create_text_file {
# Create a command to echo the hex string to the file
# Use -e to enable interpretation of backslash escapes
- my $command .= "echo -n -e \"$hex_string\" > $file_path";
+ my $command .= "echo -n -e \"$hex_string\"";
+ if ($concatenate) {
+ $command .= " >> \"$file_path\"";
+ }
+ else {
+ $command .= " > \"$file_path\"";
+ }
+
my ($exit_status, $output) = $self->execute($command);
if (!defined($output)) {
notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command to
create file on $computer_node_name: $file_path");
return;
}
elsif ($exit_status != 0 || grep(/^\w+:/i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to execute command to
create a file on $computer_node_name: $file_path, exit status: $exit_status,
output:\n" . join("\n", @$output));
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to
create a file on $computer_node_name:\ncommand: '$command', exit status:
$exit_status, output:\n" . join("\n", @$output));
return;
}
else {
@@ -1896,6 +1916,7 @@ sub execute_new {
if ($attempt > 0) {
$attempt_string = "attempt $attempt/$max_attempts: ";
$ssh->close() if $ssh;
+ delete $ENV{net_ssh_expect}{$computer_name};
notify($ERRORS{'DEBUG'}, 0, $attempt_string . "sleeping
for $attempt_delay seconds before making next attempt");
sleep $attempt_delay;
@@ -1976,7 +1997,8 @@ sub execute_new {
delete $ENV{net_ssh_expect}{$computer_name};
}
- notify($ERRORS{'DEBUG'}, 0, $attempt_string . "executing
command on $computer_name: '$command', timeout: $timeout_seconds seconds") if
($display_output);
+ (my $command_formatted = $command) =~ s/\s+(;|&|&&)\s+/\n$1 /g;
+ notify($ERRORS{'DEBUG'}, 0, $attempt_string . "executing
command on $computer_name (timeout: $timeout_seconds
seconds):\n$command_formatted") if ($display_output);
$ssh->send($command . ' 2>&1 ; echo exitstatus:$?');
my $ssh_wait_status;
@@ -2002,7 +2024,7 @@ sub execute_new {
$output =~ s/(^\s+)|(\s+$)//g;
my $exit_status_string = $ssh->match() || '';
- my ($exit_status) = $exit_status_string =~ /(\d)+/;
+ my ($exit_status) = $exit_status_string =~ /(\d+)/;
if (!$exit_status_string || !defined($exit_status)) {
my $all_output = $ssh->read_all() || '';
notify($ERRORS{'WARNING'}, 0, $attempt_string . "failed
to determine exit status from string: '$exit_status_string',
output:\n$all_output");
@@ -2149,11 +2171,7 @@ sub manage_server_access {
# Collect users in reservationaccounts table
my %res_accounts = get_reservation_accounts($reservation_id);
- notify($ERRORS{'DEBUG'}, 0, "res_accounts:".
format_data(%res_accounts));
- my $not_standalone_list = "";
- if(defined($ENV{management_node_info}{NOT_STANDALONE}) &&
$ENV{management_node_info}{NOT_STANDALONE}){
- $not_standalone_list =
$ENV{management_node_info}{NOT_STANDALONE};
- }
+ my $not_standalone_list =
$self->data->get_management_node_not_standalone();
#Add users
foreach my $userid (sort keys %user_hash) {
@@ -2204,7 +2222,7 @@ sub manage_server_access {
}
}
-
+
#Remove anyone listed in reservationaccounts list that is not in
user_hash
foreach my $res_userid (sort keys %res_accounts) {
notify($ERRORS{'OK'}, 0, "res_userid= $res_userid username=
$res_accounts{$res_userid}{username}");
@@ -2228,7 +2246,7 @@ sub manage_server_access {
$allow_list .= " $res_accounts{$res_userid}{username}";
}
notify($ERRORS{'OK'}, 0, "allow_list= $allow_list");
-
+
$self->data->set_server_allow_users($allow_list);
if ($self->can("update_server_access") ) {
@@ -2476,6 +2494,322 @@ sub is_user_connected {
return $ret_val;
}
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 copy_file_to
+
+ Parameters : $source_path, $destination_path
+ Returns : boolean
+ Description : Copies file(s) from the management node to the computer.
+ Wildcards are allowed in the source path.
+
+=cut
+
+sub copy_file_to {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ # Get the source and destination arguments
+ my ($source_path, $destination_path) = @_;
+ if (!$source_path || !$destination_path) {
+ notify($ERRORS{'WARNING'}, 0, "source and destination path
arguments were not specified");
+ return;
+ }
+
+ # Get the computer short and hostname
+ my $computer_node_name = $self->data->get_computer_node_name() ||
return;
+
+ # Get the destination parent directory path and create the directory
+ my $destination_directory_path =
parent_directory_path($destination_path);
+ if (!$destination_directory_path) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine destination
parent directory path: $destination_path");
+ return;
+ }
+ $self->create_directory($destination_directory_path) || return;
+
+ # Get the identity keys used by the management node
+ my $management_node_keys = $self->data->get_management_node_keys() ||
'';
+
+ # Run the SCP command
+ if (run_scp_command($source_path,
"$computer_node_name:\"$destination_path\"", $management_node_keys)) {
+ notify($ERRORS{'DEBUG'}, 0, "copied file from management node
to $computer_node_name: '$source_path' -->
$computer_node_name:'$destination_path'");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to copy file from
management node to $computer_node_name: '$source_path' -->
$computer_node_name:'$destination_path'");
+ return;
+ }
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 find_files
+
+ Parameters : $base_directory_path, $file_pattern, $search_type (optional)
+ Returns : array
+ Description : Finds files under the base directory and any subdirectories path
+ matching the file pattern. The search is not case sensitive. An
+ array is returned containing matching file paths.
+
+ A third argument can be supplied specifying the search type.
+
+ If 'regex' is supplied, the $file_pattern argument is assumed to
+ be a regular expression.
+
+=cut
+
+sub find_files {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ # Get the arguments
+ my ($base_directory_path, $file_pattern, $search_type) = @_;
+ if (!$base_directory_path || !$file_pattern) {
+ notify($ERRORS{'WARNING'}, 0, "base directory path and file
pattern arguments were not specified");
+ return;
+ }
+
+ # Normalize the arguments
+ $base_directory_path = normalize_file_path($base_directory_path);
+ $file_pattern = normalize_file_path($file_pattern);
+
+ # The base directory path must have a trailing slash or find won't work
+ $base_directory_path .= '/';
+
+ # Get the computer short and hostname
+ my $computer_node_name = $self->data->get_computer_node_name() ||
return;
+
+ # Run the find command
+ my $command = "/usr/bin/find \"$base_directory_path\"";
+ if ($search_type) {
+ if ($search_type =~ /regex/i) {
+ $command .= " -type f -iregex \"$file_pattern\"";
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "invalid search type
argument was specified: '$search_type'");
+ return;
+ }
+ }
+ else {
+ $command .= " -type f -iname \"$file_pattern\"";
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "attempting to find files on
$computer_node_name, base directory path: '$base_directory_path', pattern:
$file_pattern, command: $command");
+
+ my ($exit_status, $output) = $self->execute($command, 0);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to run command to find
files on $computer_node_name, base directory path: '$base_directory_path',
pattern: $file_pattern, command:\n$command");
+ return;
+ }
+ elsif (grep(/^find:.*No such file or directory/i, @$output)) {
+ notify($ERRORS{'DEBUG'}, 0, "base directory does not exist on
$computer_node_name: $base_directory_path");
+ @$output = ();
+ }
+ elsif (grep(/^find: /i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "error occurred attempting to
find files on $computer_node_name\nbase directory path:
$base_directory_path\npattern: $file_pattern\ncommand: $command\noutput:\n" .
join("\n", @$output));
+ return;
+ }
+
+ my @files;
+ LINE: for my $line (@$output) {
+ push @files, $line;
+ }
+
+ my $file_count = scalar(@files);
+
+ notify($ERRORS{'DEBUG'}, 0, "files found: $file_count, base directory:
'$base_directory_path', pattern: '$file_pattern'");
+ return @files;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_file_checksum
+
+ Parameters : $file_path
+ Returns : integer
+ Description : Runs chsum on the file specified by the argument and returns the
+ checksum of the file.
+
+=cut
+
+sub get_file_checksum {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ my $file_path = shift;
+ if (!$file_path) {
+ notify($ERRORS{'WARNING'}, 0, "file path argument was not
supplied");
+ return;
+ }
+
+ # Escape $ characters
+ $file_path =~ s/([\$])/\\$1/g;
+
+ my $command = "cksum \"$file_path\"";
+ my ($exit_status, $output) = $self->execute($command);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to
determine checksum of file: $file_path");
+ return;
+ }
+ elsif (my ($checksum_line) = grep(/^\d+\s+/, @$output)) {
+ my ($checksum) = $checksum_line =~ /^(\d+)/;
+ #notify($ERRORS{'DEBUG'}, 0, "determined checksum of file
'$file_path': $checksum");
+ return $checksum;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unexpected output in cksum
output, command: '$command', output:\n" . join("\n", @$output));
+ return;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_tools_file_paths
+
+ Parameters : $pattern
+ Returns : boolean
+ Description : Scans the tools directory on the management node for any files
+ which are intended for the OS of the reservation image. The OS
+ name and architecture are considered. A list of file paths on
the
+ reservation computer is returned.
+
+ Files intended for the reservation image are synchronized from
+ the management node. Any files which don't exist on the
+ reservation computer are copied. Files which exist on the
+ computer but are different than the file on the management node
+ are replaced. Files which exist on the computer but not on the
+ management node are ignored.
+
+ A pattern argument can be supplied to limit the results. For
+ example, to only return driver files supply '/Drivers/' as the
+ argument. To only return script files intended to for the
+ post_load stage, supply '/Scripts/post_load' as the argument.
+
+ The list of files returned is sorted by the names of the files,
+ regardless of the directory where they reside. Files can be
named
+ beginning with a number. This list returned is sorted
numerically
+ from the lowest number to the highest:
+ -1.cmd
+ -50.cmd
+ -100.cmd
+
+ File names which do not begin with a number are sorted
+ alphabetically and listed after any files beginning with a
+ number:
+ -1.cmd
+ -50.cmd
+ -100.cmd
+ -Blah.cmd
+ -foo.cmd
+
+=cut
+
+sub get_tools_file_paths {
+ 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;
+ }
+
+ my $pattern = shift || '.*';
+
+ 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';
+ my $other_architecture = $self->is_64_bit() ? 'x86' : 'x86_64';
+
+ notify($ERRORS{'DEBUG'}, 0, "attempting for find tools files:\npattern:
$pattern\narchitecture: $architecture\nother architecture:
$other_architecture");
+
+ # Find files already on the computer
+ my $computer_directory_path = "$NODE_CONFIGURATION_DIRECTORY";
+ my @existing_computer_file_array =
$self->find_files($computer_directory_path, '*');
+ my %existing_computer_files = map { $_ => 1 }
@existing_computer_file_array;
+
+ my %computer_tools_file_paths;
+
+ # 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";
+ 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 (@mn_directory_files) {
+
+ # Ignore files not matching the pattern argument,
Subversion files, and files intended for another architecture
+ if ($pattern && $mn_file_path !~ /$pattern/i) {
+ #notify($ERRORS{'DEBUG'}, 0, "ignoring file, it
does not match pattern '$pattern': $mn_file_path");
+ next MN_FILE;
+ }
+ elsif ($mn_file_path =~ /\/\.svn\//i) {
+ notify($ERRORS{'DEBUG'}, 0, "ignoring
Subversion file: $mn_file_path");
+ next MN_FILE;
+ }
+ elsif ($mn_file_path =~ /\/$other_architecture\//) {
+ notify($ERRORS{'DEBUG'}, 0, "ignoring file
intended for different computer architecture: $mn_file_path");
+ next MN_FILE;
+ }
+
+ my ($relative_file_path) = $mn_file_path =~
/$mn_directory_path\/(.+)/;
+ my $computer_file_path =
"$computer_directory_path/$relative_file_path";
+
+ # Add the computer file path to the list that will be
returned
+ $computer_tools_file_paths{$computer_file_path} = 1;
+
+ # Check if the file already exists on the computer
+ notify($ERRORS{'DEBUG'}, 0, "checking if file on
management node needs to be copied to $computer_node_name: $mn_file_path");
+ if ($existing_computer_files{$computer_file_path}) {
+
+ # Check if existing file on computer is
identical to file on managment node
+ # Retrieve the checksums
+ my $mn_file_checksum =
$self->mn_os->get_file_checksum($mn_file_path);
+ my $computer_file_checksum =
$self->get_file_checksum($computer_file_path);
+
+ # Check if the file already on the computer is
exactly the same as the one on the MN by comparing checksums
+ if ($mn_file_checksum &&
$computer_file_checksum && $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 || '<unknown>') . "\n" .
+ "computer file checksum: " .
($computer_file_checksum || '<unknown>')
+ );
+ }
+ }
+ 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");
+ return;
+ }
+ }
+ }
+
+ my @return_files = sort_by_file_name(keys %computer_tools_file_paths);
+ notify($ERRORS{'DEBUG'}, 0, "determined list of tools files intended
for $computer_node_name, pattern: $pattern, architecture: $architecture:\n" .
join("\n", @return_files));
+ return @return_files;
+}
+
#///////////////////////////////////////////////////////////////////////////
1;
Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
URL:
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm?rev=1307108&r1=1307107&r2=1307108&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Thu Mar 29
21:05:08 2012
@@ -732,35 +732,35 @@ sub logoff_user {
}
# Make sure the user login ID was passed
- my $user_login_id = shift;
- $user_login_id = $self->data->get_user_login_id() if (!$user_login_id);
+ my $user_login_id = shift || $self->data->get_user_login_id();
if (!$user_login_id) {
notify($ERRORS{'WARNING'}, 0, "user could not be determined");
return 0;
}
# Make sure the user login ID was passed
- my $computer_node_name = shift;
- $computer_node_name = $self->data->get_computer_node_name() if
(!$computer_node_name);
+ my $computer_node_name = shift || $self->data->get_computer_node_name();
if (!$computer_node_name) {
notify($ERRORS{'WARNING'}, 0, "computer node name could not be
determined");
return 0;
}
- #Make sure the identity key was passed
- my $image_identity = shift;
- $image_identity = $self->data->get_image_identity() if
(!$image_identity);
- if (!$image_identity) {
- notify($ERRORS{'WARNING'}, 0, "image identity keys could not be
determined");
- return 0;
- }
-
my $logoff_cmd = "pkill -KILL -u $user_login_id";
- if (run_ssh_command($computer_node_name, $image_identity, $logoff_cmd,
"root")) {
- notify($ERRORS{'DEBUG'}, 0, "logged off $user_login_id
from $computer_node_name");
+ my ($exit_status, $output) = $self->execute($logoff_cmd);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to log
off $user_login_id from $computer_node_name");
+ return;
+ }
+ elsif (grep(/invalid user name/i, @$output)) {
+ notify($ERRORS{'DEBUG'}, 0, "user $user_login_id does not exist
on $computer_node_name");
+ return 1;
+ }
+ elsif ($exit_status ne '0') {
+ notify($ERRORS{'WARNING'}, 0, "error occurred attempting to log
off $user_login_id from $computer_node_name, exit status: $exit_status,
output:\n" . join("\n", @$output));
+ return;
}
else {
- notify($ERRORS{'DEBUG'}, 0, "failed to log off $user_login_id
from $computer_node_name");
+ notify($ERRORS{'OK'}, 0, "logged off $user_login_id from
$computer_node_name");
}
return 1;
@@ -1773,57 +1773,6 @@ sub copy_file_from {
#/////////////////////////////////////////////////////////////////////////////
-=head2 copy_file_to
-
- Parameters : $source_path, $destination_path
- Returns : boolean
- Description : Copies file(s) from the management node to the Linux computer.
- Wildcards are allowed in the source path.
-
-=cut
-
-sub copy_file_to {
- my $self = shift;
- if (ref($self) !~ /VCL::Module/i) {
- notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
- return;
- }
-
- # Get the source and destination arguments
- my ($source_path, $destination_path) = @_;
- if (!$source_path || !$destination_path) {
- notify($ERRORS{'WARNING'}, 0, "source and destination path
arguments were not specified");
- return;
- }
-
- # Get the computer short and hostname
- my $computer_node_name = $self->data->get_computer_node_name() ||
return;
-
- # Get the destination parent directory path and create the directory
- my $destination_directory_path =
parent_directory_path($destination_path);
- if (!$destination_directory_path) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine destination
parent directory path: $destination_path");
- return;
- }
- $self->create_directory($destination_directory_path) || return;
-
- # Get the identity keys used by the management node
- my $management_node_keys = $self->data->get_management_node_keys() ||
'';
-
- # Run the SCP command
- if (run_scp_command($source_path,
"$computer_node_name:\"$destination_path\"", $management_node_keys)) {
- notify($ERRORS{'DEBUG'}, 0, "copied file from management node
to $computer_node_name: '$source_path' -->
$computer_node_name:'$destination_path'");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to copy file from
management node to $computer_node_name: '$source_path' -->
$computer_node_name:'$destination_path'");
- return;
- }
-
- return 1;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
=head2 copy_file
Parameters : $source_file_path, $destination_file_path
@@ -1901,7 +1850,7 @@ sub copy_file {
=head2 get_file_size
- Parameters : $file_path
+ Parameters : @file_paths
Returns : integer or array
Description : Determines the size of the file specified by the file path
argument in bytes. The file path argument may be a directory or
@@ -2027,77 +1976,6 @@ sub get_file_size {
#/////////////////////////////////////////////////////////////////////////////
-=head2 find_files
-
- Parameters : $base_directory_path, $file_pattern, $search_type (optional)
- Returns : array
- Description : Finds files under the base directory and any subdirectories path
- matching the file pattern. The search is not case sensitive. An
- array is returned containing matching file paths.
-
- A third argument can be supplied specifying the search type.
- 'regex' is currently the only supported type. If supplied, the
- $file_pattern argument is assumed to be a regular expression.
- Otherwise it is assumed to be a normal search pattern.
-
-=cut
-
-sub find_files {
- my $self = shift;
- if (ref($self) !~ /VCL::Module/i) {
- notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
- return;
- }
-
- # Get the arguments
- my ($base_directory_path, $file_pattern, $search_type) = @_;
- if (!$base_directory_path || !$file_pattern) {
- notify($ERRORS{'WARNING'}, 0, "base directory path and file
pattern arguments were not specified");
- return;
- }
-
- # Normalize the arguments
- $base_directory_path = normalize_file_path($base_directory_path);
- $file_pattern = normalize_file_path($file_pattern);
-
- # The base directory path must have a trailing slash or find won't work
- $base_directory_path .= '/';
-
- # Get the computer short and hostname
- my $computer_node_name = $self->data->get_computer_node_name() ||
return;
-
- # Run the find command
- my $command;
- if ($search_type && $search_type =~ /regex/i) {
- $command = "find \"$base_directory_path\" -iregex
\"$file_pattern\"";
- }
- else {
- $command = "find \"$base_directory_path\" -iname
\"$file_pattern\"";
- }
- notify($ERRORS{'DEBUG'}, 0, "attempting to find files on
$computer_node_name, base directory path: '$base_directory_path', pattern:
$file_pattern, command: $command");
-
- my ($exit_status, $output) = $self->execute($command);
- if (!defined($output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run command to find
files on $computer_node_name, base directory path: '$base_directory_path',
pattern: $file_pattern, command:\n$command");
- return;
- }
- elsif (grep(/^find:.*No such file or directory/i, @$output)) {
- notify($ERRORS{'DEBUG'}, 0, "base directory does not exist on
$computer_node_name: $base_directory_path");
- @$output = ();
- }
- elsif (grep(/^find: /i, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "error occurred attempting to
find files on $computer_node_name\nbase directory path:
$base_directory_path\npattern: $file_pattern\ncommand: $command\noutput:\n" .
join("\n", @$output));
- return;
- }
-
- # Return the file list
- my @file_paths = @$output;
- notify($ERRORS{'DEBUG'}, 0, "matching file count: " .
scalar(@file_paths));
- return sort @file_paths;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
=head2 set_file_permissions
Parameters : $file_path, $chmod_mode, $recursive (optional)
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=1307108&r1=1307107&r2=1307108&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Thu Mar 29
21:05:08 2012
@@ -616,7 +616,6 @@ sub post_load {
}
my $computer_node_name = $self->data->get_computer_node_name();
- my $imagemeta_postoption = $self->data->get_imagemeta_postoption();
notify($ERRORS{'OK'}, 0, "beginning Windows post-load tasks on
$computer_node_name");
@@ -814,7 +813,7 @@ sub post_load {
=cut
- if ($imagemeta_postoption =~ /reboot/i) {
+ if ($self->data->get_imagemeta_postoption() =~ /reboot/i) {
notify($ERRORS{'OK'}, 0, "imagemeta postoption reboot is set
for image, rebooting computer");
if (!$self->reboot()) {
notify($ERRORS{'WARNING'}, 0, "failed to reboot the
computer");
@@ -10903,10 +10902,19 @@ sub run_script {
(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';
+ my $node_configuration_directory =
$self->get_node_configuration_directory();
+ my $node_log_directory = "$node_configuration_directory/Logs";
# Assemble the log file path
- my $log_file_path = $node_log_directory . "/$script_name.log";
+ my $log_file_path;
+
+ # If the script resides in the VCL node configuration directory, append
the intermediate directory paths to the logfile path
+ if ($script_directory_path =~
/$node_configuration_directory[\\\/](.+)/) {
+ $log_file_path = "$node_log_directory/$1$script_name.log";
+ }
+ else {
+ $log_file_path = "$node_log_directory/$script_name.log";
+ }
my $timestamp = makedatestring();
@@ -10914,7 +10922,7 @@ sub run_script {
my $command = "cmd.exe /c \"$script_path_escaped & exit %ERRORLEVEL%\"";
# Execute the command
- notify($ERRORS{'DEBUG'}, 0, "executing command: '$command'");
+ notify($ERRORS{'DEBUG'}, 0, "executing script on
$computer_node_name:\nscript path: $script_path\nlog file path:
$log_file_path");
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'");
@@ -10984,120 +10992,316 @@ sub run_script {
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");
+ if (ref($self) !~ /VCL::Module/) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
return;
}
# Get the stage argument
my $stage = shift;
if (!$stage) {
- notify($ERRORS{'WANING'}, 0, "unable to run scripts, stage
argument was not supplied");
+ notify($ERRORS{'WARNING'}, 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");
+ notify($ERRORS{'WARNING'}, 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");
+ my @computer_tools_files =
$self->get_tools_file_paths("/Scripts/$stage/");
+
+ # Loop through all tools files on the computer
+ for my $computer_tools_file_path (@computer_tools_files) {
+ if ($computer_tools_file_path !~ /\.(cmd|bat)$/i) {
+ notify($ERRORS{'DEBUG'}, 0, "file on
$computer_node_name not executed because extension is not .cmd or .bat:
$computer_tools_file_path");
+ next;
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "executing script on
$computer_node_name: $computer_tools_file_path");
+
+ $self->run_script($computer_tools_file_path);
+ }
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 install_updates
+
+ Parameters : none
+ Returns : boolean
+ Description : Installs Windows update files stored in under the tools
directory
+ on the management node. Update files
which exist on the
+ management node but not on the computer
are copied. Files which
+ are named the same but differ are
replaced.
+
+=cut
+
+sub install_updates {
+ 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 $architecture = $self->is_64_bit() ? 'x86_64' : 'x86';
+ my $computer_node_name = $self->data->get_computer_node_name();
+ my $system32_path = $self->get_system32_path() || return;
- # 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"
- );
- }
+ # Get the node configuration directory, make sure it exists, create if
necessary
+ my $node_configuration_directory =
$self->get_node_configuration_directory();
+
+ my @computer_tools_files = $self->get_tools_file_paths("/Updates/");
+
+ my $logfile_directory_path =
"$node_configuration_directory/Logs/Updates";
+ $self->create_directory($logfile_directory_path);
+
+ my %installed_updates = map { $_ => 1 } $self->get_installed_updates();
+
+ my @update_ids;
+
+ # Loop through all update files on the computer
+ for my $file_path (@computer_tools_files) {
+ my ($file_name, $directory_path, $file_extension) =
fileparse($file_path, qr/\.[^.]*/);
+
+ if ($file_path !~ /\.(msu|exe)$/i) {
+ notify($ERRORS{'DEBUG'}, 0, "file on
$computer_node_name not installed because file extension is not .exe or .msu:
$file_path");
+ next;
+ }
+
+ # Get the update ID (KBxxxxxx) from the file name
+ my ($update_id) = uc($file_name) =~ /(kb\d+)/i;
+ $update_id = $file_name if !$update_id;
+
+ # Check if the update is already installed based on the list
returned from the OS
+ if ($installed_updates{$update_id}) {
+ notify($ERRORS{'DEBUG'}, 0, "update $update_id is
already installed on $computer_node_name");
+ next;
+ }
+ else {
+ # Add ID to @update_ids array, this list will be
checked after all updates are installed to verify update is installed on
computer
+ push @update_ids, $update_id;
+ }
+
+ if ($file_path =~ /\.msu$/i) {
+ $self->install_msu_update($file_path);
+ }
+ elsif ($file_path =~ /\.exe$/i) {
+ $self->install_exe_update($file_path);
+ }
+ }
+
+ # If any updates were installed, verify they appear on the OS
+ if (@update_ids) {
+ # Retrieve the installed updated from the OS to check if the
update was installed
+ %installed_updates = map { $_ => 1 }
$self->get_installed_updates(1);
+ for my $update_id (@update_ids) {
+ if ($installed_updates{$update_id}) {
+ notify($ERRORS{'DEBUG'}, 0, "verified update
$update_id is installed on $computer_node_name");
}
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;
+ notify($ERRORS{'WARNING'}, 0, "update
$update_id does not appear in the list of updates installed on
$computer_node_name");
}
-
- # 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\/(.+)/;
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 install_exe_update
+
+ Parameters : $update_file_path
+ Returns : boolean
+ Description : Installs a Windows Update .exe update package.
+
+=cut
+
+sub install_exe_update {
+ 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 $file_path = shift;
+ if (!$file_path) {
+ notify($ERRORS{'WARNING'}, 0, "path to .msu update file was not
supplied");
+ return;
+ }
+
+ my $computer_node_name = $self->data->get_computer_node_name();
+ my $system32_path = $self->get_system32_path() || return;
+
+ my ($file_name, $directory_path, $file_extension) =
fileparse($file_path, qr/\.[^.]*/);
+
+ my ($update_id) = uc($file_name) =~ /(kb\d+)/i;
+ $update_id = $file_name if !$update_id;
+
+ # Assemble the log file path
+ # wusa.exe creates log files in the Event Log format - not plain text
+ my $node_configuration_directory =
$self->get_node_configuration_directory();
+ my $logfile_directory_path =
"$node_configuration_directory/Logs/Updates";
+ my $log_file_path = "$logfile_directory_path/$file_name.log";
+
+ # Delete old log files for the update being installed so log output can
be parsed without including old data
+ $self->delete_file("$logfile_directory_path/*$update_id*");
+
+ my $command;
+ $command .= "chmod -Rv 755 \"$file_path\" ; ";
+ $command .= "\"$file_path\" /quiet /norestart /log:\"$log_file_path\"";
+
+ notify($ERRORS{'DEBUG'}, 0, "installing update on
$computer_node_name\ncommand: $command");
+ my ($exit_status, $output) = $self->execute($command, 1, 180);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to
install update on $computer_node_name: $command");
+ return;
+ }
+ elsif ($exit_status eq 194) {
+ # Exit status 194 - installed but reboot required
+ notify($ERRORS{'DEBUG'}, 0, "installed update on
$computer_node_name, exit status $exit_status indicates a reboot is required");
+ $self->data->set_imagemeta_postoption('reboot');
+ return 1;
+ }
+ elsif ($exit_status eq 0) {
+ notify($ERRORS{'DEBUG'}, 0, "installed update on
$computer_node_name");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "command to install update on
$computer_node_name returned exit status: $exit_status\ncommand:
$command\noutput:\n" . join("\n", @$output));
+ }
+
+ # Check the log file to determine if a reboot is required, skip if exit
status was 194
+ my @log_file_lines = $self->get_file_contents($log_file_path);
+ for my $line (@log_file_lines) {
+ if ($line =~ /RebootNecessary = 1|reboot is required/i) {
+ $self->data->set_imagemeta_postoption('reboot');
+ }
+ }
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 install_msu_update
+
+ Parameters : $msu_file_path
+ Returns : boolean
+ Description : Installs a Windows Update Stand-alone Installer .msu update
+ package.
+=cut
+
+sub install_msu_update {
+ 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 $file_path = shift;
+ if (!$file_path) {
+ notify($ERRORS{'WARNING'}, 0, "path to .msu update file was not
supplied");
+ return;
+ }
+
+ my $computer_node_name = $self->data->get_computer_node_name();
+ my $system32_path = $self->get_system32_path() || return;
+
+ my ($file_name, $directory_path, $file_extension) =
fileparse($file_path, qr/\.[^.]*/);
+
+ my ($update_id) = uc($file_name) =~ /(kb\d+)/i;
+ $update_id = $file_name if !$update_id;
+
+ # Assemble the log file path
+ # wusa.exe creates log files in the Event Log format - not plain text
+ my $node_configuration_directory =
$self->get_node_configuration_directory();
+ my $logfile_directory_path =
"$node_configuration_directory/Logs/Updates";
+ my $event_log_file_path = "$logfile_directory_path/$file_name.evtx";
+ my $log_file_path = "$logfile_directory_path/$file_name.log";
+
+ # Delete old log files for the update being installed so log output can
be parsed without including old data
+ $self->delete_file("$logfile_directory_path/*$update_id*");
+
+ my $wusa_command;
+ $wusa_command .= "chmod -Rv 755 \"$file_path\" ; ";
+ $wusa_command .= "$system32_path/wusa.exe \"$file_path\" /quiet
/norestart /log:\"$event_log_file_path\"";
+
+ notify($ERRORS{'DEBUG'}, 0, "installing update on
$computer_node_name\ncommand: $wusa_command");
+ my ($wusa_exit_status, $wusa_output) = $self->execute($wusa_command, 1,
180);
+ if (!defined($wusa_output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to
install update on $computer_node_name: $wusa_command");
+ return;
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "executed command to install update
on $computer_node_name, exit status: $wusa_exit_status\ncommand:
$wusa_command\noutput:\n" . join("\n", @$wusa_output));
+ }
+
+ # Convert Event Log format log file to plain text
+ # Use the wevtutil.exe - the Windows Events Command Line Utility
+ my $wevtutil_command = "$system32_path/wevtutil.exe qe
\"$event_log_file_path\" /lf:true /f:XML /e:root > \"$log_file_path\"";
+
+ my ($wevtutil_exit_status, $wevtutil_output) =
$self->execute($wevtutil_command);
+ if (!defined($wevtutil_output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to
convert event log file to plain text on $computer_node_name:
$wevtutil_command");
+ return;
+ }
+ else {
+ #notify($ERRORS{'DEBUG'}, 0, "executed command to convert event
log file to plain text $computer_node_name, exit status:
$wevtutil_exit_status\ncommand: $wevtutil_command\noutput:\n" . join("\n",
@$wevtutil_output));
+ }
+
+ my @log_file_lines = $self->get_file_contents($log_file_path);
+ #notify($ERRORS{'DEBUG'}, 0, "log file contents from installation of
$file_path:\n" . join("\n", @log_file_lines));
+
+ my $log_xml_hash = xml_string_to_hash(@log_file_lines);
+ #notify($ERRORS{'DEBUG'}, 0, "XML hash:\n" .
format_data($log_xml_hash));
+
+ my @events = @{$log_xml_hash->{Event}};
+ for my $event (@events) {
+ my $event_record_id = $event->{System}[0]->{EventRecordID}[0];
+ my %event_data = map { $_->{Name} => $_->{content} }
@{$event->{EventData}[0]->{Data}};
+
+ #notify($ERRORS{'DEBUG'}, 0, "event $event_record_id:\n" .
format_data(\%event_data));
- # 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;
+ if (my $error_code = $event_data{ErrorCode}) {
+ my $error_string = $event_data{ErrorString} || '<none>';
+
+ if ($error_code eq '2359302') {
+ # Already installed but reboot is required
+ notify($ERRORS{'DEBUG'}, 0, "update $update_id
is already installed but a reboot is required:\n" . format_data(\%event_data));
+ $self->data->set_imagemeta_postoption('reboot');
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "error occurred
installing update $update_id:\n" . format_data(\%event_data));
+ }
}
- 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;
+ elsif (my $debug_message = $event_data{DebugMessage}) {
+ if ($debug_message =~ /IsRebootRequired: 1/i) {
+ # RebootIfRequested.01446: Reboot is not
scheduled. IsRunWizardStarted: 0, IsRebootRequired: 0, RestartMode: 1
+ notify($ERRORS{'DEBUG'}, 0, "installed update
$update_id, reboot is required:\n$debug_message");
+ $self->data->set_imagemeta_postoption('reboot');
+ }
+ elsif ($debug_message =~ /Update is already
installed/i) {
+ # InstallWorker.01051: Update is already
installed
+ notify($ERRORS{'DEBUG'}, 0, "update $update_id
is already installed:\n$debug_message");
+ }
+ elsif ($debug_message =~ /0X240006/i) {
+ # InstallWorker.01051: Update is already
installed
+ notify($ERRORS{'DEBUG'}, 0, "error 0X240006
indicates that update $update_id is already installed:\n$debug_message");
+ }
+ elsif ($debug_message =~ /0X80240017/i) {
+ notify($ERRORS{'WARNING'}, 0, "update is not
intended for OS installed on $computer_node_name:\n$debug_message");
+ return;
+ }
+ elsif ($debug_message !~ /((start|end) of search|Failed
to get message for error)/i) {
+ notify($ERRORS{'DEBUG'}, 0, "debug message for
installation of update $update_id:\n$debug_message");
+ }
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "event generated while
installing update $update_id:\n" . format_data(\%event_data));
}
-
- $self->run_script($computer_file_path);
}
return 1;
@@ -11105,6 +11309,53 @@ sub run_scripts {
#/////////////////////////////////////////////////////////////////////////////
+=head2 get_installed_updates
+
+ Parameters : $no_cache (optional)
+ Returns : array
+ Description : Retrieves the list of updates installed on the computer.
+=cut
+
+sub get_installed_updates {
+ 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 $no_cache = shift;
+
+ return $self->{update_ids} if (!$no_cache && $self->{update_ids});
+
+ my $computer_node_name = $self->data->get_computer_node_name();
+ my $system32_path = $self->get_system32_path() || return;
+
+ # wmic.exe will hang if it is called by itself. It has something to do
with TTY/PTY
+ # Piping the echo command seems to prevent it from hanging
+ my $command = "echo | $system32_path/Wbem/wmic.exe QFE LIST BRIEF";
+ notify($ERRORS{'DEBUG'}, 0, "retrieving list of installed updates on
$computer_node_name, command: $command");
+ my ($exit_status, $output) = $self->execute($command);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to
list updates installed on $computer_node_name: $command");
+ return;
+ }
+
+ # Add update IDs found to a hash and then convert it to an array to
eliminate duplicates
+ my %update_id_hash;
+ for my $line (@$output) {
+ # Parse the update ID from the line, may be in the form KB000000
+ my ($update_id) = $line =~ /(kb\d+)/i;
+ $update_id_hash{$update_id} = 1 if $update_id;
+ }
+
+ my @update_ids = sort keys %update_id_hash;
+ $self->{update_ids} = \@update_ids;
+ notify($ERRORS{'DEBUG'}, 0, "retrieved list updates installed on
$computer_node_name(" . scalar(@update_ids) . "): " . join(", ", @update_ids));
+ return @update_ids;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
=head2 format_text_file
Parameters : $file_path
Modified:
incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm
URL:
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm?rev=1307108&r1=1307107&r2=1307108&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm
(original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm
Thu Mar 29 21:05:08 2012
@@ -111,7 +111,7 @@ sub pre_capture {
}
notify($ERRORS{'OK'}, 0, "beginning Windows version 5 image capture
preparation tasks");
-
+
# Check if Sysprep should be used
if ($self->data->get_imagemeta_sysprep()) {
if (!$self->run_sysprep()) {
Modified: incubator/vcl/trunk/managementnode/lib/VCL/new.pm
URL:
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/new.pm?rev=1307108&r1=1307107&r2=1307108&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/new.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/new.pm Thu Mar 29 21:05:08 2012
@@ -934,6 +934,9 @@ sub reserve_computer {
$self->reservation_failed("failed to update public IP
address");
}
+ # Update the $computer_ip_address varible in case the IP
address was different than what was originally in the database
+ $computer_ip_address = $self->data->get_computer_ip_address();
+
insertloadlog($reservation_id, $computer_id, "info", "node
ready adding user account");
# Only generate new password if:
Modified: incubator/vcl/trunk/managementnode/lib/VCL/utils.pm
URL:
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1307108&r1=1307107&r2=1307108&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/utils.pm Thu Mar 29 21:05:08 2012
@@ -10890,7 +10890,7 @@ sub sort_by_file_name {
if (!defined($a) && !defined($b)) {
my @file_paths = @_;
if (scalar(@file_paths)) {
- notify($ERRORS{'DEBUG'}, 0, "not called by sort, \$a
and \$b are not defined, array argument was passed");
+ #notify($ERRORS{'DEBUG'}, 0, "not called by sort, \$a
and \$b are not defined, array argument was passed");
return sort sort_by_file_name @file_paths;
}
else {