Author: arkurth
Date: Wed Jun  2 19:26:30 2010
New Revision: 950733

URL: http://svn.apache.org/viewvc?rev=950733&view=rev
Log:
VCL-298
Added subroutines to Linux.pm: execute, file_exists, delete_file, move_file, 
copy_file, copy_file_to, copy_file_from, get_file_contents, 
get_available_space, get_file_size, find_files. These are called by the new 
VMware code to act on the VM host.

Renamed filesystem_entry_exists to file_exists subroutine in Linux.pm to match 
other OS modules.

Added escape_file_path, normalize_file_path, and parent_directory_path 
subroutines to utils.pm. They format file paths and are called by the new subs 
in Linux.pm.

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

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=950733&r1=950732&r2=950733&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Wed Jun  2 
19:26:30 2010
@@ -51,6 +51,7 @@ use 5.008000;
 use strict;
 use warnings;
 use diagnostics;
+no warnings 'redefine';
 
 use VCL::utils;
 
@@ -63,8 +64,8 @@ use VCL::utils;
 =head2 $NODE_CONFIGURATION_DIRECTORY
 
  Data type   : String
- Description : Location on computer on which an image has been loaded where
-               configuration files reside.
+ Description : Location on computer loaded with a VCL image where configuration
+               files and scripts reside.
 
 =cut
 
@@ -74,11 +75,11 @@ our $NODE_CONFIGURATION_DIRECTORY = '/ro
 
 =head2 get_node_configuration_directory
 
- Parameters  : None.
- Returns     : String containing filesystem path
- Description : Retrieves the $NODE_CONFIGURATION_DIRECTORY variable value the
-               OS. This is the path on the computer's hard drive where image
-                                       configuration files and scripts are 
copied.
+ Parameters  : none
+ Returns     : string
+ Description : Retrieves the $NODE_CONFIGURATION_DIRECTORY variable value for
+               the OS. This is the path on the computer's hard drive where 
image
+               configuration files and scripts are copied.
 
 =cut
 
@@ -96,8 +97,8 @@ sub get_node_configuration_directory {
 
 =head2 pre_capture
 
- Parameters  :
- Returns     :
+ Parameters  : none
+ Returns     : boolean
  Description :
 
 =cut
@@ -293,7 +294,7 @@ sub post_reserve {
        notify($ERRORS{'OK'}, 0, "initiating Linux post_reserve: $image_name on 
$computer_short_name");
        
        # Check if script exists
-       if (!$self->filesystem_entry_exists($script_path)) {
+       if (!$self->file_exists($script_path)) {
                notify($ERRORS{'DEBUG'}, 0, "script does NOT exist: 
$script_path");
                return 1;
        }
@@ -1207,7 +1208,7 @@ sub call_post_load_custom {
        
        # Check if post_load_custom exists
        my $post_load_custom_path = '/etc/init.d/post_load_custom';
-       if ($self->filesystem_entry_exists($post_load_custom_path)) {
+       if ($self->file_exists($post_load_custom_path)) {
                notify($ERRORS{'DEBUG'}, 0, "post_load_custom script exists: 
$post_load_custom_path");
        }
        else {
@@ -1254,12 +1255,57 @@ sub call_post_load_custom {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 execute
+
+ Parameters  : $command, $display_output (optional)
+ Returns     : array ($exit_status, $output)
+ Description : Executes a command on the Linux computer via SSH.
+
+=cut
+
+sub execute {
+       my $self = shift;
+       unless (ref($self) && $self->isa('VCL::Module')) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called 
as an object method");
+               return;
+       }
+       
+       # Get the command argument
+       my $command = shift;
+       if (!$command) {
+               notify($ERRORS{'WARNING'}, 0, "command argument was not 
specified");
+               return;
+       }
+       
+       # Get 2nd display output argument if supplied, or set default value
+       my $display_output = shift || '0';
+       
+       # Get the computer hostname
+       my $computer_hostname = $self->data->get_computer_hostname() || return;
+       
+       # Get the identity keys used by the management node
+       my $management_node_keys = $self->data->get_management_node_keys() || 
'';
+       
+       # Run the command via SSH
+       my ($exit_status, $output) = run_ssh_command($computer_hostname, 
$management_node_keys, $command, '', '', $display_output);
+       if (defined($exit_status) && defined($output)) {
+               if ($display_output) {
+                       notify($ERRORS{'OK'}, 0, "executed command: '$command', 
exit status: $exit_status, output:\n" . join("\n", @$output));
+               }
+               return ($exit_status, $output);
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command on 
$computer_hostname: $command");
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 run_script
 
  Parameters  : script path
- Returns     : If successfully ran  script: 1
-               If  script does not exist: 0
-               If error occurred: undefined
+ Returns     : boolean
  Description : Checks if script exists on the Linux node and attempts to run 
it.
 
 =cut
@@ -1279,7 +1325,7 @@ sub run_script {
        }
        
        # Check if script exists
-       if ($self->filesystem_entry_exists($script_path)) {
+       if ($self->file_exists($script_path)) {
                notify($ERRORS{'DEBUG'}, 0, "script exists: $script_path");
        }
        else {
@@ -1327,104 +1373,638 @@ sub run_script {
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 create_directory
+=head2 file_exists
 
- Parameters  : directory path
- Returns     : If successful: true
-               If failed: false
- Description : Creates a directory on the Linux node. If a multi-level
-               directory path is specified, parent directories are also created
-                                       if they do not exist.
+ Parameters  : $path
+ Returns     : boolean
+ Description : Checks if a file or directory exists on the Linux computer.
 
 =cut
 
-sub create_directory {
+sub file_exists {
        my $self = shift;
        if (ref($self) !~ /module/i) {
                notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
                return;
        }
        
-       # Make sure path argument was specified
+       # Get the path from the subroutine arguments and make sure it was passed
        my $path = shift;
        if (!$path) {
-               notify($ERRORS{'WARNING'}, 0, "directory path argument was not 
specified");
+               notify($ERRORS{'WARNING'}, 0, "path argument was not 
specified");
                return;
        }
        
-       my $management_node_keys = $self->data->get_management_node_keys();
-       my $computer_node_name   = $self->data->get_computer_node_name();
+       # Remove any quotes from the beginning and end of the path
+       $path = normalize_file_path($path);
+       
+       # Escape all spaces in the path
+       my $escaped_path = escape_file_path($path);
+       
+       # Copy the path and replace any *'s with .* to be used with grep
+       # This string will be used to check the output
+       my $grep_path = $escaped_path;
+       $grep_path =~ s/\*/\.\*/g;
+       
+       my $computer_short_name = $self->data->get_computer_short_name();
+       
+       # Check if the file or directory exists
+       # Do not enclose the path in quotes or else wildcards won't work
+       my $command = "ls -1d $escaped_path";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command to 
determine if file or directory exists on $computer_short_name:\npath: 
'$path'\ncommand: '$command'");
+               return;
+       }
+       elsif (my @matching_file_paths = grep(/^$grep_path/i, @$output)) {
+               notify($ERRORS{'DEBUG'}, 0, "file or directory exists on 
$computer_short_name: '$path', matching paths:\n" . join("\n", 
@matching_file_paths));
+               return 1;
+       }
+       elsif (grep(/ls: /i, @$output) && !grep(/no such file/i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to determine if file or 
directory exists on $computer_short_name:\npath: '$path'\ncommand: 
'$command'\nexit status: $exit_status, output:\n" . join("\n", @$output));
+               return;
+       }
+       else {
+               notify($ERRORS{'DEBUG'}, 0, "file or directory does NOT exist 
on $computer_short_name: '$path'");
+               return 0;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_file
+
+ Parameters  : $path
+ Returns     : boolean
+ Description : Deletes files or directories on the Linux computer.
 
-       # Assemble the mkdir command and execute it
-       my $mkdir_command = "mkdir -p \"$path\" && ls -d \"$path\"";
-       my ($mkdir_exit_status, $mkdir_output) = 
run_ssh_command($computer_node_name, $management_node_keys, $mkdir_command, '', 
'', 1);
-       if (defined($mkdir_output) && grep(/ls: /, @$mkdir_output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to create directory on 
$computer_node_name: $path, exit status: $mkdir_exit_status, 
output:\...@{$mkdir_output}");
+=cut
+
+sub delete_file {
+       my $self = shift;
+       if (ref($self) !~ /module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       # Get the path argument
+       my $path = shift;
+       if (!$path) {
+               notify($ERRORS{'WARNING'}, 0, "path argument were not 
specified");
                return;
        }
-       elsif (defined($mkdir_exit_status)) {
-               notify($ERRORS{'OK'}, 0, "directory created on 
$computer_node_name: $path, output:\...@{$mkdir_output}");
+       
+       # Remove any quotes from the beginning and end of the path
+       $path = normalize_file_path($path);
+       
+       # Escape all spaces in the path
+       my $escaped_path = escape_file_path($path);
+       
+       my $computer_short_name = $self->data->get_computer_short_name();
+       
+       # Delete the file
+       my $command = "rm -rv $escaped_path";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command to delete 
file or directory on $computer_short_name:\npath: '$path'\ncommand: 
'$command'");
+               return;
+       }
+       elsif (!grep(/rm: /i, @$output) && (my @file_paths_deleted = 
grep(/^removed/i, @$output))) {
+               @file_paths_deleted = map { $_ =~ /removed \W(.*)\W$/} 
@file_paths_deleted;
+               notify($ERRORS{'OK'}, 0, "deleted '$path' on 
$computer_short_name, files or directories deleted: " . 
scalar(@file_paths_deleted) . "\n" . join("\n", @file_paths_deleted));
+       }
+       elsif (grep(/(cannot access|no such file)/i, @$output)) {
+               notify($ERRORS{'OK'}, 0, "file or directory not deleted because 
it does not exist on $computer_short_name: $path");
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "error occurred attempting to 
delete file or directory on $computer_short_name: '$path':\ncommand: 
'$command'\nexit status: $exit_status\noutput:\n" . join("\n", @$output));
+       }
+       
+       # Make sure the path does not exist
+       my $host_file_exists = $self->file_exists($path);
+       if (!defined($host_file_exists)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to confirm file doesn't 
exist on $computer_short_name: '$path'");
+               return;
+       }
+       return !$host_file_exists;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_directory
+
+ Parameters  : $directory_path, $mode (optional)
+ Returns     : boolean
+ Description : Creates a directory on the Linux computer as indicated by the
+               $directory_path argument. A 2nd argument specifying the file 
mode
+               (as in chmod) can be specified. The default file mode is 755
+               (drwxr-xr-x).
+
+=cut
+
+sub create_directory {
+       my $self = shift;
+       if (ref($self) !~ /module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       # Get the directory path argument
+       my $directory_path = shift;
+       if (!$directory_path) {
+               notify($ERRORS{'WARNING'}, 0, "directory path argument was not 
supplied");
+               return;
+       }
+       
+       # Remove any quotes from the beginning and end of the path
+       $directory_path = normalize_file_path($directory_path);
+       
+       # Get the mode argument or set the default value
+       my $mode = shift || 755;
+       
+       my $computer_short_name = $self->data->get_computer_short_name();
+       
+       # Attempt to create the directory
+       my $command = "mkdir -p -v --mode=$mode \"$directory_path\" 2>&1 && ls 
-1d \"$directory_path\"";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command to create 
directory on $computer_short_name:\npath: '$directory_path'\ncommand: 
'$command'");
+               return;
+       }
+       elsif (grep(/created directory/i, @$output)) {
+               notify($ERRORS{'OK'}, 0, "created directory on 
$computer_short_name: '$directory_path', mode: $mode");
                return 1;
        }
-       elsif (defined($mkdir_exit_status)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to create directory on 
$computer_node_name: $path, exit status: $mkdir_exit_status, 
output:\...@{$mkdir_output}");
+       elsif (grep(/mkdir: /i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "error occurred attempting to 
create directory on $computer_short_name: '$directory_path':\ncommand: 
'$command'\nexit status: $exit_status\noutput:\n" . join("\n", @$output));
                return;
        }
+       elsif (grep(/^$directory_path/, @$output)) {
+               notify($ERRORS{'OK'}, 0, "directory already exists on 
$computer_short_name: '$directory_path'");
+               return 1;
+       }
        else {
-               notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to 
delete file on $computer_node_name: $path");
+               notify($ERRORS{'WARNING'}, 0, "unexpected output returned from 
command to create directory on $computer_short_name: 
'$directory_path':\ncommand: '$command'\nexit status: $exit_status\noutput:\n" 
. join("\n", @$output) . "\nlast line:\n" . @$output[-1]);
                return;
        }
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 filesystem_entry_exists
+=head2 move_file
 
- Parameters  : filesystem path
- Returns     : If entry exists: 1
-               If entry does not exist: 0
-                                       If error occurred: undefined
- Description : Checks if a filesystem entry (file or directory) exists on the
-                                       Linux node.
+ Parameters  : $source_path, $destination_path
+ Returns     : boolean
+ Description : Moves or renames a file on a Linux computer.
 
 =cut
 
-sub filesystem_entry_exists {
+sub move_file {
        my $self = shift;
        if (ref($self) !~ /module/i) {
                notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
                return;
        }
        
-       # Get the path from the subroutine arguments and make sure it was passed
+       # Get the path arguments
+       my $source_path = shift;
+       my $destination_path = shift;
+       if (!$source_path || !$destination_path) {
+               notify($ERRORS{'WARNING'}, 0, "source and destination path 
arguments were not specified");
+               return;
+       }
+       
+       # Remove any quotes from the beginning and end of the path
+       $source_path = normalize_file_path($source_path);
+       $destination_path = normalize_file_path($destination_path);
+       
+       # Escape all spaces in the path
+       my $escaped_source_path = escape_file_path($source_path);
+       my $escaped_destination_path = escape_file_path($destination_path);
+       
+       my $computer_short_name = $self->data->get_computer_short_name();
+       
+       # Execute the command to move the file
+       my $command = "mv -fv $escaped_source_path $escaped_destination_path";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command to move 
file on $computer_short_name:\nsource path: '$source_path'\ndestination path: 
'$destination_path'\ncommand: '$command'");
+               return;
+       }
+       elsif (grep(/^mv: /i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to move file on 
$computer_short_name:\nsource path: '$source_path'\ndestination path: 
'$destination_path'\ncommand: '$command'\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       elsif (grep(/->/i, @$output)) {
+               notify($ERRORS{'OK'}, 0, "moved file on 
$computer_short_name:\n'$source_path' --> '$destination_path'");
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "unexpected output returned from 
command to move file on $computer_short_name:\nsource path: 
'$source_path'\ndestination path: '$destination_path'\ncommand: 
'$command'\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_file_contents
+
+ Parameters  : $file_path
+ Returns     : array
+ Description : Returns an array containing the contents of the file specified 
by
+               the file path argument. Each array element contains a line from
+               the file.
+
+=cut
+
+sub get_file_contents {
+       my $self = shift;
+       if (ref($self) !~ /module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       # Get the path argument
        my $path = shift;
        if (!$path) {
-               notify($ERRORS{'WARNING'}, 0, "unable to detmine if file 
exists, path was not specified as an argument");
+               notify($ERRORS{'WARNING'}, 0, "path argument was not 
specified");
                return;
        }
        
-       my $management_node_keys = $self->data->get_management_node_keys();
-       my $computer_node_name   = $self->data->get_computer_node_name();
+       my $computer_short_name = $self->data->get_computer_short_name();
        
-       # Assemble the dir command and execute it
-       my $ls_command = "ls -l \"$path\"";
-       my ($ls_exit_status, $ls_output) = run_ssh_command($computer_node_name, 
$management_node_keys, $ls_command, '', '', 1);
-       if (defined($ls_output) && grep(/no such file/i, @$ls_output)) {
-               notify($ERRORS{'DEBUG'}, 0, "filesystem entry does NOT exist on 
$computer_node_name: $path");
-               return 0;
+       # Run cat to retrieve the contents of the file
+       my $command = "cat \"$path\"";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command to read 
file on $computer_short_name:\n path: '$path'\ncommand: '$command'");
+               return;
        }
-       elsif ((defined($ls_exit_status) && $ls_exit_status == 0) || 
(defined($ls_output) && grep(/$path/i, @$ls_output))) {
-               notify($ERRORS{'DEBUG'}, 0, "filesystem entry exists on 
$computer_node_name: $path, dir output:\n" . join("\n", @$ls_output));
-               return 1;
+       elsif (grep(/^cat: /, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to read contents of file 
on $computer_short_name: '$path', exit status: $exit_status, output:\n" . 
join("\n", @$output));
+               return;
        }
-       elsif ($ls_exit_status) {
-               notify($ERRORS{'WARNING'}, 0, "failed to determine if 
filesystem entry exists on $computer_node_name: $path, exit status: 
$ls_exit_status, output:\...@{$ls_output}");
+       else {
+               notify($ERRORS{'DEBUG'}, 0, "retrieved " . scalar(@$output) . " 
lines from file on $computer_short_name: '$path'");
+               return @$output;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_available_space
+
+ Parameters  : none
+ Returns     : integer
+ Description : Returns the bytes available in the path specified by the
+               argument.
+
+=cut
+
+sub get_available_space {
+       my $self = shift;
+       if (ref($self) !~ /module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
                return;
        }
+       
+       # Get the path argument
+       my $path = shift;
+       if (!$path) {
+               notify($ERRORS{'WARNING'}, 0, "path argument was not 
specified");
+               return;
+       }
+       
+       my $computer_short_name = $self->data->get_computer_short_name();
+       
+       # Run df specifying the path as an argument if specified
+       my $command = "df \"$path\"";
+       my ($exit_status, $output) = $self->execute($command);
+       return if !defined($output);
+       
+       if (grep(/^df: /i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "error occurred running df 
command on $computer_short_name:\n" . join("\n", @$output));
+               return;
+       }
+       
+       my @path_lines = grep(!/^Filesystem/i, @$output);
+       if (scalar(@path_lines) == 0) {
+               notify($ERRORS{'WARNING'}, 0, "unable to find filesystem data 
line in df output:\n" . join("\n", @$output));
+               return;
+       }
+       elsif (scalar(@path_lines) > 1) {
+               notify($ERRORS{'WARNING'}, 0, "found multiple filesystem data 
lines in df output:\n" . join("\n", @$output));
+               return;
+       }
+       
+       my ($filesystem, $blocks_total, $blocks_used, $blocks_available, 
$percent_used, $mounted_on) = split(/\s+/, $path_lines[0]);
+       if (!$mounted_on) {
+               notify($ERRORS{'WARNING'}, 0, "failed to parse df output 
line:\n$path_lines[0]\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       
+       my $bytes_available = ($blocks_available * 1024);
+       my $mb_available = format_number(($bytes_available / 1024 / 1024), 2);
+       my $gb_available = format_number(($bytes_available / 1024 / 1024 / 
1024), 1);
+       
+       notify($ERRORS{'DEBUG'}, 0, "bytes available in '$path' on 
$computer_short_name: " . format_number($bytes_available) . " bytes 
($mb_available MB, $gb_available GB)");
+       return $bytes_available;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 copy_file_from
+
+ Parameters  : $source_file_path, $destination_file_path
+ Returns     : boolean
+ Description : Copies file(s) from the Linux computer to the management node.
+
+=cut
+
+sub copy_file_from {
+       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;
+       }
+       
+       # Get the source and destination arguments
+       my ($source_file_path, $destination_file_path) = @_;
+       if (!$source_file_path || !$destination_file_path) {
+               notify($ERRORS{'WARNING'}, 0, "source and destination file path 
arguments were not specified");
+               return;
+       }
+       
+       # Get the computer name
+       my $computer_node_name = $self->data->get_computer_node_name() || 
return;
+       
+       # Get the destination parent directory path and create the directory on 
the management node
+       my $destination_directory_path = 
parent_directory_path($destination_file_path);
+       if (!$destination_directory_path) {
+               notify($ERRORS{'WARNING'}, 0, "unable to determine destination 
parent directory path: $destination_file_path");
+               return;
+       }
+       create_management_node_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("$computer_node_name:\"$source_file_path\"", 
$destination_file_path, $management_node_keys)) {
+               notify($ERRORS{'DEBUG'}, 0, "copied file from 
$computer_node_name to management node: $computer_node_name:'$source_file_path' 
--> '$destination_file_path'");
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to copy file from 
$computer_node_name to management node: $computer_node_name:'$source_file_path' 
--> '$destination_file_path'");
+               return;
+       }
+       
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=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
+ Returns     : boolean
+ Description : Copies a single file on the Linux computer to another location 
on
+               the computer. The source and destination file path arguments may
+               not be directory paths nor may they contain wildcards. 
+
+=cut
+
+sub copy_file {
+       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 path arguments
+       my $source_file_path = shift;
+       my $destination_file_path = shift;
+       if (!$source_file_path || !$destination_file_path) {
+               notify($ERRORS{'WARNING'}, 0, "source and destination file path 
arguments were not specified");
+               return;
+       }
+       
+       # Normalize the source and destination paths
+       $source_file_path = normalize_file_path($source_file_path);
+       $destination_file_path = normalize_file_path($destination_file_path);
+       
+       # Escape all spaces in the path
+       my $escaped_source_path = escape_file_path($source_file_path);
+       my $escaped_destination_path = escape_file_path($destination_file_path);
+       
+       # Make sure the source and destination paths are different
+       if ($escaped_source_path eq $escaped_destination_path) {
+               notify($ERRORS{'WARNING'}, 0, "unable to copy file, source and 
destination file path arguments are the same: $escaped_source_path");
+               return;
+       }
+       
+       # Get the destination parent directory path and create the directory if 
it does not exist
+       my $destination_directory_path = 
parent_directory_path($destination_file_path);
+       if (!$destination_directory_path) {
+               notify($ERRORS{'WARNING'}, 0, "unable to determine destination 
parent directory path: $destination_file_path");
+               return;
+       }
+       $self->create_directory($destination_directory_path) || return;
+       
+       my $computer_node_name = $self->data->get_computer_node_name();
+       
+       # Execute the command to copy the file
+       my $command = "cp -fvr $escaped_source_path $escaped_destination_path";
+       notify($ERRORS{'DEBUG'}, 0, "attempting to copy file on 
$computer_node_name: '$source_file_path' -> '$destination_file_path'");
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command to copy 
file on $computer_node_name:\nsource path: '$source_file_path'\ndestination 
path: '$destination_file_path'\ncommand: '$command'");
+               return;
+       }
+       elsif (grep(/^cp: /i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to copy file on 
$computer_node_name:\nsource path: '$source_file_path'\ndestination path: 
'$destination_file_path'\ncommand: '$command'\noutput:\n" . join("\n", 
@$output));
+               return;
+       }
+       elsif (grep(/->/i, @$output)) {
+               notify($ERRORS{'OK'}, 0, "copied file on $computer_node_name: 
'$source_file_path' --> '$destination_file_path'");
+               return 1;
+       }
        else {
-               notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to 
determine if filesystem entry exists on $computer_node_name: $path");
+               notify($ERRORS{'WARNING'}, 0, "unexpected output returned from 
command to copy file on $computer_node_name:\nsource path: 
'$source_file_path'\ndestination path: '$destination_file_path'\ncommand: 
'$command'\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_file_size
+
+ Parameters  : $file_path
+ Returns     : integer
+ Description : Determines the size of the file or directory specified by the
+               file path argument in bytes. The file path argument may be a
+               directory path or contain wildcards.
+
+=cut
+
+sub get_file_size {
+       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 path argument
+       my $file_path = shift;
+       if (!$file_path) {
+               notify($ERRORS{'WARNING'}, 0, "path argument was not 
specified");
                return;
        }
+       
+       # Normalize the file path
+       $file_path = normalize_file_path($file_path);
+       
+       # Escape all spaces in the path
+       my $escaped_file_path = escape_file_path($file_path);
+       
+       # Get the computer name
+       my $computer_node_name = $self->data->get_computer_node_name() || 
return;
+       
+       # Run du specifying the path as an argument
+       my $command = "du -bc $escaped_file_path";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to run command to 
determine file size on $computer_node_name: $file_path\ncommand: $command");
+               return;
+       }
+       elsif (grep(/^du: /i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "error occurred attempting to 
determine file size on $computer_node_name: $file_path\ncommand: 
$command\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       
+       # Find the line containing 'total'
+       my ($total_line) = grep(/total/, @$output);
+       if (!$total_line) {
+               notify($ERRORS{'WARNING'}, 0, "unable to find total line in du 
output on $computer_node_name: $file_path, output:\n" . join("\n", @$output));
+               return;
+       }
+       
+       # Extract the blocks used number from the total line
+       my ($bytes_used) = $total_line =~ /(\d+)/g;
+       if (!defined($bytes_used) || length($bytes_used) == 0) {
+               notify($ERRORS{'WARNING'}, 0, "unable to determine bytes used 
from the total line in du output on $computer_node_name: $file_path\ncommand: 
$command\ntotal line: $total_line\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       
+       my $kb_used = ($bytes_used / 1024);
+       my $mb_used = ($bytes_used / 1024 / 1024);
+       my $gb_used = ($bytes_used / 1024 / 1024 / 1024);
+       
+       notify($ERRORS{'DEBUG'}, 0, "size of $file_path: " . 
format_number($bytes_used) . " bytes (" . format_number($kb_used, 2) . " KB, " 
. format_number($mb_used, 2) . " MB, " . format_number($gb_used, 2) . " GB)");
+       return $bytes_used;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 find_files
+
+ Parameters  : $base_directory_path, $file_pattern
+ 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.
+
+=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) = @_;
+       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);
+       
+       # Get the computer short and hostname
+       my $computer_node_name = $self->data->get_computer_node_name() || 
return;
+       
+       # Run the find command
+       my $command = "find \"$base_directory_path\" -type f -iname 
\"$file_pattern\"";
+       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: /i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "error occurred attempting to 
find files on $computer_node_name\nbase directory 
path:\n$base_directory_path\npattern: $file_pattern\ncommand: 
$command\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       
+       # Return the file list
+       return @$output;
 }
 
 #/////////////////////////////////////////////////////////////////////////////

Modified: incubator/vcl/trunk/managementnode/lib/VCL/utils.pm
URL: 
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=950733&r1=950732&r2=950733&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/utils.pm Wed Jun  2 19:26:30 2010
@@ -103,6 +103,7 @@ our @EXPORT = qw(
   delete_computerloadlog_reservation
   delete_request
   disablesshd
+  escape_file_path
   firewall_compare_update
   format_data
   format_number
@@ -158,10 +159,12 @@ our @EXPORT = qw(
   makedatestring
   monitorloading
   nmap_port
+  normalize_file_path
   notify
   notify_via_IM
   notify_via_msg
   notify_via_wall
+  parent_directory_path
   preplogfile
   read_file_to_array
   rename_vcld_process
@@ -5956,7 +5959,7 @@ sub run_scp_command {
                # Check the output for known error messages
                # Check the exit status
                # scp exits with 0 on success or >0 if an error occurred
-               if ($scp_output =~ /permission denied|no such file|ambiguous 
target/i) {
+               if ($scp_output =~ /permission denied|no such file|ambiguous 
target|is a directory/i) {
                        notify($ERRORS{'WARNING'}, 0, "failed to copy file, scp 
error occurred: command: $scp_command, exit status: $scp_exit_status, output: 
$scp_output");
                        return 0;
                }
@@ -9862,6 +9865,94 @@ sub create_management_node_directory {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 normalize_file_path
+
+ Parameters  : $path
+ Returns     : string
+ Description : Normalizes a file or directory path:
+               -spaces from the beginning and end of the path are removed
+                                       -quotes from the beginning and end of 
the path are removed
+                                       -trailing slashes are removed
+                                       -escaped spaces are unescaped
+
+=cut
+
+sub normalize_file_path {
+       # Get the path argument
+       my $path = shift;
+       if (!$path) {
+               notify($ERRORS{'WARNING'}, 0, "path argument was not 
specified");
+               return;
+       }
+       
+       # Remove any spaces and quotes from the beginning and end of the path
+       # Remove any slashes from the end of the path
+       $path =~ s/(^['"\s]*|['"\s\\\/]*$)//g;
+       
+       # Unescape any spaces
+       $path =~ s/\\+(\s)/$1/g;
+       
+       return $path;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 escape_file_path
+
+ Parameters  : $path
+ Returns     : string
+ Description : Escapes special characters in a file or directory path with
+               backslashes:
+               -spaces are escaped
+
+=cut
+
+sub escape_file_path {
+       # Get the path argument
+       my $path = shift;
+       if (!$path) {
+               notify($ERRORS{'WARNING'}, 0, "path argument was not 
specified");
+               return;
+       }
+       
+       $path = normalize_file_path($path);
+       
+       # Add a backslash before each space
+       # Also check for spaces that already have a leading backslash
+       $path =~ s/\\*(\s)/\\$1/g;
+       
+       return $path;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 parent_directory_path
+
+ Parameters  : $path
+ Returns     : string
+ Description : Returns the parent directory path of the path argument. The path
+               returned is normalized.
+
+=cut
+
+sub parent_directory_path {
+       # Get the path argument
+       my $path = shift;
+       if (!$path) {
+               notify($ERRORS{'WARNING'}, 0, "path argument was not 
specified");
+               return;
+       }
+       
+       $path = normalize_file_path($path);
+       
+       # Remove everthing after the last forward or backslash
+       $path =~ s/\/[^\/\\]+$//g;
+       
+       return $path;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 1;
 __END__
 


Reply via email to