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__