Author: arkurth
Date: Wed Apr 22 18:41:02 2015
New Revision: 1675454

URL: http://svn.apache.org/r1675454
Log:
VCL-844
Added OS.pm::get_os_perl_package. Added Windows.pm::_get_os_perl_package which 
is called by OS.pm::get_os_perl_package.

Updated OS.pm::get_os_type to return 'linux-ubuntu' if Ubuntu is detected 
rather than just 'linux'.

Added generate_ssh_key_files,generate_ssh_public_key_string, and 
create_ssh_public_key_file subroutines to OS.pm. These are used to facilitate 
host to host copying of files for VM migrations.

Added hibernate subroutine to Linux.pm, Ubuntu.pm, Windows.pm, and Version_5.pm.

Added Windows.pm::enable_hibernation.

Added is_process_running and is_display_manager_running to Linux.pm. These are 
used by Ubuntu.pm::hibernate to overcome issues which cause hibernation to fail.

Added subroutines to Ubuntu.pm to help overcome issues with hibernation:
* grubenv_unset_recordfail
* install_package
* _install_package_helper
* simulate_install_package
* apt_get_update
* fix_debconf_db

VCL-860
Updated Linux.pm::create_user to display a warning if any output line begins 
with 'useradd:'.

Modified:
    vcl/trunk/managementnode/lib/VCL/Module/OS.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/Ubuntu.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS.pm?rev=1675454&r1=1675453&r2=1675454&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS.pm Wed Apr 22 18:41:02 2015
@@ -518,7 +518,7 @@ sub get_currentimage_txt_contents {
                return;
        }
 
-       my $computer_node_name   = $self->data->get_computer_node_name();
+       my $computer_node_name = $self->data->get_computer_node_name();
 
        # Attempt to retrieve the contents of currentimage.txt
        my $cat_command = "cat ~/currentimage.txt";
@@ -2783,7 +2783,7 @@ sub execute_new {
  Returns     : If successful: string
                If failed: false
  Description : Determines the OS type currently installed on the computer. It
-               returns 'windows' or 'linux'.
+               returns 'windows', 'linux', or 'linux-ubuntu'.
 
 =cut
 
@@ -2807,6 +2807,10 @@ sub get_os_type {
                notify($ERRORS{'WARNING'}, 0, "error occurred attempting to 
determine OS type currently installed on $computer_node_name\ncommand: 
'$command'\noutput:\n" . join("\n", @$output));
                return;
        }
+       elsif (grep(/ubuntu/i, @$output)) {
+               notify($ERRORS{'DEBUG'}, 0, "Ubuntu Linux OS is currently 
installed on $computer_node_name, output:\n" . join("\n", @$output));
+               return 'linux-ubuntu';
+       }
        elsif (grep(/linux/i, @$output)) {
                notify($ERRORS{'DEBUG'}, 0, "Linux OS is currently installed on 
$computer_node_name, output:\n" . join("\n", @$output));
                return 'linux';
@@ -2823,6 +2827,62 @@ sub get_os_type {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_os_perl_package
+
+ Parameters  : $computer_name
+ Returns     : string
+ Description : Attempts to determine the Perl package which should be used to
+               control the computer.
+
+=cut
+
+sub get_os_perl_package {
+       my $computer_identifier = shift;
+       if (ref($computer_identifier)) {
+               $computer_identifier = shift
+       }
+       if (!$computer_identifier) {
+               notify($ERRORS{'WARNING'}, 0, "computer identifier argument not 
specified");
+               return;
+       }
+       
+       my $os = VCL::Module::create_object('VCL::Module::OS', { 
computer_identifier => $computer_identifier});
+       if (!$os) {
+               notify($ERRORS{'WARNING'}, 0, "unable to determine perl package 
to use for OS installed on $computer_identifier, OS object could not be 
created");
+               return;
+       }
+       
+       
+       my $command = "uname -a";
+       my ($exit_status, $output) = $os->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
determine OS installed on $computer_identifier");
+               return;
+       }
+       
+       my $os_perl_package;
+       if (grep(/Cygwin/i, @$output)) {
+               my $windows_os = 
VCL::Module::create_object('VCL::Module::OS::Windows', { computer_identifier => 
$computer_identifier});
+               if (!$windows_os) {
+                       notify($ERRORS{'WARNING'}, 0, "unable to determine perl 
package to use for OS installed on $computer_identifier, Windows OS object 
could not be created");
+                       return;
+               }
+               return $windows_os->_get_os_perl_package($os);
+       }
+       elsif (grep(/Ubuntu/i, @$output)) {
+               return "VCL::Module::OS::Linux::Ubuntu"
+       }
+       elsif (grep(/Linux/i, @$output)) {
+               return "VCL::Module::OS::Linux"
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to determine OS installed 
on $computer_identifier, unsupported output returned from '$command':\n" . 
join("\n", @$output));
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 process_connect_methods
 
  Parameters  : $remote_ip (optional), $overwrite
@@ -4035,6 +4095,182 @@ sub get_cluster_info_file_path {
        return $self->{cluster_info_file_path};
 }
 
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 generate_ssh_key_files
+
+ Parameters  : $private_key_file_path, $type (optional), $bits (optional), 
$comment (optional), $passphrase, $options (optional)
+ Returns     : boolean
+ Description : Calls ssh-keygen to generate an SSH private key file.
+
+=cut
+
+sub generate_ssh_key_files {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module::OS/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my ($private_key_file_path, $type, $bits, $comment, $passphrase, 
$options) = @_;
+       if (!$private_key_file_path) {
+               notify($ERRORS{'WARNING'}, 0, "private key file path argument 
was not specified");
+               return;
+       }
+       $type = 'rsa' unless $type;
+       $passphrase = '' unless $passphrase;
+       
+       my $computer_name = $self->data->get_computer_short_name();
+       
+       # Make sure the file does not already exist
+       if ($self->file_exists($private_key_file_path)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to generate SSH key, file 
already exists on $computer_name: $private_key_file_path");
+               return;
+       }
+       
+       my $command = "ssh-keygen -t $type -f \"$private_key_file_path\" -N 
\"$passphrase\"";
+       $command .= " -b $bits" if defined($bits);
+       $comment .= " $options" if defined($options);
+       
+       if (defined($comment)) {
+               $comment =~ s/\\*(["])/\\"$1/g;
+               $command .= " -C \"$comment\"";;
+       }
+       
+       my ($exit_status, $output) = $self->mn_os->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
generate SSH key on $computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to generate SSH key on 
$computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . 
join("\n", @$output));
+               return;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "generated SSH key on $computer_name: 
$private_key_file_path, command: $command");
+               return 1;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 generate_ssh_public_key_string
+
+ Parameters  : $private_key_file_path, $comment (optional)
+ Returns     : boolean
+ Description : Calls ssh-keygen to retrieve the corresponding SSH public key
+               from a private key file.
+
+=cut
+
+sub generate_ssh_public_key_string {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module::OS/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my ($private_key_file_path, $comment) = @_;
+       if (!$private_key_file_path) {
+               notify($ERRORS{'WARNING'}, 0, "private key file path argument 
was not specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_short_name();
+       
+       # Make sure the private key file exists
+       if (!$self->file_exists($private_key_file_path)) {
+               notify($ERRORS{'WARNING'}, 0, "unable to generate SSH public 
key, private key file does not exist on $computer_name: 
$private_key_file_path");
+               return;
+       }
+       
+       my $command = "ssh-keygen -y -f \"$private_key_file_path\"";
+       my ($exit_status, $output) = $self->mn_os->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
generate SSH public key string from $private_key_file_path on $computer_name");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to generate SSH public 
key string from $private_key_file_path on $computer_name, exit status: 
$exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       
+       my ($ssh_public_key_string) = grep(/^ssh-.*/, @$output);
+       if ($ssh_public_key_string) {
+               if ($comment) {
+                       if ($ssh_public_key_string !~ /=/) {
+                               $ssh_public_key_string .= "==";
+                       }
+                       $ssh_public_key_string .= " $comment";
+               }
+               notify($ERRORS{'OK'}, 0, "generated SSH public key string from 
$private_key_file_path on $computer_name: $ssh_public_key_string");
+               return $ssh_public_key_string;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "failed to generate SSH public key 
string from $private_key_file_path on $computer_name, output does not contain a 
line beginning with 'ssh-', command:\n$command\noutput:\n" . join("\n", 
@$output));
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_ssh_public_key_file
+
+ Parameters  : $private_key_file_path, $public_key_file_path, $comment 
(optional)
+ Returns     : boolean
+ Description : Calls ssh-keygen to retrieve the corresponding SSH public key
+               from a private key file and generates a file containing the
+               public key.
+
+=cut
+
+sub create_ssh_public_key_file {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module::OS/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my ($private_key_file_path, $public_key_file_path, $comment) = @_;
+       if (!$private_key_file_path) {
+               notify($ERRORS{'WARNING'}, 0, "private key file path argument 
was not specified");
+               return;
+       }
+       if (!$public_key_file_path) {
+               notify($ERRORS{'WARNING'}, 0, "public key file path argument 
was not specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_short_name();
+       
+       # Make sure the private key file exists
+       if (!$self->file_exists($private_key_file_path)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to generate SSH public 
key file, private key file does not exist on $computer_name: 
$private_key_file_path");
+               return;
+       }
+       
+       # Make sure the public key file does not exist
+       if ($self->file_exists($public_key_file_path)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to create SSH public key 
file, public key file already exists on $computer_name: $public_key_file_path");
+               return;
+       }
+       
+       my $public_key_string = 
$self->generate_ssh_public_key_string($private_key_file_path, $comment);
+       if (!$public_key_string) {
+               notify($ERRORS{'WARNING'}, 0, "failed to create SSH public key 
file: $public_key_file_path, public key string could not be retrieved from 
private key file: $private_key_file_path");
+               return;
+       }
+       
+       if ($self->create_text_file($public_key_file_path, $public_key_string)) 
{
+               notify($ERRORS{'DEBUG'}, 0, "created SSH public key file: 
$public_key_file_path");
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to create SSH public key 
file: $public_key_file_path");
+               return;
+       }
+}
+
 #///////////////////////////////////////////////////////////////////////////
 
 1;

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm?rev=1675454&r1=1675453&r2=1675454&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Wed Apr 22 18:41:02 2015
@@ -2608,11 +2608,70 @@ sub shutdown {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 hibernate
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Hibernates the computer.
+
+=cut
+
+sub hibernate {
+       my $self = shift;
+       if (ref($self) !~ /linux/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 $command = 'echo disk > /sys/power/state &';
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
hibernate $computer_node_name");
+               return;
+       }
+       elsif ($exit_status eq 0) {
+               notify($ERRORS{'OK'}, 0, "executed command to hibernate 
$computer_node_name: $command" . (scalar(@$output) ? "\noutput:\n" . join("\n", 
@$output) : ''));
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_node_name, exit status: $exit_status, command:\n$command\noutput:\n" 
. join("\n", @$output));
+               return;
+       }
+       
+       # Wait for computer to power off
+       my $power_off = $self->provisioner->wait_for_power_off(300, 5);
+       if (!defined($power_off)) {
+               # wait_for_power_off result will be undefined if the 
provisioning module doesn't implement a power_status subroutine
+               notify($ERRORS{'OK'}, 0, "unable to determine power status of 
$computer_node_name from provisioning module, sleeping 1 minute to allow 
computer time to hibernate");
+               sleep 60;
+               return 1;
+       }
+       elsif (!$power_off) {
+               notify($ERRORS{'WARNING'}, 0, "$computer_node_name never 
powered off after executing hibernate command: $command");
+               return;
+       }
+       else {
+               notify($ERRORS{'DEBUG'}, 0, "$computer_node_name powered off 
after executing hibernate command");
+               return 1;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 create_user
 
- Parameters  : 
+ Parameters  : $argument_hash_ref
  Returns     : boolean
- Description : 
+ Description : Creates a user on the computer. The argument hash reference
+               should be constructed as follows:
+                                       {
+                                               username => $username,
+                                               password => $password, 
(optional)
+                                               root_access => $root_access,
+                                               uid => $uid, (optional)
+                                               ssh_public_keys => 
$ssh_public_keys, (optional)
+                                       });
 
 =cut
 
@@ -2679,11 +2738,11 @@ sub create_user {
                                notify($ERRORS{'WARNING'}, 0, "failed to 
execute command to add user '$username' to $computer_node_name: 
'$useradd_command'");
                                return;
                        }
-                       elsif (grep(/^useradd: warning/, @$useradd_output)) {
+                       elsif (grep(/^useradd: /, @$useradd_output)) {
                                notify($ERRORS{'WARNING'}, 0, "warning detected 
on add user '$username' to $computer_node_name\ncommand: 
'$useradd_command'\noutput:\n" . join("\n", @$useradd_output));
                        }
                        else {
-                               notify($ERRORS{'OK'}, 0, "added user 
'$username' to $computer_node_name");
+                               notify($ERRORS{'OK'}, 0, "added user 
'$username' to $computer_node_name, output:" . (scalar(@$useradd_output) ? "\n" 
. join("\n", @$useradd_output) : ' <none>'));
                        }
                }
                else {
@@ -5627,6 +5686,173 @@ sub kill_process {
        }
 }
 
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 is_process_running
+
+ Parameters  : $process_regex
+ Returns     : array or hash reference
+ Description : Determines if any processes matching the $process_regex
+               argument are running on the computer. The $process_regex must be
+               a valid Perl regular expression.
+               
+               The following command is used to determine if a process is
+               running:
+               ps -e -o pid,args | grep -P "$process_regex"
+               
+               The behavior is different than if the -P argument is not used.
+               The following characters must be escaped with a backslash in
+               order for a literal match to be found:
+               | ( ) [ ] . +
+               
+               If these are not escaped, grep will interpret them as the
+               corresponing regular expression operational character. For
+               example:
+               
+               To match this literal string:
+               |(foo)|
+               Pass this:
+               \|\(foo\)\|
+               
+               To match 'foo' or 'bar, pass this:
+               (foo|bar)
+               
+               To match a pipe character ('|'), followed by either 'foo' or
+               'bar, followed by another pipe character:
+               |foo|
+               Pass this:
+               \|(foo|bar)\|
+               
+               The return value differs based on how this subroutine is called.
+               If called in scalar context, a hash reference is returned. The
+               hash keys are PIDs and the values are the full name of the
+               process. If called in list context, an array is returned
+               containing the PIDs.
+
+=cut
+
+sub is_process_running {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       # Check the arguments
+       my ($process_regex) = @_;
+       if (!defined($process_regex)) {
+               notify($ERRORS{'WARNING'}, 0, "process regex pattern argument 
was not specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_short_name();
+       
+       my $command = "ps -e -o pid,args | grep -P \"$process_regex\"";
+       my ($exit_status, $output) = $self->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command on 
$computer_name to determine if process is running: $command");
+               return;
+       }
+       
+       my $processes_running = {};
+       for my $line (@$output) {
+               my ($pid, $process_name) = $line =~ /^\s*(\d+)\s*(.*[^\s])\s*/g;
+               
+               if (!defined($pid)) {
+                       notify($ERRORS{'DEBUG'}, 0, "ignoring line, it does not 
begin with a number: '$line'");
+                       next;
+               }
+               elsif ($pid eq $PID) {
+                       #notify($ERRORS{'DEBUG'}, 0, "ignoring line for the 
currently running process: $line");
+                       next;
+               }
+               elsif ($line =~ /grep -P/) {
+                       #notify($ERRORS{'DEBUG'}, 0, "ignoring line containing 
for this command: $line");
+                       next;
+               }
+               elsif ($line =~ /sh -c/) {
+                       # Ignore lines containing 'sh -c', probably indicating 
a duplicate process of a command run remotely
+                       #notify($ERRORS{'DEBUG'}, 0, "ignoring containing 'sh 
-c': $line");
+                       next;
+               }
+               else {
+                       #notify($ERRORS{'DEBUG'}, 0, "found matching process: 
$line");
+                       $processes_running->{$pid} = $process_name;
+               }
+       }
+       
+       my $process_count = scalar(keys %$processes_running);
+       if ($process_count) {
+               if (wantarray) {
+                       my @process_ids = sort keys %$processes_running;
+                       notify($ERRORS{'DEBUG'}, 0, "process is running on 
$computer_name, identifier: '$process_regex', returning array containing PIDs: 
@process_ids");
+                       return @process_ids;
+               }
+               else {
+                       notify($ERRORS{'DEBUG'}, 0, "process is running on 
$computer_name, identifier: '$process_regex', returning hash reference:\n" . 
format_data($processes_running));
+                       return $processes_running;
+               }
+       }
+       else {
+               notify($ERRORS{'DEBUG'}, 0, "process is NOT running on 
$computer_name, identifier: '$process_regex', command: $command");
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 is_display_manager_running
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Checks if a display manager (GUI) is running on the computer.
+
+=cut
+
+sub is_display_manager_running {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_short_name();
+       
+       # Note: runlevel isn't reliable for all distros
+       # On Ubuntu, it displays 2 even if the GUI is running
+       
+       my $process_pattern;
+       
+       # CentOS "Welcome" screen
+       #  1700 /usr/bin/Xorg :9 -ac -nolisten tcp vt6 -br
+       
+       # ' 416 lightdm'
+       # '2955 lightdm --session-child 12 21'
+       $process_pattern .= '^\s*\d+\s+(kdm|lightdm)(\s|$)';
+       
+       # Gnome
+       # 1870 /usr/sbin/gdm-binary -nodaemon
+       # 1898 /usr/libexec/gdm-simple-slave --display-id 
/org/gnome/DisplayManager/Display1
+       # 1901 /usr/bin/Xorg :0 -br -verbose -audit 4 -auth 
/var/run/gdm/auth-for-gdm-laIZj5/database -nolisten tcp vt1
+       # 1989 /usr/bin/gnome-session 
--autostart=/usr/share/gdm/autostart/LoginWindow/
+       $process_pattern .= '|(gnome-session|gdm-binary)';
+       
+       # ' 2891 /usr/bin/X -core :0 -seat seat0 -auth /var/run/lightdm/root/:0 
-nolisten tcp vt7 -novtswitch'
+       $process_pattern .= '|bin\/X';
+       
+       $process_pattern = "($process_pattern)";
+       
+       my $process_info = $self->is_process_running($process_pattern);
+       if ($process_info) {
+               notify($ERRORS{'DEBUG'}, 0, "display manager is running on 
$computer_name:\n" . format_data($process_info));
+               return 1;
+       }
+       else {
+               notify($ERRORS{'DEBUG'}, 0, "display manager is not running on 
$computer_name");
+               return 0
+       }
+}
+
 ##/////////////////////////////////////////////////////////////////////////////
 1;
 __END__

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/Ubuntu.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/Ubuntu.pm?rev=1675454&r1=1675453&r2=1675454&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/Ubuntu.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/Ubuntu.pm Wed Apr 22 
18:41:02 2015
@@ -894,6 +894,477 @@ sub activate_interfaces {
 }
 
 #/////////////////////////////////////////////////////////////////////////////
+
+=head2 hibernate
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Hibernates the computer.
+
+=cut
+
+sub hibernate {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       # Notes (ARK): Ubuntu 14+ seems to have issues hibernating. The 
machine's
+       # console may turn into a black screen with a blinking cursor if the GUI
+       # isn't running and SSH access may become unavailable. I haven't found 
a way
+       # to recover from this when it happens without a hard reset.
+
+       my $computer_name = $self->data->get_computer_node_name();
+       
+       # Make sure pm-hibernate command exists
+       if (!$self->command_exists('pm-hibernate')) {
+               if (!$self->install_package('pm-utils')) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_name, pm-hibernate command does not exist and pm-utils could not be 
installed");
+                       return;
+               }
+       }
+       
+       # Ubuntu seems to have problems hibernating if a display manager isn't 
running
+       # If it is not running, attempt to install and start lightdm
+       if (!$self->is_display_manager_running()) {
+               #if (!$self->install_package('xfce4')) {
+               #       notify($ERRORS{'WARNING'}, 0, "hibernation of 
$computer_name not attempted, display manager/GUI is not running, failed to 
install xfce4");
+               #       return;
+               #}
+               if (!$self->install_package('lightdm')) {
+                       notify($ERRORS{'WARNING'}, 0, "hibernation of 
$computer_name not attempted, display manager/GUI is not running, failed to 
install xfce4");
+                       return;
+               }
+               if (!$self->start_service('lightdm')) {
+                       notify($ERRORS{'WARNING'}, 0, "hibernation of 
$computer_name not attempted, display manager/GUI is not running, failed to 
start lightdm service");
+                       return;
+               }
+               if (!$self->is_display_manager_running()) {
+                       notify($ERRORS{'WARNING'}, 0, "hibernation of 
$computer_name not attempted, unable to verify display manager/GUI is running, 
hibernate may fail to shut down the computer unless GUI is running");
+                       return;
+               }
+       }
+       
+       # Delete old log files
+       $self->delete_file('/var/log/pm-*');
+       
+       # Try to determine if NetworkManager or network service is being used
+       my $network_service_name = 'network';
+       if ($self->service_exists('network-manager')) {
+               $network_service_name = 'network-manager';
+       }
+       
+       my $private_interface_name = $self->get_private_interface_name() || 
'eth0';
+       my $public_interface_name = $self->get_public_interface_name() || 
'eth1';
+       
+       # Some versions of Ubuntu fail to respond after resuming from 
hibernation
+       # Networking is up but not responding
+       # Add script to restart networking service
+       my $fix_network_script_path = '/etc/pm/sleep.d/50_restart_networking';
+       my $fix_network_log_path = '/var/log/50_restart_networking.log';
+       
+       $self->delete_file($fix_network_log_path);
+       
+       my $fix_network_script_contents = <<"EOF";
+#!/bin/sh
+echo >> /var/log/50_restart_networking.log
+date -R >> /var/log/50_restart_networking.log
+echo "\$1: begin" >> /var/log/50_restart_networking.log
+
+case "\$1" in
+   hibernate)
+      ifdown $private_interface_name 2>&1 >> /var/log/50_restart_networking.log
+      ifdown $public_interface_name 2>&1 >> /var/log/50_restart_networking.log
+      initctl stop $network_service_name 2>&1 >> 
/var/log/50_restart_networking.log
+      modprobe -r vmxnet3 2>&1 >> /var/log/50_restart_networking.log
+      ;;
+   thaw)
+      modprobe vmxnet3 2>&1 >> /var/log/50_restart_networking.log
+      initctl restart $network_service_name 2>&1 >> 
/var/log/50_restart_networking.log
+      ifup $private_interface_name 2>&1 >> /var/log/50_restart_networking.log
+      ifup $public_interface_name 2>&1 >> /var/log/50_restart_networking.log
+      ;;
+esac
+
+echo "\$1: done" >> $fix_network_log_path
+date -R >> /var/log/50_restart_networking.log
+EOF
+       if (!$self->create_text_file($fix_network_script_path, 
$fix_network_script_contents)) {
+               notify($ERRORS{'WARNING'}, 0, "hibernate not attempted, failed 
to create $fix_network_script_path on $computer_name in order to prevent 
networking problems after computer is powered back on");
+               return;
+       }
+       if (!$self->set_file_permissions($fix_network_script_path, '755')) {
+               notify($ERRORS{'WARNING'}, 0, "hibernate not attempted, failed 
to set file permissions on $fix_network_script_path on $computer_name, 
networking problems may occur after computer is powered back on");
+               return;
+       }
+       
+       # Make sure the grubenv recordfail flag is not set
+       if (!$self->unset_grubenv_recordfail()) {
+               notify($ERRORS{'WARNING'}, 0, "hibernate not attempted, failed 
to unset grubenv recordfail flag, computer may hang on grub boot screen after 
it is powered back on");
+               return;
+       }
+       
+       my $command = 'pm-hibernate';
+       #$command .= ' --quirk-dpms-on'                                 if 
($computer_name =~ /32$/);
+       #$command .= ' --quirk-dpms-suspend'            if ($computer_name =~ 
/33$/);
+       #$command .= ' --quirk-radeon-off'                      if 
($computer_name =~ /34$/);
+       #$command .= ' --quirk-s3-bios'                                 if 
($computer_name =~ /35$/);
+       #$command .= ' --quirk-s3-mode'                                 if 
($computer_name =~ /36$/);
+       #$command .= ' --quirk-vbe-post'                                if 
($computer_name =~ /37$/);
+       #$command .= ' --quirk-vbemode-restore'         if ($computer_name =~ 
/38$/);
+       #$command .= ' --quirk-vbestate-restore'        if ($computer_name =~ 
/39$/);
+       #$command .= ' --quirk-vga-mode-3'                      if 
($computer_name =~ /40$/);
+       #$command .= ' --quirk-save-pci'                                if 
($computer_name =~ /41$/);
+       #$command .= ' --store-quirks-as-lkw'           if ($computer_name =~ 
/42$/);
+       $command .= ' &';
+       
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
hibernate $computer_name");
+               return;
+       }
+       elsif ($exit_status eq 0) {
+               notify($ERRORS{'OK'}, 0, "executed command to hibernate 
$computer_name: $command" . (scalar(@$output) ? "\noutput:\n" . join("\n", 
@$output) : ''));
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . 
join("\n", @$output));
+               return;
+       }
+       
+       # Wait for computer to power off
+       my $power_off = $self->provisioner->wait_for_power_off(300, 5);
+       if (!defined($power_off)) {
+               # wait_for_power_off result will be undefined if the 
provisioning module doesn't implement a power_status subroutine
+               notify($ERRORS{'OK'}, 0, "unable to determine power status of 
$computer_name from provisioning module, sleeping 1 minute to allow computer 
time to hibernate");
+               sleep 60;
+               return 1;
+       }
+       elsif (!$power_off) {
+               notify($ERRORS{'WARNING'}, 0, "$computer_name never powered off 
after executing hibernate command: $command");
+               return;
+       }
+       else {
+               notify($ERRORS{'DEBUG'}, 0, "$computer_name powered off after 
executing hibernate command");
+               return 1;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 grubenv_unset_recordfail
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Unsets the grub "recordfail" flag. If this is set, the computer
+               may hang at the grub boot screen when rebooted.
+
+=cut
+
+sub unset_grubenv_recordfail {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_node_name();
+       
+       if (!$self->command_exists('grub-editenv')) {
+               return 1;
+       }
+       
+       my $command = "grub-editenv /boot/grub/grubenv unset recordfail";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
unset grubenv recordfail on $computer_name");
+               return;
+       }
+       elsif ($exit_status eq 0) {
+               notify($ERRORS{'OK'}, 0, "unset grubenv recordfail on 
$computer_name, command: '$command'" . (scalar(@$output) ? "\noutput:\n" . 
join("\n", @$output) : ''));
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to unset grubenv 
recordfail on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 install_package
+
+ Parameters  : $package_name
+ Returns     : boolean
+ Description : Installs a Linux package using apt-get.
+
+=cut
+
+sub install_package {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my ($package_name) = @_;
+       if (!$package_name) {
+               notify($ERRORS{'WARNING'}, 0, "package name argument was not 
supplied");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_node_name();
+       
+       # Run apt-get update before installing package - only do this once
+       $self->apt_get_update();
+       
+       # Some packages are known to cause debconf database errors
+       # Check if package being installed will also install/update a package 
with known problems
+       # Attempt to fix the debconf database if any are found
+       my @simulate_lines = $self->simulate_install_package($package_name);
+       if (@simulate_lines) {
+               my @problematic_packages = grep { $_ =~ 
/(dictionaries-common)/; $_ = $1; } @simulate_lines;
+               if (@problematic_packages) {
+                       @problematic_packages = 
remove_array_duplicates(@problematic_packages);
+                       notify($ERRORS{'DEBUG'}, 0, "installing $package_name 
requires the following packages to be installed which are known to have 
problems with the debconf database, attempting to fix the debconf database 
first:\n" . join("\n", @problematic_packages));
+                       for my $problematic_package (@problematic_packages) {
+                               $self->fix_debconf_db();
+                               
$self->_install_package_helper($problematic_package);
+                       }
+                       $self->fix_debconf_db();
+               }
+       }
+       
+       my $attempt = 0;
+       my $attempt_limit = 2;
+       for (my $attempt = 1; $attempt <= $attempt_limit; $attempt++) {
+               my $attempt_string = ($attempt > 1 ? "attempt 
$attempt/$attempt_limit: " : '');
+               if ($self->_install_package_helper($package_name, 
$attempt_string)) {
+                       return 1;
+               }
+       }
+       
+       notify($ERRORS{'WARNING'}, 0, "failed to install $package_name on 
$computer_name, made $attempt_limit attempts");
+       return;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 _install_package_helper
+
+ Parameters  : $package_name, $attempt_string (optional)
+ Returns     : boolean
+ Description : Helper subroutine to install_package. Executes command to
+               installs a Linux package using apt-get.
+
+=cut
+
+sub _install_package_helper {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my ($package_name, $attempt_string) = @_;
+       if (!$package_name) {
+               notify($ERRORS{'WARNING'}, 0, "package name argument was not 
supplied");
+               return;
+       }
+       $attempt_string = '' unless defined($attempt_string);
+       
+       my $computer_name = $self->data->get_computer_node_name();
+       
+       my $command = "apt-get -qq -y install $package_name";
+       notify($ERRORS{'DEBUG'}, 0, $attempt_string . "installing package on 
$computer_name: $package_name");
+       my ($exit_status, $output) = $self->execute($command, 0, 300);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, $attempt_string . "failed to 
execute command to install $package_name on $computer_name");
+               return;
+       }
+       elsif ($exit_status eq 0) {
+               if (grep(/$package_name is already/, @$output)) {
+                       notify($ERRORS{'OK'}, 0, $attempt_string . 
"$package_name is already installed on $computer_name");
+               }
+               else {
+                       notify($ERRORS{'OK'}, 0, $attempt_string . "installed 
$package_name on $computer_name");
+               }
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, $attempt_string . "failed to 
install $package_name on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return 0;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 simulate_install_package
+
+ Parameters  : $package_name
+ Returns     : array
+ Description : Simulates the installation of a Linux package using apt-get.
+               Returns the output lines as an array.
+
+=cut
+
+sub simulate_install_package {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my ($package_name) = @_;
+       if (!$package_name) {
+               notify($ERRORS{'WARNING'}, 0, "package name argument was not 
supplied");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_node_name();
+       
+       my $command = "apt-get -s install $package_name";
+       notify($ERRORS{'DEBUG'}, 0, "attempting to simulate the installation of 
$package_name on $computer_name");
+       my ($exit_status, $output) = $self->execute($command, 0, 300);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
simulate the installation of $package_name on $computer_name");
+               return;
+       }
+       elsif ($exit_status eq 0) {
+               #notify($ERRORS{'DEBUG'}, 0, "simulated the installation of 
$package_name on $computer_name, output:\n" . join("\n", @$output));
+               return @$output;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to simulate the 
installation of $package_name on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 apt_get_update
+
+ Parameters  : $force (optional)
+ Returns     : boolean
+ Description : Runs 'apt-get update' to resynchronize package index files from
+               their sources. By default, this will only be executed once. The
+               $force argument will cause apt-get update to be executed even if
+               it was previously executed.
+
+=cut
+
+sub apt_get_update {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my ($force) = @_;
+       
+       return 1 if (!$force && $self->{apt_get_update});
+       
+       my $computer_name = $self->data->get_computer_node_name();
+       
+       # Clear out the files under lists to try to avoid these errors:
+       #    W: Failed to fetch 
http://us.archive.ubuntu.com/ubuntu/dists/trusty-updates/universe/i18n/Translation-en
  Hash Sum mismatch
+       #    E: Some index files failed to download. They have been ignored, or 
old ones used instead.
+       $self->delete_file('/var/lib/apt/lists/*');
+       
+       notify($ERRORS{'DEBUG'}, 0, "executing 'apt-get update' on 
$computer_name");
+       my $command = "apt-get -qq update";
+       my ($exit_status, $output) = $self->execute($command, 0, 300);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute 'apt-get 
update' on $computer_name");
+               return;
+       }
+       elsif ($exit_status eq 0) {
+               notify($ERRORS{'OK'}, 0, "executed 'apt-get update' on 
$computer_name");
+               $self->{apt_get_update} = 1;
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute 'apt-get 
update' on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 fix_debconf_db
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Executes /usr/share/debconf/fix_db.pl to attempt to fix problems
+               installing packages via apt-get.
+
+=cut
+
+sub fix_debconf_db {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_node_name();
+
+       # Setting up dictionaries-common (1.20.5) ...
+       # debconf: unable to initialize frontend: Dialog
+       # debconf: (TERM is not set, so the dialog frontend is not usable.)
+       # debconf: falling back to frontend: Readline
+       # debconf: unable to initialize frontend: Readline
+       # debconf: (This frontend requires a controlling tty.)
+       # debconf: falling back to frontend: Teletype
+       # update-default-wordlist: Question empty but elements installed for 
class "wordlist"
+       # dictionaries-common/default-wordlist: return code: "0", value: ""
+       # Choices: , Manual symlink setting
+       # shared/packages-wordlist: return code: "10" owners/error: 
"shared/packages-wordlist doesn't exist"
+       # Installed elements: english (Webster's Second International English 
wordlist)
+       # Please see "/usr/share/doc/dictionaries-common/README.problems", 
section
+       # "Debconf database corruption" for recovery info.
+       # update-default-wordlist: Selected wordlist ""
+       # does not correspond to any installed package in the system
+       # and no alternative wordlist could be selected.
+       # dpkg: error processing package dictionaries-common (--configure):
+       # subprocess installed post-installation script returned error exit 
status 255
+
+       my $command = "/usr/share/debconf/fix_db.pl";
+       my $attempt = 0;
+       my $attempt_limit = 5;
+       while ($attempt < $attempt_limit) {
+               $attempt++;
+               
+               my ($exit_status, $output) = $self->execute($command, 0, 60);
+               if (!defined($output)) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to execute 
command to attempt to fix debconf database on $computer_name: $command");
+                       return;
+               }
+               
+               # This command occasionally needs to be run multiple times to 
fix all problems
+               # If output contains a line such as the following, run it again:
+               #    debconf: template "base-passwd/user-change-uid" has no 
owners; removing it.
+               if ($exit_status == 0) {
+                       my @lines = grep(/^debconf: /, @$output);
+                       my $line_count = scalar(@lines);
+                       if ($line_count) {
+                               notify($ERRORS{'DEBUG'}, 0, "attempt 
$attempt/$attempt_limit: executed command to fix debconf database on 
$computer_name, $line_count problems were detected and/or fixed, another 
attempt will be made");
+                               next;
+                       }
+                       else {
+                               notify($ERRORS{'DEBUG'}, 0, "attempt 
$attempt/$attempt_limit: no debconf database problems were detected on 
$computer_name");
+                               return 1;
+                       }
+               }
+               else {
+                       notify($ERRORS{'WARNING'}, 0, "attempt 
$attempt/$attempt_limit: failed to execute command to fix debconf database on 
$computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . 
join("\n", @$output));
+                       return;
+               }
+       }
+       
+}
+
+#/////////////////////////////////////////////////////////////////////////////
 1;
 __END__
 

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=1675454&r1=1675453&r2=1675454&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Wed Apr 22 18:41:02 
2015
@@ -9954,6 +9954,57 @@ sub set_device_path_key {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 enable_hibernation
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Enables the hibernation feature.
+
+=cut
+
+sub enable_hibernation {
+       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 $computer_node_name = $self->data->get_computer_node_name();
+       my $system32_path = $self->get_system32_path() || return;
+       
+       # Rename disableGuestHibernate.dll if it exists, this can prevent 
hibernation from working as expected
+       my $disable_hibernate_file_path = 'C:\Program Files\VMware\VMware 
Tools\plugins\vmsvc\disableGuestHibernate.dll';
+       if ($self->file_exists($disable_hibernate_file_path)) {
+               $self->move_file($disable_hibernate_file_path, 
"$disable_hibernate_file_path.disabled");
+       }
+       
+       # Run powercfg.exe to enable hibernation
+       my $command = "$system32_path/powercfg.exe -HIBERNATE ON";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
enable hibernation on $computer_node_name");
+               return;
+       }
+       elsif ($exit_status == 0) {
+               notify($ERRORS{'OK'}, 0, "enabled hibernation on 
$computer_node_name" . (scalar(@$output) ? ", output:\n" . join("\n", @$output) 
: ''));
+       }
+       elsif (grep(/PAE mode/i, @$output)) {
+               # The following may be displayed:
+               #    Hibernation failed with the following error: The request 
is not supported.
+               #    The following items are preventing hibernation on this 
system.
+               #    The system is running in PAE mode, and hibernation is not 
allowed in PAE mode.
+               notify($ERRORS{'OK'}, 0, "hibernation NOT enabled because 
$computer_node_name is running in PAE mode");
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to enable hibernation on 
$computer_node_name, exit status: $exit_status, output:\n" . join("\n", 
@$output));
+               return;
+       }
+       
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 disable_hibernation
 
  Parameters  : None
@@ -9970,28 +10021,34 @@ sub disable_hibernation {
                return;
        }
        
-       my $computer_node_name   = $self->data->get_computer_node_name();
-       my $system32_path        = $self->get_system32_path() || return;
-
+       my $computer_node_name = $self->data->get_computer_node_name();
+       my $system32_path = $self->get_system32_path() || return;
+       
+       # Rename disableGuestHibernate.dll if it exists, this can prevent 
hibernation from working as expected
+       my $disable_hibernate_file_path = 'C:\Program Files\VMware\VMware 
Tools\plugins\vmsvc\disableGuestHibernate.dll';
+       if ($self->file_exists($disable_hibernate_file_path)) {
+               $self->move_file($disable_hibernate_file_path, 
"$disable_hibernate_file_path.disabled");
+       }
+       
        # Run powercfg.exe to disable hibernation
-       my $powercfg_command = "$system32_path/powercfg.exe -HIBERNATE OFF";
-       my ($powercfg_exit_status, $powercfg_output) = 
$self->execute($powercfg_command, 1);
-       if (defined($powercfg_exit_status) && $powercfg_exit_status == 0) {
-               notify($ERRORS{'OK'}, 0, "disabled hibernation");
+       my $command = "$system32_path/powercfg.exe -HIBERNATE OFF";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
disable hibernation on $computer_node_name");
+               return;
+       }
+       elsif ($exit_status == 0) {
+               notify($ERRORS{'OK'}, 0, "disabled hibernation on 
$computer_node_name" . (scalar(@$output) ? ", output:\n" . join("\n", @$output) 
: ''));
        }
-       elsif (grep(/PAE mode/i, @$powercfg_output)) {
+       elsif (grep(/PAE mode/i, @$output)) {
                # The following may be displayed:
                #    Hibernation failed with the following error: The request 
is not supported.
                #    The following items are preventing hibernation on this 
system.
                #    The system is running in PAE mode, and hibernation is not 
allowed in PAE mode.
                notify($ERRORS{'OK'}, 0, "hibernation NOT disabled because 
$computer_node_name is running in PAE mode");
        }
-       elsif ($powercfg_exit_status) {
-               notify($ERRORS{'WARNING'}, 0, "failed to disable hibernation, 
exit status: $powercfg_exit_status, output:\n" . join("\n", @$powercfg_output));
-               return;
-       }
        else {
-               notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to 
disable hibernation");
+               notify($ERRORS{'WARNING'}, 0, "failed to disable hibernation on 
$computer_node_name, exit status: $exit_status, output:\n" . join("\n", 
@$output));
                return;
        }
        
@@ -10006,6 +10063,60 @@ sub disable_hibernation {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 hibernate
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Hibernate the computer.
+
+=cut
+
+sub hibernate {
+       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 $computer_node_name = $self->data->get_computer_node_name();
+       my $system32_path = $self->get_system32_path() || return;
+       
+       if (!$self->enable_hibernation()) {
+               notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_node_name, hibernation could not be enabled");
+               return;
+       }
+       
+       # Run powercfg.exe to enable hibernation
+       my $command = "/bin/cygstart.exe \$SYSTEMROOT/system32/cmd.exe /c 
\"$system32_path/shutdown.exe -h -f\"";
+       my $start_time = time;
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
hibernate $computer_node_name");
+               return;
+       }
+       elsif ($exit_status eq 0) {
+               notify($ERRORS{'OK'}, 0, "executed command to hibernate 
$computer_node_name: $command" . (scalar(@$output) ? "\noutput:\n" . join("\n", 
@$output) : ''));
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_node_name, exit status: $exit_status, command:\n$command\noutput:\n" 
. join("\n", @$output));
+               return;
+       }
+       
+       # Wait for the computer to stop responding
+       my $wait_seconds = 300;
+       if ($self->provisioner->wait_for_power_off($wait_seconds, 3)) {
+               my $duration = (time - $start_time);
+               notify($ERRORS{'DEBUG'}, 0, "hibernate successful, 
$computer_node_name stopped responding after $duration seconds");
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_node_name, still responding to ping after $wait_seconds seconds");
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 disable_ceip
 
  Parameters  : None
@@ -12291,6 +12402,43 @@ sub set_computer_hostname {
 }
 
 #/////////////////////////////////////////////////////////////////////////////
+
+=head2 _get_os_perl_package
+
+ Parameters  : $windows_os
+ Returns     : string
+ Description : 
+
+=cut
+
+sub _get_os_perl_package {
+       my $windows_os = shift;
+       unless (ref($windows_os) && $windows_os->isa('VCL::Module')) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+
+       my $product_name = $windows_os->get_product_name();
+       my $perl_package;
+       if (!$product_name) {
+               return;
+       }
+       elsif ($product_name =~ /(XP|2003)/i) {
+               $perl_package = "VCL::Module::OS::Windows::Version_5::$1";
+       }
+       elsif ($product_name =~ /(Vista|2008|2012|7|8)/ig) {
+               $perl_package = "VCL::Module::OS::Windows::Version_6::$1";
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to determine OS installed 
on computer, unsupported Windows product name: $product_name");
+               return;
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, "perl package to use for '$product_name': 
$perl_package");
+       return $perl_package;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
 
 1;
 __END__

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm?rev=1675454&r1=1675453&r2=1675454&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5.pm Wed Apr 22 
18:41:02 2015
@@ -757,6 +757,60 @@ sub disable_sleep {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 hibernate
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Hibernate the computer.
+
+=cut
+
+sub hibernate {
+       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 $computer_node_name = $self->data->get_computer_node_name();
+       my $system32_path = $self->get_system32_path() || return;
+       
+       if (!$self->enable_hibernation()) {
+               notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_node_name, hibernation could not be enabled");
+               return;
+       }
+       
+       # Run powercfg.exe to enable hibernation
+       my $command = "/bin/cygstart.exe \$SYSTEMROOT/system32/cmd.exe /c 
\"$system32_path/rundll32.exe powrprof.dll,SetSuspendState\"";
+       my $start_time = time;
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
hibernate $computer_node_name");
+               return;
+       }
+       elsif ($exit_status eq 0) {
+               notify($ERRORS{'OK'}, 0, "executed command to hibernate 
$computer_node_name:\n$command" . (scalar(@$output) ? "\noutput:\n" . 
join("\n", @$output) : ''));
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_node_name, exit status: $exit_status, output:\n" . join("\n", 
@$output));
+               return;
+       }
+       
+       # Wait for the computer to stop responding
+       my $wait_seconds = 300;
+       if ($self->provisioner->wait_for_power_off($wait_seconds, 3)) {
+               my $duration = (time - $start_time);
+               notify($ERRORS{'DEBUG'}, 0, "hibernate successful, 
$computer_node_name stopped responding after $duration seconds");
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to hibernate 
$computer_node_name, still responding to ping after $wait_seconds seconds");
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 1;
 __END__
 


Reply via email to