Author: arkurth Date: Wed Mar 18 17:23:20 2009 New Revision: 755664 URL: http://svn.apache.org/viewvc?rev=755664&view=rev Log: VCL-23 Changed xCAT.pm's load() sub to check if the OS module has implemented a post_load() sub instead of checking if the OS name was winvista. This has the same effect but is more generic.
Renamed xCAT.pm::capture_prepare() to capture(). This follows the design described on the list. A provisioning module's capture() sub will take care of everything instead of splitting it up into capture_prepare() and capture_monitor(). Added code to xCAT.pm::capture() to check if OS module has implemented a pre_capture() sub. If not, it checks if the OS module has implemented a capture_prepare() sub. All OS modules will eventually have pre_capture() subs but this will work through the transition. If OS::pre_capture() exists and has returned 1, it powers on the computer and proceeds to monitor the capture. Added subs to xCAT.pm: power_reset(), power_on(), power_off(), power_status(), wait_for_on(), wait_for_off(). Made changes to xCAT.pm::_rpower() so it can use either a passed argument or the data structure to determine the computer node name. It can also be called as an object method or class method. Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/xCAT.pm Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/xCAT.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/xCAT.pm?rev=755664&r1=755663&r2=755664&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/xCAT.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/xCAT.pm Wed Mar 18 17:23:20 2009 @@ -923,10 +923,10 @@ # Perform post load tasks # Windows specific routines - if ($image_os_name =~ /winvista/) { - # If Vista, don't perform post-load tasks here - # new.pm calls the Vista module's post_load() subroutine to perform the same tasks as below - notify($ERRORS{'OK'}, 0, "vista image, skipping OS preparation tasks in xCAT.pm, returning 1"); + if ($self->os->can('post_load')) { + # If post-load has been implemented by the OS module, don't perform these tasks here + # new.pm calls the Windows module's post_load() subroutine to perform the same tasks as below + notify($ERRORS{'OK'}, 0, "post_load() has been implemented by the OS module, skipping these tasks in xCAT.pm, returning 1"); return 1; } elsif ($image_os_name =~ /winxp|wxp|win2003|winvista/) { @@ -1203,15 +1203,15 @@ #///////////////////////////////////////////////////////////////////////////// -=head2 capture_prepare +=head2 capture Parameters : - Returns : 1 if sucessful, 0 if failed + Returns : 1 if successful, 0 if failed Description : =cut -sub capture_prepare { +sub capture { my $self = shift; if (ref($self) !~ /xCAT/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); @@ -1224,7 +1224,7 @@ my $computer_node_name = $self->data->get_computer_node_name(); # Print some preliminary information - notify($ERRORS{'OK'}, 0, "image=$image_name, computer=$computer_short_name"); + notify($ERRORS{'OK'}, 0, "xCAT capture beginning: image=$image_name, computer=$computer_short_name"); # Create currentimage.txt on the node containing information about the new image revision if (write_currentimage_txt($self->data)) { @@ -1233,43 +1233,68 @@ else { notify($ERRORS{'WARNING'}, 0, "unable to update currentimage.txt on $computer_short_name"); } - - # Edit the nodetype.tab file to set the node with the new image name - if ($self->_edit_nodetype($computer_node_name, $image_name)) { - notify($ERRORS{'OK'}, 0, "nodetype modified, node $computer_node_name, image name $image_name"); + + # Check if pre_capture() subroutine has been implemented by the OS module + if ($self->os->can("pre_capture")) { + # Call OS pre_capture() - it should perform all OS steps necessary to capture an image + # pre_capture() should shut down the computer when it is done + notify($ERRORS{'OK'}, 0, "calling OS module's pre_capture() subroutine"); + if (!$self->os->pre_capture({end_state => 'off'})) { + notify($ERRORS{'WARNING'}, 0, "OS module pre_capture() failed"); + return 0; + } + + # Get the power status, make sure computer is off + my $power_status = $self->power_status(); + notify($ERRORS{'DEBUG'}, 0, "retrieved power status: $power_status"); + if ($power_status eq 'off') { + notify($ERRORS{'OK'}, 0, "verified $computer_node_name power is off"); + } + elsif ($power_status eq 'on') { + notify($ERRORS{'WARNING'}, 0, "$computer_node_name power is still on, turning computer off"); + + # Attempt to power off computer + if ($self->power_off()) { + notify($ERRORS{'OK'}, 0, "$computer_node_name was powered off"); + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to power off $computer_node_name"); + return 0; + } + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to determine power status of $computer_node_name"); + return 0; + } + } + elsif ($self->os->can("capture_prepare")) { + notify($ERRORS{'OK'}, 0, "calling OS module's capture_prepare() subroutine"); + if (!$self->os->capture_prepare()) { + notify($ERRORS{'WARNING'}, 0, "OS module capture_prepare() failed"); + $self->image_creation_failed(); + } } else { - notify($ERRORS{'CRITICAL'}, 0, "could not edit nodetype, node $computer_node_name, image name $image_name"); - return 0; + notify($ERRORS{'WARNING'}, 0, "OS module does not have either a pre_capture() or capture_prepare() subroutine"); + $self->image_creation_failed(); } - # Get the image repository path - my $image_repository_path = $self->_get_image_repository_path(); - if (!$image_repository_path) { - notify($ERRORS{'CRITICAL'}, 0, "xCAT image repository information could not be determined"); - return 0; - } - # Get the image template repository path - my $tmpl_repository_path = $self->_get_image_template_path(); - if (!$tmpl_repository_path) { - notify($ERRORS{'CRITICAL'}, 0, "xCAT template repository information could not be determined"); - return 0; + # Create the tmpl file for the image + if ($self->_create_template()) { + notify($ERRORS{'OK'}, 0, "created .tmpl file for $image_name"); } - - # Get the image template repository path - my $basetmpl = $self->_get_base_template_filename(); - if (!$basetmpl) { - notify($ERRORS{'WARNING'}, 0, "xCAT template repository information could not be determined"); + else { + notify($ERRORS{'WARNING'}, 0, "failed to create .tmpl file for $image_name"); return 0; } - - # Create the tmpl file for the image - if ($self->_create_template()) { - notify($ERRORS{'OK'}, 0, "created template file for $image_name"); + + # Edit the nodetype.tab file to set the node with the new image name + if ($self->_edit_nodetype($computer_node_name, $image_name)) { + notify($ERRORS{'OK'}, 0, "nodetype modified, node $computer_node_name, image name $image_name"); } else { - notify($ERRORS{'WARNING'}, 0, "failed to create template file for $image_name"); + notify($ERRORS{'WARNING'}, 0, "could not edit nodetype, node $computer_node_name, image name $image_name"); return 0; } @@ -1281,8 +1306,44 @@ notify($ERRORS{'WARNING'}, 0, "failed to set $computer_node_name to capture image on next reboot"); return 0; } + + + # Check if pre_capture() subroutine has been implemented by the OS module + # If so, all that needs to happen is for the computer to be powered on + if ($self->os->can("pre_capture")) { + # Turn the computer on + if ($self->power_on()) { + notify($ERRORS{'OK'}, 0, "$computer_node_name was powered on"); + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to turn computer on before monitoring image capture"); + return 0; + } + } + # If capture_start() is implemented, call it, it will initiate a reboot + elsif ($self->os->can("capture_start")) { + notify($ERRORS{'OK'}, 0, "calling OS module's capture_start() subroutine"); + if (!$self->os->capture_start()) { + notify($ERRORS{'WARNING'}, 0, "OS module capture_start() failed"); + $self->image_creation_failed(); + } + } + else { + notify($ERRORS{'WARNING'}, 0, "OS module does not have either a pre_capture() or capture_start() subroutine"); + $self->image_creation_failed(); + } + + + # Monitor the image capture + if ($self->capture_monitor()) { + notify($ERRORS{'OK'}, 0, "image capture monitoring is complete"); + } + else { + notify($ERRORS{'WARNING'}, 0, "problem occurred while monitoring image capture"); + return 0; + } - notify($ERRORS{'OK'}, 0, "returning 1"); + notify($ERRORS{'OK'}, 0, "image was successfully captured, returning 1"); return 1; } ## end sub capture_prepare @@ -1806,6 +1867,357 @@ #///////////////////////////////////////////////////////////////////////////// +=head2 power_reset + + Parameters : $computer_node_name (optional) + Returns : + Description : + +=cut + +sub power_reset { + my $argument_1 = shift; + my $argument_2 = shift; + + my $computer_node_name; + + # Check if subroutine was called as an object method + if (ref($argument_1) =~ /xcat/i) { + my $self = $argument_1; + + $computer_node_name = $argument_2; + + # Check if computer argument was specified + # If not, use computer node name in the data object + if (!$computer_node_name) { + $computer_node_name = $self->data->get_computer_node_name(); + } + } + else { + # Subroutine was not called as an object method, 2 arguments must be specified + $computer_node_name = $argument_1; + } + + # Check if computer was determined + if (!$computer_node_name) { + notify($ERRORS{'WARNING'}, 0, "computer could not be determined from arguments"); + return; + } + + # Turn computer off + my $off_attempts = 0; + while (!power_off($computer_node_name)) { + $off_attempts++; + + if ($off_attempts == 3) { + notify($ERRORS{'WARNING'}, 0, "failed to turn $computer_node_name off, rpower status not is off after 3 attempts"); + return; + } + + sleep 2; + } + + # Turn computer on + my $on_attempts = 0; + while (!power_on($computer_node_name)) { + $on_attempts++; + + if ($on_attempts == 3) { + notify($ERRORS{'WARNING'}, 0, "failed to turn $computer_node_name on, rpower status not is on after 3 attempts"); + return; + } + + sleep 2; + } + + notify($ERRORS{'OK'}, 0, "successfully reset power on $computer_node_name"); + return 1; +} ## end sub _nodeset_option + +#///////////////////////////////////////////////////////////////////////////// + +=head2 power_on + + Parameters : $computer_node_name (optional) + Returns : + Description : + +=cut + +sub power_on { + my $argument_1 = shift; + my $argument_2 = shift; + + my $computer_node_name; + + # Check if subroutine was called as an object method + if (ref($argument_1) =~ /xcat/i) { + my $self = $argument_1; + + $computer_node_name = $argument_2; + + # Check if computer argument was specified + # If not, use computer node name in the data object + if (!$computer_node_name) { + $computer_node_name = $self->data->get_computer_node_name(); + } + } + else { + # Subroutine was not called as an object method, 2 arguments must be specified + $computer_node_name = $argument_1; + } + + # Check if computer was determined + if (!$computer_node_name) { + notify($ERRORS{'WARNING'}, 0, "computer could not be determined from arguments"); + return; + } + + # Turn computer on + my $on_attempts = 0; + my $power_status = 'unknown'; + while ($power_status !~ /on/) { + $on_attempts++; + + if ($on_attempts == 3) { + notify($ERRORS{'WARNING'}, 0, "failed to turn $computer_node_name on, rpower status not is on after 3 attempts"); + return; + } + + _rpower($computer_node_name, 'on'); + sleep 2; + + $power_status = power_status($computer_node_name); + } + + notify($ERRORS{'OK'}, 0, "successfully powered on $computer_node_name"); + return 1; +} ## end sub _nodeset_option + +#///////////////////////////////////////////////////////////////////////////// + +=head2 power_off + + Parameters : $computer_node_name (optional) + Returns : + Description : + +=cut + +sub power_off { + my $argument_1 = shift; + my $argument_2 = shift; + + my $computer_node_name; + + # Check if subroutine was called as an object method + if (ref($argument_1) =~ /xcat/i) { + my $self = $argument_1; + + $computer_node_name = $argument_2; + + # Check if computer argument was specified + # If not, use computer node name in the data object + if (!$computer_node_name) { + $computer_node_name = $self->data->get_computer_node_name(); + } + } + else { + # Subroutine was not called as an object method, 2 arguments must be specified + $computer_node_name = $argument_1; + } + + # Check if computer was determined + if (!$computer_node_name) { + notify($ERRORS{'WARNING'}, 0, "computer could not be determined from arguments"); + return; + } + + # Turn computer off + my $power_status = 'unknown'; + my $off_attempts = 0; + while ($power_status !~ /off/) { + $off_attempts++; + + if ($off_attempts == 3) { + notify($ERRORS{'WARNING'}, 0, "failed to turn $computer_node_name off, rpower status not is off after 3 attempts"); + return; + } + + _rpower($computer_node_name, 'off'); + sleep 2; + + $power_status = power_status($computer_node_name); + } + + notify($ERRORS{'OK'}, 0, "successfully powered off $computer_node_name"); + return 1; +} ## end sub _nodeset_option + +#///////////////////////////////////////////////////////////////////////////// + +=head2 power_status + + Parameters : $computer_node_name (optional) + Returns : + Description : + +=cut + +sub power_status { + my $argument_1 = shift; + my $argument_2 = shift; + + my $computer_node_name; + + # Check if subroutine was called as an object method + if (ref($argument_1) =~ /xcat/i) { + my $self = $argument_1; + + $computer_node_name = $argument_2; + + # Check if computer argument was specified + # If not, use computer node name in the data object + if (!$computer_node_name) { + $computer_node_name = $self->data->get_computer_node_name(); + } + } + else { + # Subroutine was not called as an object method, 2 arguments must be specified + $computer_node_name = $argument_1; + } + + # Check if computer was determined + if (!$computer_node_name) { + notify($ERRORS{'WARNING'}, 0, "computer could not be determined from arguments"); + return; + } + + # Call rpower to determine power status + my $rpower_stat = _rpower($computer_node_name, 'stat'); + notify($ERRORS{'DEBUG'}, 0, "retrieved power status of $computer_node_name: $rpower_stat"); + + if (!$rpower_stat) { + notify($ERRORS{'WARNING'}, 0, "failed to determine power status, rpower subroutine returned $rpower_stat"); + return; + } + elsif ($rpower_stat =~ /^(on|off)$/i) { + return lc($1); + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to determine power status, unexpected output returned from rpower: $rpower_stat"); + return; + } +} ## end sub _nodeset_option + +#///////////////////////////////////////////////////////////////////////////// + +=head2 wait_for_on + + Parameters : Maximum number of minutes to wait (optional) + Returns : 1 if computer is on, 0 otherwise + Description : + +=cut + +sub wait_for_on { + my $self = shift; + if (ref($self) !~ /xcat/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + my $computer_node_name = $self->data->get_computer_node_name(); + + # Attempt to get the total number of minutes to wait from the arguments + my $total_wait_minutes = shift; + if (!defined($total_wait_minutes) || $total_wait_minutes !~ /^\d+$/) { + $total_wait_minutes = 5; + } + + # Looping configuration variables + # Seconds to wait in between loop attempts + my $attempt_delay = 15; + # Total loop attempts made + # Add 1 to the number of attempts because if you're waiting for x intervals, you check x+1 times including at 0 + my $attempts = ($total_wait_minutes * 4) + 1; + + notify($ERRORS{'OK'}, 0, "waiting for $computer_node_name to turn on, maximum of $total_wait_minutes minutes"); + + # Loop until computer is on + for (my $attempt = 1; $attempt <= $attempts; $attempt++) { + if ($attempt > 1) { + notify($ERRORS{'OK'}, 0, "attempt " . ($attempt-1) . "/" . ($attempts-1) . ": $computer_node_name is not on, sleeping for $attempt_delay seconds"); + sleep $attempt_delay; + } + + if ($self->power_status() =~ /on/i) { + notify($ERRORS{'OK'}, 0, "$computer_node_name is on"); + return 1; + } + } + + # Calculate how long this waited + my $total_wait = ($attempts * $attempt_delay); + notify($ERRORS{'WARNING'}, 0, "$computer_node_name is NOT on after waiting for $total_wait seconds"); + return 0; +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 wait_for_off + + Parameters : Maximum number of minutes to wait (optional) + Returns : 1 if computer is off, 0 otherwise + Description : + +=cut + +sub wait_for_off { + my $self = shift; + if (ref($self) !~ /xcat/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + my $computer_node_name = $self->data->get_computer_node_name(); + + # Attempt to get the total number of minutes to wait from the arguments + my $total_wait_minutes = shift; + if (!defined($total_wait_minutes) || $total_wait_minutes !~ /^\d+$/) { + $total_wait_minutes = 5; + } + + # Looping configuration variables + # Seconds to wait in between loop attempts + my $attempt_delay = 15; + # Total loop attempts made + # Add 1 to the number of attempts because if you're waiting for x intervals, you check x+1 times including at 0 + my $attempts = ($total_wait_minutes * 4) + 1; + + notify($ERRORS{'OK'}, 0, "waiting for $computer_node_name to turn off, maximum of $total_wait_minutes minutes"); + + # Loop until computer is off + for (my $attempt = 1; $attempt <= $attempts; $attempt++) { + if ($attempt > 1) { + notify($ERRORS{'OK'}, 0, "attempt " . ($attempt-1) . "/" . ($attempts-1) . ": $computer_node_name is not off, sleeping for $attempt_delay seconds"); + sleep $attempt_delay; + } + + if ($self->power_status() =~ /off/i) { + notify($ERRORS{'OK'}, 0, "$computer_node_name is off"); + return 1; + } + } + + # Calculate how long this waited + my $total_wait = ($attempts * $attempt_delay); + notify($ERRORS{'WARNING'}, 0, "$computer_node_name is NOT off after waiting for $total_wait seconds"); + return 0; +} + +#///////////////////////////////////////////////////////////////////////////// + =head2 makesshgkh Parameters : imagename @@ -1852,41 +2264,108 @@ =cut sub _rpower { - my ($node, $option) = @_; - - #make sure node and option are defined - notify($ERRORS{'WARNING'}, 0, "_rpower: node is not defined") - if (!(defined($node))); - notify($ERRORS{'WARNING'}, 0, "_rpower: option is not defined setting to cycle") - if (!(defined($option))); - return 0 if (!(defined($node))); - - $option = "cycle" if (!(defined($option))); - - my $l; - my @file; - RPOWER: - if (open(RPOWER, "$XCAT_ROOT/bin/rpower $node $option |")) { - @file = <RPOWER>; - close(RPOWER); - foreach $l (@file) { - if ($l =~ /not in bay/) { - - # not in bay problem - if (_fix_rpower($node)) { - goto RPOWER; #try again - } - } - if ($l =~ /$node:\s+(on|off)/) { - return $1; - } - } ## end foreach $l (@file) - return 0; - } ## end if (open(RPOWER, "$XCAT_ROOT/bin/rpower $node $option |"... + my $argument_1 = shift; + my $argument_2 = shift; + my $argument_3 = shift; + + my $computer_node_name; + my $mode; + + # Check if subroutine was called as an object method + if (ref($argument_1) =~ /xcat/i) { + my $self = $argument_1; + + # Check if 1 or 2 arguments were specified + if ($argument_3) { + $computer_node_name = $argument_2; + $mode = $argument_3; + } + else { + $computer_node_name = $self->data->get_computer_node_name(); + $mode = $argument_2; + } + } else { - notify($ERRORS{'WARNING'}, 0, "_rpower: could not run $XCAT_ROOT/bin/rpower $node $option $!"); - return 0; + # Subroutine was not called as an object method, 2 arguments must be specified + $computer_node_name = $argument_1; + $mode = $argument_2; } + + # Check the arguments + if (!$computer_node_name) { + notify($ERRORS{'WARNING'}, 0, "rpower was not executed, computer was not specified"); + return; + } + if (!$mode) { + notify($ERRORS{'WARNING'}, 0, "rpower mode was not specified, setting mode to cycle"); + $mode = 'cycle'; + } + if ($mode !~ /^on|off|stat|state|reset|boot|cycle$/i) { + notify($ERRORS{'WARNING'}, 0, "rpower was not executed, mode is not valid: $mode"); + return; + } + + # If one of the reset modes was specified, call power_reset() + # It attempts to turn off then on, and makes sure attempts were successful + if ($mode =~ /^reset|boot|cycle$/i) { + return power_reset($computer_node_name); + } + + notify($ERRORS{'DEBUG'}, 0, "attempting to execute rpower for computer: $computer_node_name, mode: $mode"); + + # Assemble the rpower command + my $command = "$XCAT_ROOT/bin/rpower $computer_node_name $mode"; + + # Run the command + my ($exit_status, $output) = run_command($command); + + # rpower options: + # on - Turn power on + # off - Turn power off + # stat | state - Return the current power state + # reset - Send a hardware reset + # boot - If off, then power on. If on, then hard reset. This option is recommended over cycle. + # cycle - Power off, then on + + # Typical output: + # Invalid node is specified (exit status = 0): + # [r...@managementnode]# rpower vclb2-8x stat + # invalid node, group, or range: vclb2-8x + # Successful off (exit status = 0): + # [r...@managementnode]# rpower vclb2-8 off + # vclb2-8: off + # Successful reset (exit status = 0): + # [r...@managementnode test]# rpower vclb2-8 reset + # vclb2-8: reset + # Successful stat (exit status = 0): + # [r...@managementnode test]# rpower vclb2-8 stat + # vclb2-8: on + # Successful cycle (exit status = 0): + # [r...@managementnode test]# rpower vclb2-8 cycle + # vclb2-8: off on + + foreach my $output_line (@{$output}) { + # Check for 'invalid node' + if ($output_line =~ /invalid node/) { + notify($ERRORS{'WARNING'}, 0, "rpower reported invalid node: @{$output}"); + return; + } + + # Check for known 'not in bay' problem + if ($output_line =~ /not in bay/) { + if (_fix_rpower($computer_node_name)) { + return _rpower($computer_node_name, $mode); + } + } + + # Check for successful output line + if ($output_line =~ /$computer_node_name:\s+(.*)/) { + return $1; + } + } + + notify($ERRORS{'WARNING'}, 0, "unexpected output returned from rpower: @{$output}"); + return 0; } ## end sub _rpower