You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by jf...@apache.org on 2019/01/30 21:09:12 UTC

[vcl] branch VCL-1096_Window_NFS_shares updated: VCL-1096 - Add ability to automatically mount NFS shares under Windows

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 <jf...@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__