This is an automated email from the ASF dual-hosted git repository. jfthomps pushed a commit to branch VCL-1096_Window_NFS_shares in repository https://gitbox.apache.org/repos/asf/vcl.git
The following commit(s) were added to refs/heads/VCL-1096_Window_NFS_shares by this push: new 3dffb95 VCL-1096 - Add ability to automatically mount NFS shares under Windows 3dffb95 is described below commit 3dffb95ed3d45b096585d9b147f2878b7d13b47f Author: Josh Thompson <jftho...@ncsu.edu> AuthorDate: Wed Jan 30 16:04:33 2019 -0500 VCL-1096 - Add ability to automatically mount NFS shares under Windows Windows.pm: -modified pre_capture: added call to $self->unmount_nfs_shares at end of function to removing mounting of any NFS shares at next user login -modified sanitize: added call to $self->unmount_nfs_shares at end of function to remove code that would run at user login to do NFS mounts -added reserve function that calls parent reserve function and then calls $self->mount_nfs_shares -added nfs_mount_share -added nfs_unmount_share -added set_windows_nfs_client_uid --- managementnode/lib/VCL/Module/OS/Windows.pm | 296 +++++++++++++++++++++++++++- 1 file changed, 294 insertions(+), 2 deletions(-) diff --git a/managementnode/lib/VCL/Module/OS/Windows.pm b/managementnode/lib/VCL/Module/OS/Windows.pm index 52f7b19..479fc22 100644 --- a/managementnode/lib/VCL/Module/OS/Windows.pm +++ b/managementnode/lib/VCL/Module/OS/Windows.pm @@ -677,14 +677,27 @@ sub pre_capture { notify($ERRORS{'WARNING'}, 0, "unable to set sshd service startup mode to manual"); return 0; } - + +=item * + + Unmount any NFS shares + +=cut + + if (!$self->unmount_nfs_shares()) { + notify($ERRORS{'WARNING'}, 0, "unable to unmount NFS shares"); + return; + } + else { + notify($ERRORS{'DEBUG'}, 0, "Call to unmount_nfs_shares returned successfully"); + } + =back =cut notify($ERRORS{'OK'}, 0, "returning 1"); return 1; - } ## end sub pre_capture #////////////////////////////////////////////////////////////////////////////// @@ -1178,6 +1191,14 @@ sub sanitize { return 0; } + if (!$self->unmount_nfs_shares()) { + notify($ERRORS{'WARNING'}, 0, "failed to unmount nfs shares"); + return 0; + } + else { + notify($ERRORS{'DEBUG'}, 0, "successfully unmounted any NFS shares"); + } + notify($ERRORS{'OK'}, 0, "$computer_node_name has been sanitized"); return 1; } ## end sub sanitize @@ -14912,6 +14933,277 @@ sub _escape_password { #////////////////////////////////////////////////////////////////////////////// +=head2 reserve + + Parameters : none + Returns : boolean + Description : Calls parent OS.pm::reserve then mounts NFS shares. + +=cut + +sub reserve { + my $self = shift; + if (ref($self) !~ /Windows/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return 0; + } + + # Call OS.pm's reserve subroutine + $self->SUPER::reserve() || return; + + notify($ERRORS{'OK'}, 0, "beginning Windows reserve tasks"); + + # Attempt to mount NFS shares configured for the management node (Site Configuration > NFS Mounts) + $self->mount_nfs_shares(); + + notify($ERRORS{'OK'}, 0, "Windows reserve tasks complete"); + return 1; +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 nfs_mount_share + + Parameters : $remote_target + Returns : boolean + Description : Prepares a Windows image to automatically mount a user's NFS + shares when the user logs in. + + The Windows image is checked to determine if the required Windows + features are installed in the image. If not installed, an attempt + is made to install them. If any features are installed, a reboot + is performed. + +=cut + +sub nfs_mount_share { + my $self = shift; + if (ref($self) !~ /Windows/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + my ($remote_target) = @_; + if (!$remote_target) { + notify($ERRORS{'WARNING'}, 0, "remote NFS target argument was not supplied"); + return; + } + + # This subroutine may be called multiple times to allow multiple shares to be mounted + # If this is not the first call, append to the batch file instead of recreating it + my $previously_called = $self->{nfs_mount_share_called}; + if ($previously_called && !$self->{nfs_mount_share_preparation_complete}) { + notify($ERRORS{'DEBUG'}, 0, "skipping subsequent NFS mount attempt, this subroutine was previously called but the preparation tasks failed to complete"); + return; + } + + $self->{nfs_mount_share_called} = 1; + + my $username = $self->data->get_user_login_id(); + my $uid = $self->data->get_user_uid(); + my $reservation_id = $self->data->get_reservation_id(); + my $computer_name = $self->data->get_computer_node_name(); + my $management_node_id = $self->data->get_management_node_id; + + my $nfs_mount_batch_file_path = 'C:/mount_nfs.cmd'; + + # Only perform the prep tasks the first time this subroutine is called + if ($previously_called) { + notify($ERRORS{'DEBUG'}, 0, "Windows NFS preparation tasks not necessary, this subroutine was previously executed"); + } + else { + notify($ERRORS{'DEBUG'}, 0, "beginning Windows NFS preparation tasks"); + + $self->delete_file($nfs_mount_batch_file_path); + + my $windows_product_name = $self->get_product_name(); + if ($windows_product_name =~ /(Windows.*Professional)/) { + notify($ERRORS{'WARNING'}, 0, "Windows NFS share not mounted, Client for NFS is not available on this edition: $windows_product_name"); + return 0; + } + + # Check if required Windows features are installed in the image + # Attempt to install them if missing + # A reboot is required if any features are installed + my @required_windows_feature_names = ( + 'ServicesForNFS-ClientOnly', + 'ClientForNFS-Infrastructure', + ); + + my $installed_feature_count = 0; + for my $required_windows_feature_name (@required_windows_feature_names) { + #notify($ERRORS{'DEBUG'}, 0, "sleeping 10 seconds before installing next Windows feature"); + #sleep_uninterrupted(10) if $installed_feature_count > 0; + + if (!$self->is_windows_feature_enabled($required_windows_feature_name)) { + if ($self->enable_windows_feature($required_windows_feature_name)) { + $self->{windows_nfs_reboot_required} = 1; + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to prepare Windows NFS, '$required_windows_feature_name' Windows feature could not be enabled"); + return; + } + } + $installed_feature_count++; + } + + # Stop and disable the "TCP/IP NetBIOS Helper" service - it causes delays using NFS shares + $self->stop_service('lmhosts'); + $self->set_service_startup_mode('lmhosts', 'disabled'); + + # Set the anonymous UID + $self->set_windows_nfs_client_uid($uid) || return; + + #if ($self->{windows_nfs_reboot_required}) { + # notify($ERRORS{'DEBUG'}, 0, "attempting to reboot $computer_name to complete the configuration of the NFS Client"); + # if (!$self->reboot()) { + # notify($ERRORS{'WARNING'}, 0, "failed to prepare Windows NFS, failed to reboot $computer_name after configuring NFS client"); + # return; + # } + #} + + # Attempt to start the NFS client or make sure it's already started + $self->start_service('NfsClnt') || return; + + #if (!$self->start_service('NfsClnt')) { + # if ($self->{windows_nfs_reboot_required}) { + # notify($ERRORS{'WARNING'}, 0, "failed to prepare Windows NFS, failed to start the NFS client, $computer_name was already rebooted"); + # return; + # } + # else { + # if (!$self->reboot()) { + # notify($ERRORS{'WARNING'}, 0, "failed to prepare Windows NFS, failed to reboot $computer_name after attempting to start the NFS client"); + # return; + # } + # + # if (!$self->start_service('NfsClnt')) { + # notify($ERRORS{'WARNING'}, 0, "failed to prepare Windows NFS, failed to start the NFS client on $computer_name after it was rebooted"); + # return; + # } + # } + #} + #notify($ERRORS{'DEBUG'}, 0, "Windows NFS preparation tasks complete"); + + $self->{nfs_mount_share_preparation_complete} = 1; + } + + my $mount_script_contents; + if (!$previously_called) { + $mount_script_contents = "nfsadmin.exe client start\r\n"; + } + $mount_script_contents .= "C:\\Windows\\system32\\mount.exe -o mtype=hard $remote_target *"; + + if ($previously_called) { + notify($ERRORS{'DEBUG'}, 0, "appending line to $nfs_mount_batch_file_path: $mount_script_contents"); + if (!$self->append_text_file($nfs_mount_batch_file_path, $mount_script_contents)) { + notify($ERRORS{'WARNING'}, 0, "failed to append batch file to mount NFS shares: $nfs_mount_batch_file_path"); + return; + } + } + else { + notify($ERRORS{'DEBUG'}, 0, "creating Windows NFS mount batch file: $nfs_mount_batch_file_path, contents:\n$mount_script_contents"); + if (!$self->create_text_file($nfs_mount_batch_file_path, $mount_script_contents)) { + notify($ERRORS{'WARNING'}, 0, "failed to create Windows NFS mount batch file: $nfs_mount_batch_file_path"); + return; + } + $self->add_hklm_run_registry_key('VCL Mount NFS', $nfs_mount_batch_file_path) || return; + $self->execute("chmod 777 $nfs_mount_batch_file_path"); + } + + notify($ERRORS{'DEBUG'}, 0, "finished Windows NFS mount tasks"); + return 1; +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 nfs_unmount_share + + Parameters : none + Returns : boolean + Description : This doesn't actually unmount any shares but deletes the registry + key and batch file created to mount NFS shares when a user logs + in. + +=cut + +sub nfs_unmount_share { + my $self = shift; + if (ref($self) !~ /Windows/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + $self->delete_file('C:/mount_nfs.cmd'); + $self->delete_hklm_run_registry_key('VCL Mount NFS'); + return 1; +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 set_windows_nfs_client_uid + + Parameters : $uid_number + Returns : boolean + Description : Sets registry values used by the Windows NFS Client to determine + which user UID number to use when accessing NFS shares. + +=cut + +sub set_windows_nfs_client_uid { + my $self = shift; + if (ref($self) !~ /Windows/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + my $uid = shift || $self->data->get_user_uid(); + my $computer_name = $self->data->get_computer_node_name(); + + notify($ERRORS{'DEBUG'}, 0, "attempting to configure the Windows NFS Client to use anonymous UID $uid"); + + my $nfs_client_service_name = 'NfsClnt'; + my $base_key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default'; + my $uid_key = 'AnonymousUid'; + + # Check if UID already set correctly + my $existing_uid = $self->reg_query($base_key, $uid_key, 1) || ''; + if ($existing_uid eq $uid) { + notify($ERRORS{'DEBUG'}, 0, "anonymous UID ($existing_uid) value are already set correctly in the registry on $computer_name"); + } + else { + # Set the registry keys + if (!$self->reg_add($base_key, $uid_key, 'REG_DWORD', $uid)) { + notify($ERRORS{'WARNING'}, 0, "failed to set Windows NFS Client anonymous UID in the registry"); + return; + } + } + + # Stop the NFS service, this may fail if something is already mounted + if (!$self->{windows_nfs_reboot_required}) { + if ($self->stop_service($nfs_client_service_name)) { + ## Service won't restart immediately after stopping it + #sleep_uninterrupted(10); + + if ($self->start_service($nfs_client_service_name)) { + notify($ERRORS{'DEBUG'}, 0, "set Windows NFS Client anonymous UID value in registry and restarted $nfs_client_service_name service"); + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to restart Windows NFS Client service, it may be in use, $computer_name will be rebooted"); + $self->{windows_nfs_reboot_required} = 1; + } + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to stop Windows NFS Client service, it may be in use, $computer_name will be rebooted"); + $self->{windows_nfs_reboot_required} = 1; + } + } + + notify($ERRORS{'DEBUG'}, 0, "set Windows NFS Client anonymous UID values in registry"); + return 1; +} + +#////////////////////////////////////////////////////////////////////////////// + 1; __END__