sub nfs_mount_share()

in managementnode/lib/VCL/Module/OS/Linux.pm [6202:6346]


sub nfs_mount_share {
	my $self = shift;
	if (ref($self) !~ /linux/i) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return;
	}
	
	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;
	}
	elsif (!defined($local_mount_directory)) {
		notify($ERRORS{'WARNING'}, 0, "local directory path argument was not supplied");
		return;
	}
	
	my $computer_name = $self->data->get_computer_node_name();
	
	# Try to repair NFS client if 1st attempt failed
	if ($is_retry_attempt) {
		# Check if nfs-utils is installed, if not, try to install it
		# Error looks like this if nfs-utils is not installed:
		#    mount: wrong fs type, bad option, bad superblock on 10.1.2.3:/nfs,
		#    missing codepage or helper program, or other error
		#    (for several filesystems (e.g. nfs, cifs) you might
		#    need a /sbin/mount.<type> helper program)
		#    In some cases useful info is found in syslog - try
		#    dmesg | tail  or so
		if (!$self->command_exists('mount.nfs')) {
			if (ref($self) =~ /Ubuntu/) {
				$self->install_package('nfs-common');
			}
			else {
				$self->install_package('nfs-utils');
			}
		}
		
		# Check if the rpcbind service exists, if not, try to install it
		# Mount may fail if rpcbind service is not installed and running:
		#    mount.nfs: rpc.statd is not running but is required for remote locking.
		#    mount.nfs: Either use '-o nolock' to keep locks local, or start statd.
		#    mount.nfs: an incorrect mount option was specified
		$self->install_package('rpcbind') if !$self->service_exists('rpcbind');
		
		# Try to start the service
		$self->start_service('rpcbind') if $self->service_exists('rpcbind');
	}
	
	# Create the local mount point directory if it does not exist
	my $local_mount_directory_previously_existed = $self->file_exists($local_mount_directory);
	if (!$local_mount_directory_previously_existed && !$self->create_directory($local_mount_directory)) {
		notify($ERRORS{'WARNING'}, 0, "unable to mount $remote_nfs_share on $computer_name, failed to create directory: $local_mount_directory");
		return;
	}
	
	my $mount_command = "mount -t nfs $remote_nfs_share \"$local_mount_directory\" -v";
	if ($nfs_options) {
		# Add retry=0 if it wasn't explicitly specified in the argument
		if ($nfs_options =~ /retry/) {
			$mount_command .= " -o $nfs_options";
		}
		else {
			$mount_command .= " -o retry=0,$nfs_options";
		}
	}
	else {
		$mount_command .= " -o retry=0";
	}
	
	# Save return value if error is encountered and don't return immediately
	# Facilitates a single call to clean up local directory just created if it didn't previously exist
	my $return_value;
	
	notify($ERRORS{'DEBUG'}, 0, "attempting to mount NFS share on $computer_name: $mount_command");
	my ($mount_exit_status, $mount_output) = $self->execute({
		command => $mount_command,
		timeout_seconds => 10,
		max_attempts => 2,
	});
	if (!defined($mount_exit_status)) {
		notify($ERRORS{'WARNING'}, 0, "failed to execute command to mount NFS share on $computer_name: $mount_command");
		$return_value = undef;
	}
	elsif ($mount_exit_status eq 0) {
		notify($ERRORS{'OK'}, 0, "mounted NFS share on $computer_name: $remote_nfs_share --> $local_mount_directory");
		
		# Add the share to /etc/fstab
		$self->add_fstab_nfs_mount($remote_nfs_share, $local_mount_directory);
		
		return 1;
	}
	elsif (grep(/already mounted/, @$mount_output)) {
		# mount.nfs: /mnt/mymountpoint is busy or already mounted
		if ($self->is_nfs_share_mounted($remote_nfs_share, $local_mount_directory)) {
			return 1;
		}
		else {
			notify($ERRORS{'WARNING'}, 0, "failed to mount NFS share on $computer_name: $remote_nfs_share --> $local_mount_directory, mount command output indicates 'already mounted' but failed to verify mount in /proc/mounts, mount command: '$mount_command', exit status: $mount_exit_status, mount output:\n" . join("\n", @$mount_output));
			$return_value = undef;
		}
	}
	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_value = 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_value = undef;
	}
	elsif ($is_retry_attempt) {
		notify($ERRORS{'WARNING'}, 0, "failed to mount NFS share on $computer_name on 2nd attempt: $remote_nfs_share --> $local_mount_directory, command: '$mount_command', exit status: $mount_exit_status, output:\n" . join("\n", @$mount_output));
		$return_value = undef;
	}
	else {
		notify($ERRORS{'OK'}, 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));
	}
	
	# Clean up local directory if it didn't previously exist
	if (!$local_mount_directory_previously_existed) {
		my @local_mount_directory_files = $self->find_files($local_mount_directory, '*', 1);
		if (@local_mount_directory_files) {
			notify($ERRORS{'WARNING'}, 0, "local mount directory just created will NOT be deleted: $local_mount_directory, NFS mount seemed to have failed but directory is not empty:\n" . join("\n", @local_mount_directory_files));
		}
		else {
			notify($ERRORS{'DEBUG'}, 0, "local mount directory just created will be deleted: $local_mount_directory");
			$self->delete_file($local_mount_directory);
		}
	}
	
	if ($is_retry_attempt) {
		return $return_value;
	}
	else {
		# Try to mount the NFS share again, set retry flag to avoid endless loop
		return $self->nfs_mount_share($remote_nfs_share, $local_mount_directory, $ignore_missing_remote_directory_error, $nfs_options, 1);
	}
}