Author: arkurth
Date: Tue May  9 22:21:55 2017
New Revision: 1794650

URL: http://svn.apache.org/viewvc?rev=1794650&view=rev
Log:
VCL-915
Added OS.pm::mount_nfs_shares. It is currently only called from 
Linux.pm::reserve but was added to OS.pm so support for Windows can be added in 
the future.

Made minor changes to how arguments are handled in Linux.pm::mount_nfs_share. 
Added $ignore_missing_remote_directory_error argument to allow warnings to be 
suppressed the first time an attempt is made to mount a share.

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

Modified: vcl/trunk/managementnode/lib/VCL/DataStructure.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/DataStructure.pm?rev=1794650&r1=1794649&r2=1794650&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/DataStructure.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/DataStructure.pm Tue May  9 22:21:55 2017
@@ -2630,10 +2630,10 @@ sub substitute_string_variables {
        # Extract all sections of input string which should be replaced
        my @input_substitute_sections = $input_string =~ 
/($variable_identifier_regex)/g;
        if (!@input_substitute_sections) {
-               notify($ERRORS{'DEBUG'}, 0, "input string does not contain any 
sections to replace matching the substitution identifier pattern: 
'$variable_identifier_regex', returning original input string:\n" . 
$input_string);
+               notify($ERRORS{'DEBUG'}, 0, "input string does not contain any 
sections to replace matching the substitution identifier pattern: 
'$variable_identifier_regex', returning original input string: 
'$input_string'");
                return $input_string;
        }
-       notify($ERRORS{'DEBUG'}, 0, "found sections of input string that match 
the substitution identifier pattern '$variable_identifier_regex', input 
string:\n$input_string\n\nmatching sections:\n" . join("\n", 
@input_substitute_sections));
+       #notify($ERRORS{'DEBUG'}, 0, "found sections of input string that match 
the substitution identifier pattern '$variable_identifier_regex', input 
string:\n$input_string\n\nmatching sections:\n" . join("\n", 
@input_substitute_sections));
        
        for my $input_substitute_section 
(remove_array_duplicates(@input_substitute_sections)) {
                # Remove brackets, etc from matching section: '[user_login_id]' 
--> 'user_login_id'
@@ -2678,7 +2678,7 @@ sub substitute_string_variables {
                }
        }
        
-       notify($ERRORS{'OK'}, 0, "replaced all matching sections of input 
string with values retrieved from this DataStructure object:\ninput 
string:\n$input_string\noutput string:\n$output_string");
+       notify($ERRORS{'OK'}, 0, "replaced all matching sections of input 
string with values retrieved from this DataStructure object:\ninput string: 
'$input_string'\noutput string: '$output_string'");
        return $output_string;
 }
 

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS.pm?rev=1794650&r1=1794649&r2=1794650&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS.pm Tue May  9 22:21:55 2017
@@ -52,6 +52,7 @@ use strict;
 use warnings;
 use diagnostics;
 use English '-no_match_vars';
+use File::Temp qw(tempdir);
 use POSIX qw(tmpnam);
 use Net::SSH::Expect;
 use List::Util qw(min max);
@@ -4991,6 +4992,212 @@ sub delete_reservation_info_json_file {
 }
 
 #//////////////////////////////////////////////////////////////////////////////
+
+=head2 mount_nfs_shares
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Checks if a 'nfsmount|<managementnode.id>' variable exists in 
the
+               database. If so, it parses the value and attempts to mount one 
or
+               more shares on the reservation computer. There variable value 
may
+               contain substitution components enclosed in square brackets such
+               as:
+               [user_login_id]
+               
+               See DataStructure.pm::substitute_string_variables for more
+               information.
+               
+               If the last part of a remote directory specification contains a
+               substitution value and the directory could not be mounted on the
+               computer because it doesn't exist, the parent remote directory 
is
+               temporarily mounted on the management node in a subdirectory 
with
+               a random name under /tmp. If successfully mounted, a 
subdirectory
+               with a name containing substituted values based on the
+               reservation data is created. For example, if the nfsmount
+               variable contains:
+               /user_data/share/user-[user_login_id]-[user-uid]
+               
+               A directory should be mounted on the reservation computer named
+               something like:
+               /user_data/share/user-jdoe-3459
+               
+               If the user-jdoe-3459 directory does not exist, this subroutine
+               will:
+               * Attempt to mount /user_data/share on the management node under
+                 /tmp
+               * Create a subdirectory named 'user-jdoe-3459'
+               * If any part of the directory name specification contains a
+                 substitution component that includes '[user_*]', the newly
+                 created directory's owner is set to the user.uid value in the
+                 database and the directory permissions are set to 0700.
+               * The directory is unmounted from the management node.
+               * The directory under /tmp is deleted
+               * The share is mounted on the reservation computer.
+               
+               This subroutine will currently only attempt to automatically
+               create directories if the last component contains a substitution
+               component. Substitution components are allowed in the
+               intermediate directory path such as:
+               /schools/[affiliation_name]/share
+               
+               However, no attempt will be made to create the
+               <[affiliation_name]> directory. It must exist prior to the
+               reservation.
+
+=cut
+
+sub mount_nfs_shares {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my $management_node_id = $self->data->get_management_node_id();
+       my $computer_name = $self->data->get_computer_short_name();;
+       my $user_uid = $self->data->get_user_uid();
+       
+       # Get the NFS mount information configured for the management node from 
the variable table
+       my $nfsmount_variable_name = "nfsmount|$management_node_id";
+       my $nfsmount_variable_value = get_variable($nfsmount_variable_name);
+       if (!$nfsmount_variable_value) {
+               notify($ERRORS{'DEBUG'}, 0, "'$nfsmount_variable_name' variable 
is NOT configured for management node $management_node_id");
+               return 1;
+       }
+       notify($ERRORS{'DEBUG'}, 0, "retrieved '$nfsmount_variable_name' 
variable configured for management node: '$nfsmount_variable_value'");
+       
+       my $error_encountered = 0;
+       
+       MOUNT_SPECIFICATION: for my $mount_specification (split(/;/, 
$nfsmount_variable_value)) {
+               # Format:
+               #    <IP/hostname>:<remote directory>,<local directory>
+               # Example:
+               #    10.0.0.12:/users/home/[username],/home/[username]
+               my ($remote_host, $remote_specification, $local_specification) 
= $mount_specification =~
+                       /
+                               ^\s*
+                               ([^:\s]+)             # $remote_host
+                               \s*:\s*               # :
+                               (\/[^,]*[^,\s\/])\/?  # $remote_specification
+                               \s*,\s*               # ,
+                               (\/.*[^\s\/])\/?      # $local_specification
+                               \s*$
+                       /gx;
+               if (!defined($remote_host) || !defined($remote_specification) 
|| !defined($local_specification)) {
+                       notify($ERRORS{'CRITICAL'}, 0, "failed to parse mount 
specification: '$mount_specification'");
+                       $error_encountered = 1;
+                       next MOUNT_SPECIFICATION;
+               }
+               
+               # Replace variables in local and remote directory paths
+               my $local_substituted = 
$self->data->substitute_string_variables($local_specification);
+               my $remote_substituted = 
$self->data->substitute_string_variables($remote_specification);
+               my $remote_target = "$remote_host:$remote_substituted";
+               
+               notify($ERRORS{'DEBUG'}, 0, "parsed mount definition: 
'$mount_specification'\n" .
+                       "remote storage target : $remote_target" . 
($remote_specification ne $remote_substituted ? " (specification: 
$remote_specification)" : '') . "\n" .
+                       "local mount directory : $local_substituted " . 
($local_specification ne $local_substituted ? " (specification: 
$local_specification)" : '')
+               );
+               
+               # Specify ignore error option to prevent warnings on first 
attempt
+               my $mount_result = $self->mount_nfs_share($remote_target, 
$local_substituted, 1);
+               # If successful or failed and returned undefined, stop 
processing this share
+               if (!defined($mount_result)) {
+                       # Unrepairable error encountered
+                       $error_encountered = 1;
+                       next MOUNT_SPECIFICATION;
+               }
+               elsif ($mount_result == 1) {
+                       # Successfully mounted share
+                       next MOUNT_SPECIFICATION;
+               }
+               
+               # mount_nfs_share() returned 0 indicating the remote directory 
does not exist
+               notify($ERRORS{'OK'}, 0, "unable to mount $remote_target on 
$computer_name on first attempt, checking if directories need to be created");
+               
+               # Get the last component of the remote directory specification 
following the last forward slash
+               my ($remote_directory_name_specification) = 
$remote_specification =~
+                       /
+                               \/
+                               (
+                                       [^
+                                               \/
+                                       ]+
+                               )
+                               $
+                       /gx;
+               if (!$remote_directory_name_specification) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to mount share on 
$computer_name: $remote_target --> $local_substituted, no attempt made to 
create user/reservation-specific directory on share because the remote 
directory name specification could not be determined: $remote_specification");
+                       return;
+               }
+               elsif ($remote_directory_name_specification !~
+                       /
+                               \[
+                               [^
+                                       \]
+                               ]+
+                               \]
+                       /gx) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to mount share on 
$computer_name: $remote_target --> $local_substituted, no attempt made to 
create user/reservation-specific directory on share because the remote 
directory name specification does not contain a substitution value: 
$remote_directory_name_specification");
+                       return;
+               }
+               
+               # Get the remote directory name and its parent directory path
+               my ($remote_parent_directory_path, $remote_directory_name) = 
$remote_substituted =~
+                       /
+                               ^
+                               (
+                                       \/.+
+                               )
+                               \/
+                               (
+                                       [^
+                                               \/
+                                       ]+
+                               )
+                               $
+                       /gx;
+               if (!defined($remote_directory_name)) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to mount share on 
$computer_name: $remote_target --> $local_substituted, no attempt made to 
create user/reservation-specific directory on share because the remote 
directory name and its parent directory path could not be determined: 
$remote_substituted");
+                       return;
+               }
+               
+               notify($ERRORS{'DEBUG'}, 0, "attempting to create 
user/reservation-specific directory on share, remote directory name 
specification contains a substitution value: 
$remote_directory_name_specification --> $remote_directory_name");
+               
+               # Attempt to mount the remote parent directory on the 
management node
+               my $mn_temp_remote_target = 
"$remote_host:$remote_parent_directory_path";
+               my $mn_temp_mount_directory_path = tempdir(CLEANUP => 1);
+               my $mn_temp_create_directory_path = 
"$mn_temp_mount_directory_path/$remote_directory_name";
+               if (!$self->mn_os->mount_nfs_share($mn_temp_remote_target, 
$mn_temp_mount_directory_path)) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to mount share on 
$computer_name: $remote_target --> $local_substituted, failed to temporarily 
mount remote parent directory share on management node: $mn_temp_remote_target 
--> $mn_temp_mount_directory_path");
+                       return;
+               }
+               
+               # Try to create the directory containing the substitution value
+               if 
(!$self->mn_os->create_directory($mn_temp_create_directory_path)) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to mount share on 
$computer_name: $remote_target --> $local_substituted, mounted temporary remote 
parent directory share on management node ($mn_temp_remote_target) but failed 
to create '$remote_directory_name' subdirectory under it");
+                       
$self->mn_os->unmount_nfs_share($mn_temp_mount_directory_path);
+                       return;
+               }
+               
+               # Check if the directory name contains a substitution for 
user-specific data
+               if ($remote_directory_name_specification =~ /\[user/) {
+                       
$self->mn_os->set_file_owner($mn_temp_create_directory_path, $user_uid);
+                       
+                       # Set the permissions on the directory to 700 so other 
users can't read contents
+                       
$self->mn_os->set_file_permissions($mn_temp_create_directory_path, '700');
+               }
+               
+               
+               $self->mn_os->unmount_nfs_share($mn_temp_mount_directory_path);
+               
+               # Try to mount the share on the computer again
+               $self->mount_nfs_share($remote_target, $local_substituted) || 
$error_encountered++;
+       }
+       return !$error_encountered
+}
+
+#//////////////////////////////////////////////////////////////////////////////
 1;
 __END__
 

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=1794650&r1=1794649&r2=1794650&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Tue May  9 22:21:55 2017
@@ -1227,7 +1227,7 @@ sub logoff_user {
  Parameters  : none
  Returns     : boolean
  Description : Performs the steps necessary to reserve a computer for a user.
-               A "vcl" user group is added to the.
+               A "vcl" user group is added.
 
 =cut
 
@@ -1240,23 +1240,22 @@ sub reserve {
        
        notify($ERRORS{'OK'}, 0, "beginning Linux reserve tasks");
        
-       my $computer_node_name = $self->data->get_computer_node_name();
+       # Add a local vcl user group if it doesn't already exist
+       # Do this before OS.pm::reserve calls add_user_accounts
+       $self->add_vcl_usergroup();
+       
+       $self->SUPER::reserve() || return;
        
        # Configure sshd to only listen on the private interface and add 
ext_sshd service listening on the public interface
-       # This needs to be done after update_public_ip_address is called
-       if (!$self->configure_ext_sshd()) {
-               notify($ERRORS{'WARNING'}, 0, "failed to configure ext_sshd on 
$computer_node_name");
-               return 0;
-       }
+       # This needs to be done after update_public_ip_address is called from 
OS.pm::reserve
+       $self->configure_ext_sshd() || return
        
-       if (!$self->add_vcl_usergroup()) {
-               notify($ERRORS{'WARNING'}, 0, "failed to add vcl user group to 
$computer_node_name");
-       }
+       # Attempt to mount NFS shares configured for the management node (Site 
Configuration > NFS Mounts)
+       $self->mount_nfs_shares();
        
-       notify($ERRORS{'OK'}, 0, "Linux reserve tasks complete");
        
-       # Call OS.pm's reserve subroutine
-       return unless $self->SUPER::reserve();
+       notify($ERRORS{'OK'}, 0, "Linux reserve tasks complete");
+       return 1;
 } ## end sub reserve
 
 #//////////////////////////////////////////////////////////////////////////////
@@ -1807,7 +1806,7 @@ sub clear_file {
 
 =head2 create_directory
 
- Parameters  : $directory_path, $mode (optional)
+ Parameters  : $directory_path
  Returns     : boolean
  Description : Creates a directory on the Linux computer as indicated by the
                $directory_path argument.
@@ -6059,7 +6058,7 @@ sub mount_nfs_share {
                return;
        }
        
-       my ($remote_nfs_share, $local_mount_directory, $options, 
$is_retry_attempt) = @_;
+       my ($remote_nfs_share, $local_mount_directory, 
$ignore_missing_remote_directory_error, $nfs_options, $is_retry_attempt) = @_;
        if (!defined($remote_nfs_share)) {
                notify($ERRORS{'WARNING'}, 0, "remote target argument was not 
supplied");
                return;
@@ -6109,8 +6108,8 @@ sub mount_nfs_share {
        }
        
        my $mount_command = "mount -t nfs $remote_nfs_share 
\"$local_mount_directory\" -v";
-       if ($options) {
-               $mount_command .= " -o $options";
+       if ($nfs_options) {
+               $mount_command .= " -o $nfs_options";
        }
        
        notify($ERRORS{'DEBUG'}, 0, "attempting to mount NFS share on 
$computer_name: $mount_command");
@@ -6120,7 +6119,7 @@ sub mount_nfs_share {
                max_attempts => 2,
        });
        if (!defined($mount_exit_status)) {
-               notify($ERRORS{'CRITICAL'}, 0, "failed to execute command to 
mount NFS share on $computer_name: $mount_command");
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
mount NFS share on $computer_name: $mount_command");
                return;
        }
        elsif ($mount_exit_status eq 0) {
@@ -6137,6 +6136,17 @@ sub mount_nfs_share {
                        return;
                }
        }
+       elsif (grep(/(No such file or directory)/, @$mount_output)) {
+               # mount.nfs: mount(2): No such file or directory
+               # mount.nfs: mounting <hostname>:/<remote directory> failed, 
reason given by server: No such file or directory
+               if ($ignore_missing_remote_directory_error) {
+                       notify($ERRORS{'DEBUG'}, 0, "unable to mount NFS share 
on $computer_name because remote directory does not exist: $remote_nfs_share, 
returning 0");
+               }
+               else {
+                       notify($ERRORS{'WARNING'}, 0, "unable to mount NFS 
share on $computer_name because remote directory does not exist: 
$remote_nfs_share, returning 0, command: '$mount_command', exit status: 
$mount_exit_status, output:\n" . join("\n", @$mount_output));
+               }
+               return 0;
+       }
        elsif (grep(/(Invalid argument|incorrect mount option|Usage:)/, 
@$mount_output)) {
                notify($ERRORS{'WARNING'}, 0, "failed to mount NFS share on 
$computer_name: $remote_nfs_share --> $local_mount_directory, command: 
'$mount_command', exit status: $mount_exit_status, output:\n" . join("\n", 
@$mount_output));
                return;
@@ -6150,7 +6160,7 @@ sub mount_nfs_share {
                        notify($ERRORS{'WARNING'}, 0, "failed to mount NFS 
share on $computer_name on 1st attempt: $remote_nfs_share --> 
$local_mount_directory, command: '$mount_command', exit status: 
$mount_exit_status, output:\n" . join("\n", @$mount_output));
                        
                        # Try to mount the NFS share again, set retry flag to 
avoid endless loop
-                       return $self->mount_nfs_share($remote_nfs_share, 
$local_mount_directory, $options, 1);
+                       return $self->mount_nfs_share($remote_nfs_share, 
$local_mount_directory, $ignore_missing_remote_directory_error, $nfs_options, 
1);
                }
        }
 }


Reply via email to