MINI MINI MANI MO
#
#
# osds_unix_linux_acfslib.pm
#
# Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
#
#
# NAME
# osds_unix_linux_acfslib.pm - Linux Unix OSD library components.
#
# DESCRIPTION
# Purpose
# Linux Unix OSD library functions for the install/runtime scripts.
#
# NOTES
# All user visible output should be done in the common code.
# this will ensure a consistent look and feel across all platforms.
#
#
use strict;
use Cwd;
use File::Path;
use acfslib;
package osds_unix_linux_acfslib;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(
lib_osds_krncmp
lib_osds_am_root
lib_osds_control_devices_accessible
lib_osds_create_mount_point
lib_osds_device_from_mountpoint
lib_osds_get_advm_mounts
lib_osds_get_asm_user
lib_osds_get_drive_info
lib_osds_mount
lib_osds_mountpoint_descriptors
lib_osds_run_as_user
lib_osds_unmount
lib_osds_usm_supported
lib_osds_verify_usm_devices
lib_osds_is_db_home
lib_osds_is_abs_path
lib_osds_are_same_file
get_dev_mntpt_from_mount_line
$ACFSUTIL
$ADVMUTIL
$TMPDIR
$REDIRECT
AVD_DIR
AVD_CTL_DEV
OFS_CTL_DEV
AVD_IDX
OFS_IDX
OKS_IDX
OPT_CHR
USM_FAIL
USM_SUCCESS
USM_SUPPORTED
USM_NOT_SUPPORTED
USM_REBOOT_RECOMMENDED
USM_TRANSIENT_FAIL
lib_check_nfs_path
lib_install_nfs_path
lib_uninstall_nfs_path
);
use Config;
my ($OSNAME) = $Config{osname};
chomp($OSNAME);
my ($UNAME_A) = `uname -a`;
chomp($UNAME_A);
# return/exit codes
#
# USM_TRANSIENT_FAILures are those that can be easily filed by the admin.
# In the case of "acfsroot install", the admin could fix the error and then
# resume a grid install, for example, from the checkpoint.
use constant USM_SUCCESS => 0;
use constant USM_FAIL => 1;
use constant USM_NOT_SUPPORTED => 2;
use constant USM_REBOOT_RECOMMENDED => 3;
use constant USM_TRANSIENT_FAIL => 1; # failures that can be easily fixed
use constant USM_SUPPORTED => 5;
use constant OPT_CHR => "-"; # Linux/Unix option character
use constant AVD_DIR => "/dev/asm"; # created when AVD loaded
use constant AVD_CTL_DEV => "/dev/asm/.asm_ctl_spec"; # created when AVD loaded
use constant OFS_CTL_DEV => "/dev/ofsctl"; # created when OFS loaded
use constant AVD_IDX => 0; # index into driver_ccomponents
use constant OKS_IDX => 1; # index into driver_ccomponents
use constant OFS_IDX => 2; # index into driver_ccomponents
# On Windows, in the CRS environment, this
# redirection does not work. It fails with
# "cannot open file descriptor" or "cannot open pipe NOWAIT".
our ($REDIRECT) = "2>&1";
my ($CACHED_INSTALLED_DRIVERS); # saved find /lib/modules output
my ($CACHED_LOADED_DRIVERS); # saved find lsmod output
our ($ACFSUTIL) = "/sbin/acfsutil"; # Linux
$ACFSUTIL = "/sbin/acfsutil" if ($OSNAME eq "aix");
$ACFSUTIL = "/usr/lib/fs/acfs/acfsutil" if ($OSNAME eq "solaris");
our ($ADVMUTIL) = "/sbin/advmutil";
# /sbin/fuser ... RH Enterprise Linux
# /bin/fuser ... Suse
# /usr/sbin/fuser ... AIX, HPUX, Solaris
# /usr/sbin/lsof ... RH Enterprise Linux
# /sbin/modprobe ... RH Enterprise Linux
my $ORACLE_HOME = $ENV{ORACLE_HOME};
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin:" . $ORACLE_HOME;
if (! -e $ACFSUTIL)
{
# Not installed yet.
$ACFSUTIL = "$ORACLE_HOME/usm/install/cmds/bin/acfsutil";
}
our ($TMPDIR) = "/tmp";
#
# HANFS Constants
#
use constant FALSE => 0;
use constant TRUE => 1;
my ($hostname) = $ENV{HOSTNAME};
my ($SBIN_DIR) = "/sbin";
my ($USR_SBIN_DIR)= "/usr/sbin";
my ($MKFS) = "$SBIN_DIR/mkfs";
my ($MKFS_ACFS) = "$SBIN_DIR/mkfs.acfs";
# lib_osds_am_root
#
# verify root access
#
sub lib_osds_am_root
{
if ($>) # get euid
{
# not zero (root)
# If this is AIX, let the grid user in.
if ($OSNAME eq "aix ")
{
my $tusr=`/usr/bin/grep ORACLE_OWNER $ORACLE_HOME/crs/install/crsconfig_params|/usr/bin/cut -f2 -d '='`;
my $me=`/bin/id -un`;
$tusr =~ s/\s+$//;
$me =~ s/\s+$//;
if ($me eq $tusr)
{
return 1;
} else
{
return 0;
}
}
return 0;
}
return 1;
} # end lib_osds_am_root
# lib_osds_mount
#
# Mount the specified file system
#
sub lib_osds_mount
{
my ($device, $mount_point, $options) = @_;
my ($asmadmin);
my ($result);
my ($fs_switch);
if ($OSNAME eq "linux")
{
$fs_switch = "-t";
}
elsif ($OSNAME eq "solaris")
{
$fs_switch = "-F";
}
elsif ($OSNAME eq "aix")
{
$fs_switch = "-v";
}
else
{
# should never get here if lib_osds_usm_supported() did its job.
lib_error_print(9125,
"ADVM/ACFS is not supported on this OS: '%s'", $OSNAME);
return USM_FAIL;
}
if ($options ne "none")
{
$options = "-o " . $options;
}
else
{
$options = "";
}
$result = system("mount $fs_switch acfs $options $device $mount_point");
if ($result)
{
return USM_FAIL;
}
# set mount point attributes if the defaults have not been changed.
my ($dev,$ino,$mode,$nlink,$uid,$gid) = stat($mount_point);
if (($mode == 040755) && ($uid == 0) && ($gid == 0))
{
# The user has not changed the defaults.
$asmadmin = acfslib::lib_get_asm_admin_name();
system ("chmod 0770 $mount_point");
system ("chgrp $asmadmin $mount_point");
}
# If the mount point is to be exported, do it here.
# This will be a no-op if the mount point is not in /etc/exports
acfslib::lib_osds_exportfs($mount_point);
return USM_SUCCESS;
} # end lib_osds_mount
# lib_osds_mountpoint_descriptors
#
# called with action = 1 when the user calls "clean" for force any open file
# references to be cleared from a mount point to ensure that the unmount
# will succeed.
#
# called with action = 0 to print open references on the mount point.
#
sub lib_osds_mountpoint_descriptors
{
my ($mountpoint, $action) = @_;
my ($fuser) = "fuser";
my ($have_fuser) = 1;
my ($lsof) = "lsof";
my ($have_lsof) = 1;
my ($str); # temp work space
my ($descriptor_list) = ""; # returned if action == 0
my ($pid) = "";
my ($retval) = USM_SUCCESS;
my ($busyCmd);
my @CLEAN_DO_NOT_KILL_LIST = (
'^/\S*/acfs_script.sh',
'^asm_.*\+ASM\d$',
'^\[asmError0]$',
'^\[asmIntervalTime]$',
'^\[asmShutdown]$',
'^/\S*/crsd',
'^/\S*/cssdmonitor',
'^/\S*/diskmon',
'^/\S*/evmlogger',
'^/\S*/evmd',
'^/\S*/gipcd',
'^/\S*/gpnpd',
'^/\S*/mdnsd',
'^/\S*/ocssd',
'^/\S*/octssd',
'^/\S*/ohasd',
'^/\S*/ologgerd',
'^/\S*/ons',
'^/\S*/oraagent',
'^/\S*/orarootagent',
'^/\S*/osysmond',
'^/\S*/tnslsnr',
);
# All systems are "supposed" to have fuser (LSB_Core), but who really
# knows. Either fuser or lsof can do the job. Let's see what we have.
open (WHICH, "which $fuser 2>&1 |");
$str = <WHICH>;
close (WHICH);
if ($str =~ /no $fuser in/)
{
$have_fuser = 0;
}
open (WHICH, "which $lsof 2>&1 |");
$str = <WHICH>;
close (WHICH);
if ($str =~ /no $lsof in/)
{
$have_lsof = 0;
}
# for debugging, uncomment the appropriate line below.
# $have_fuser = 1; $have_lsof = 0;
# $have_fuser = 0; $have_lsof = 1;
if (($have_fuser == 0) && ($have_lsof == 0))
{
print "This system has neither fuser nor lsof in its command search path\n";
if ($action)
{
return USM_FAIL;
}
else
{
return $descriptor_list;
}
}
#
# Search for processes with open files on the target mount point
# using fuser. If found, list or kill them. fuser should be on
# most operating systems.
#
if ($have_fuser)
{
# If the mountpoint got unmounted before we run fuser, we could get all
# pids on the system. To avoid this, we run lib_osds_is_mounted() after
# fuser completes to ensure that the data is still valid. We used to call
# lib_osds_is_mounted() and then fuser - which opened a race condition.
#
# If fuser gets an error, it will appear on a separate line so we need
# to handle that. For example:
# Cannot stat file /proc/6318/fd/6: Stale NFS file handle
# /mnt1: 14134
#
# You won't believe this but on the line:
# /mnt1: 14134
# "/mnt1: goes to STDERR and "14134" to STDOUT. So, we can't do
# something like "open (FUSER, "$fuser -c $mountpoint 2> /dev/null |");".
if ($OSNAME eq "linux")
{
# Only linux has the -a flag to fuser.
open (FUSER, "$fuser -ac $mountpoint 2>&1 |");
}
else
{
open (FUSER, "$fuser -c $mountpoint 2>&1 |");
}
my ($fuser_line);
while ($fuser_line = <FUSER>)
{
my (@fuser_array) = split(/\s+/, $fuser_line);
last if (! acfslib::lib_osds_is_mounted($mountpoint) );
EACH_FUSER_PID: foreach $pid (@fuser_array)
{
if ($pid =~ /$mountpoint/)
{
# The list begins with "<mountpoint>:". Ignore that.
next;
}
my ($first_char) = substr($pid, 0, 1);
if ($first_char =~ /\D/)
{
# non-digit first character
# e.g., Cannot stat file /proc/6318/fd/6: Stale NFS file handle
next;
}
# strip non digit characters from (base 10) pid.
#
# fuser output: <mountpoint>: [<pid>[n]>
# where [n] is one or more of the following:
# 'c' current directory symbol
# 'e' executable running symbol
# 'f' open file symbol
# 'F' open for write symbol
# 'm' mmap'ed file symbol
# 'n' holding non-blocking lock (Solaris)
# 'o' open file (Solaris)
# 'r' root directory symbol
# 's' shared library file (AIX)
# 't' text file (Solaris)
# 'y' controlling terminal (Solaris)
$pid =~ s/[[:alpha:]]+//g;
# Does the PID contain non-digits?
if ($pid =~ /\D/)
{
# This is not a PID. Likely an error from fuser.
next;
}
#
# This process has a file open on the mount point. Get it's
# name and log it, but not in alert.log.
#
$busyCmd = qx(ps -p $pid -o args=);
next EACH_FUSER_PID if ( ! $busyCmd );
chomp $busyCmd;
# Not sending to alert.log, it just recives the list of PIDs
# from acfsregistrymount.pl
acfslib::lib_inform_print_noalert(9153,
"Program '%s' with OS process ID '%s' is using mount point '%s'.",
$busyCmd, $pid, $mountpoint );
#
# Perform the requested action: list or kill.
#
if ($action == 1) # clean
{
#
# If process is our "safe list" don't kill it.
#
foreach my $safe ( @CLEAN_DO_NOT_KILL_LIST )
{
if ( $busyCmd =~ /$safe/ )
{
# This isn't going to end well ...
acfslib::lib_inform_print(9152,
"Program '%s' with OS process ID '%s' will not be terminated.",
$busyCmd, $pid );
$retval = USM_FAIL;
next EACH_FUSER_PID;
}
}
acfslib::lib_inform_print(9126,
"Attempting to terminate the program '%s' with OS process ID '%s'.",
$busyCmd, $pid);
my $killRet = system("kill -9 $pid");
if ( $killRet )
{
# The kill command failed. Either the process is already gone
# or we can't kill it.
my (%info) = lib_get_pid_info($pid);
if (defined($info{'PID'}))
{
# The process is still alive.
$retval = USM_FAIL;
acfslib::lib_inform_print(9136,
"PID %s could not be killed.", $pid);
report_pid_info(%info);
}
}
}
else
{
$descriptor_list .= "$pid ";
}
}
}
close (FUSER);
} # end if have_fuser
#
# Search for processes with open files on the target mount point
# using lsof. If found, list or kill them. lsof isn't found on some
# operating system installations.
#
if ($have_lsof)
{
my $skipToNextProcess = 0;
#
# lsof may block. We may want to use the -b option to
# avoid kernel calls that might block which can cause hangs
# for several minutes.
#
# Use '-F pn' option to get parsable output (pid, file name)
#
open (LSOF, "$lsof -b -F pn 2>/dev/null |");
LSOF_LINE: while($str = <LSOF>)
{
# If pid line, save it and continue.
if ( $str =~ /^p\d+$/ )
{
$pid = $str;
$pid =~ s/^p//;
chomp $pid;
$skipToNextProcess = 0;
next LSOF_LINE;
}
#
# We should be looking at a file name line. ( "n/abc/def/ghi" );
#
# If we've already performed the desired action against the
# process, skip to the next process.
#
next LSOF_LINE if ( $skipToNextProcess == 1 );
# Next if this file is not on the target mount point.
next LSOF_LINE if ( $str !~ /^n$mountpoint/ );
#
# This process has a file open on the mount point. Get it's name
# and log it.
#
$busyCmd = qx(ps -p $pid -o args=);
if ( ! $busyCmd )
{
# Maybe it exited between the time lsof found it and now.
$skipToNextProcess = 1;
next LSOF_LINE;
}
chomp $busyCmd;
acfslib::lib_inform_print_noalert(9153,
"Program '%s' with OS process ID '%s' is using mount point '%s'.",
$busyCmd, $pid, $mountpoint );
#
# Perform the requested action: list or kill.
#
if ($action == 1) # clean
{
#
# Don't kill this process if it's on our "safe list".
#
foreach my $safe ( @CLEAN_DO_NOT_KILL_LIST )
{
if ( $busyCmd =~ /$safe/ )
{
# This isn't going to end well ...
acfslib::lib_inform_print(9152,
"Program '%s' with OS process ID '%s' will not be terminated.",
$busyCmd, $pid );
$skipToNextProcess = 1;
$retval = USM_FAIL;
next LSOF_LINE;
}
}
acfslib::lib_inform_print(9126,
"Attempting to terminate the program '%s' " .
"with OS process ID '%s'.", $busyCmd, $pid);
my $killRet = system("kill -9 $pid");
if ( $killRet )
{
# The kill command failed. Either the process is already gone
# or we can't kill it.
my (%info) = lib_get_pid_info($pid);
if (defined($info{'PID'}))
{
# The process is still alive.
$retval = USM_FAIL;
acfslib::lib_inform_print(9136,
"PID %s could not be killed.", $pid);
report_pid_info(%info);
}
}
}
else
{
$descriptor_list .= "$pid ";
}
#
# A process can have multiple files open on the target mount
# point. We only want to list or kill the process once.
#
$skipToNextProcess = 1;
}
close (LSOF);
}
# Split the string by spaces.
my @words= split / /, $descriptor_list;
# New hash.
my %newwords;
# Put the values in the string as the hash keys, assign a value of 1.
# This prevents duplicates as they hash to the same value.
for (@words) { $newwords{$_}=1 }
# Join the keys back into a list. This effectively removes duplicate
# process id's in the string.
$descriptor_list = join ' ', keys(%newwords);
if ($action)
{
return $retval;
}
else
{
return $descriptor_list;
}
} # end lib_osds_ mountpoint_descriptors
# lib_osds_control_devices_accessible
#
# We test the USM control device accessibility by opening them
# Note that this will work on all Linux/Unix - but not Windows
#
# return true (1) or false (0)
#
sub lib_osds_control_devices_accessible
{
my ($ret) = 1; # assume true
open AVD, "<" . AVD_CTL_DEV or $ret = 0;
if ($ret)
{
close AVD;
# AVD open worked, now try OFS
open OFS, "<" . OFS_CTL_DEV or $ret = 0;
if ($ret)
{
# we can talk to both AVD and OFS control devices - success
close OFS;
}
}
return $ret;
} # end lib_osds_control_devices_accessible
# lib_osds_create_mount_point
#
# Create the mount point directory.
#
sub lib_osds_create_mount_point
{
my $mount_point = shift;
acfslib::lib_inform_print(9255, "Creating '%s' mount point.", $mount_point);
eval File::Path::mkpath($mount_point);
if ($@)
{
acfslib::lib_error_print(9256, "Failed to create mountpoint '%s'.", $@);
return USM_FAIL;
}
return USM_SUCCESS;
}
# lib_osds_device_from_mountpoint
#
# return the device name given a mountpoint
#
sub lib_osds_device_from_mountpoint
{
my ($mountpoint) = @_;
my ($device) = "";
my ($line);
open (MOUNT, "mount |");
while ($line = <MOUNT>)
{
my ($dev, $mntpt) = get_dev_mntpt_from_mount_line($line);
if ($mntpt eq $mountpoint)
{
$device = $dev;
last;
}
}
close (MOUNT);
return $device;
} # end lib_osds_device_from_mountpoint
# lib_osds_get_advm_mounts
#
# return an doubly dimensioned array of devices and mountpoints
# of all currently mounted OFS file systems
# array element[0] is the device and array element[1] is the mountpoint
#
sub lib_osds_get_advm_mounts
{
my (@array);
my ($i) = 0;
my ($line);
open (MOUNT, "mount |");
while ($line = <MOUNT>)
{
my ($device, $mount_point) = get_dev_mntpt_from_mount_line($line);
if ($device =~ /^\/dev\/asm\//)
{
push @{$array[$i]}, $device, $mount_point;
$i += 1;
}
}
close(MOUNT);
return \@array;
} #end lib_osds_get_advm_mounts
# osds_get_asm_user
#
# Get the user name and group id that we use to connect to ASM
#
sub lib_osds_get_asm_user
{
my (@out_array);
my ($group);
my ($line);
if ($OSNAME eq "solaris")
{
open(PS, "ps -z `/bin/zonename` -o user,gid,comm | grep asm_pmon | grep -v grep |");
}
elsif ($OSNAME eq "linux")
{
open(PS, "ps -eo user,gid,cmd | grep asm_pmon | grep -v grep |");
}
elsif ($OSNAME eq "aix")
{
# On AIX the asm_pmon process shows up as "oracle" whem using -o comm.
# When using -fe the command sometimes does not return a string,
# using -eo user,gid,args works
open(PS, "ps -eo user,gid,args | grep asm_pmon | grep -v grep |") or do
{
acfslib::lib_error_print(9999,
"Unable to get the output for ps command: $! ");
close(PS);
return (@out_array);
}
}
while ($line = <PS>)
{
# Element 0 is the ASM user name, element 1 is the ASM gid.
$line =~ s/^\s+//; # Remove leading whitespace from the line
@out_array = split(/\s+/, $line);
last;
}
close(PS);
return (@out_array);
} # end lib_osds_get_asm_user
# lib_osds_run_as_user
#
# For now, ASM connections require euid of the ASM user - bug 6900692.
#
sub lib_osds_run_as_user
{
my ($user_name, $cmd) = @_;
my ($return_code);
my (@args);
##### special case for usm_dbhome
##### usm_dbhome can be called as non-root - in which case we simmply pass
##### the command on without changing the user ID
my ($euid) = $>;
my ($asm_user) = lib_osds_get_asm_user();
if (( $euid == 0) || ($user_name ne $asm_user))
{
@args = ('su', $user_name, '-c', "$cmd");
}
else
{
@args = $cmd;
}
$return_code = system(@args);
return $return_code;
#### once we drop usm_dbhome, the function can revert back to the below #####
@args = ('su', $user_name, '-c', "$cmd");
$return_code = system(@args);
return $return_code;
}
# lib_osds_unmount
#
# unmount the specified file system
#
sub lib_osds_unmount
{
my ($mountpoint) = @_;
my ($result);
my (@nfs_export_list) = acfslib::lib_osds_remove_exports($mountpoint);
$result = system("umount $mountpoint 2> /dev/null");
if ($result)
{
# The unmount failed. Restore previously existing NFS exports, if any.
acfslib::lib_osds_restore_exports(@nfs_export_list);
return USM_FAIL;
}
return USM_SUCCESS;
} # end lib_osds_unmount
# lib_osds_usm_supported.
#
# The fact that we got here means that there is some support for
# this platform. However, perhaps not all releases are supported.
# We make that determination here.
#
# return true or false
#
sub lib_osds_usm_supported
{
my ($arch) = $Config{archname}; # Machine architecture - e.g., i386
chomp($arch);
$osds_acfslib::configuration{os} = $OSNAME;
$osds_acfslib::configuration{arch} = $arch;
# For the time being, of all the "Unix" versions,
# only Linux, AIX, and Solaris are supported
if (!(($OSNAME eq "linux") || ($OSNAME eq "aix") || ($OSNAME eq "solaris")))
{
# TODO for other operating systems
acfslib::lib_error_print(9125,
"ADVM/ACFS is not supported on this OS: '%s'.",
$OSNAME);
return 0;
}
$osds_acfslib::configuration{unamea} = $UNAME_A;
if (!(($arch =~ /^i686/) || ($arch =~ /^x86_64/)
|| ($arch =~ /^aix-thread-multi-64all/)
|| ($arch =~ /^i86pc/)
|| ($arch =~ /^sun4-solaris/)))
{
# TODO for other architectures
acfslib::lib_error_print(9120,
"The '%s' machine architecture is not supported.", $arch);
return 0;
}
# $type is "EL5", for example
my ($type) = osds_acfslib::lib_osds_get_os_type(@_);
if ($type =~ /not supported/)
{
return 0;
}
return 1;
} # end lib_osds_usm_supported
# lib_osds_verify_usm_devices
#
# Verify that the USM drivers are loaded and running by checking the
# existence of the device control files for the drivers.
#
sub lib_osds_verify_usm_devices
{
my ($asmadmin) = acfslib::lib_get_asm_admin_name();
# Make sure that the proper /dev files get created
my ($found) = 0;
my ($max_wait_seconds) = 60;
my ($device) = AVD_CTL_DEV;
acfslib::lib_inform_print(9156, "Detecting control device '%s'.", $device);
while (($max_wait_seconds > 0) && (!$found))
{
if (! -e $device)
{
sleep 1;
$max_wait_seconds -= 1;
}
else
{
$found = 1;
# Solaris - Control device permissions come from the original add_drv
# command, which is called with a '-m' switch to set the permissions for
# subsequent devfsadm calls to load the driver.
# Linux - control device permissions come from a udev rule file.
# AIX - control device permissions come from cfgadvmctl\cfgacfsctl and
# the mknod command there.
open LSDIR, "ls -Lla $device |";
while (<LSDIR>)
{
acfslib::lib_trace( 9999, "VUD: control device permissions: \n\t". $_);
}
close(LSDIR);
}
}
if (!$found)
{
acfslib::lib_error_print(9121, "Failed to detect control device '%s'.", $device);
return USM_FAIL;
}
else
{
# The ADVM driver creates /dev/asm/* and not (directly) /dev/asm.
my $rcode;
$rcode = system("chgrp $asmadmin " . AVD_DIR);
if($rcode > 0)
{
acfslib::lib_error_print(10624, "User '%s' is not member of the group '%s'.","root",$asmadmin);
return USM_FAIL;
}
system("chmod 0770 " . AVD_DIR);
}
$max_wait_seconds = 60;
$found = 0;
$device = OFS_CTL_DEV;
acfslib::lib_inform_print(9156, "Detecting control device '%s'.", $device);
while (($max_wait_seconds > 0) && (!$found))
{
if (! -e $device)
{
sleep 1;
$max_wait_seconds -= 1;
}
else
{
$found = 1;
# Solaris - Control device permissions come from the original add_drv
# command, which is called with a '-m' switch to set the permissions for
# subsequent devfsadm calls to load the driver.
# Linux - control device permissions come from a udev rule file.
# AIX - control device permissions come from cfgadvmctl\cfgacfsctl and
# the mknod command there.
open LSDIR, "ls -Lla $device |";
while (<LSDIR>)
{
acfslib::lib_trace( 9999, "VUD: control device permissions: \n\t". $_);
}
close (LSDIR);
}
}
if (!$found)
{
acfslib::lib_error_print(9121, "Failed to detect control device '%s'.", $device);
return USM_FAIL;
}
# In some cases, udev doesn't apply the rules.
# A touch of the devices will change the permissions to match udev rules.
my $ctl_path = AVD_DIR . "/.asm*";
system("touch $ctl_path");
lib_osds_trigger_udev_rules($ctl_path, $asmadmin);
# We only warn if persistent logging can't be started.
`$ACFSUTIL plogconfig -d`;
if ($?)
{
acfslib::lib_inform_print(9225, "Failed to start OKS persistent logging.");
}
} #end lib_osds_verify_usm_devices
###### static routines only after this point ##########
# lib_get_pid_info
#
# Collect and return information, for a given PID.
# Returns an undefined %pid_hash if the PID is not found.
#
# Linux, Solaris, and AIX all support ps -fe and output in the same format.
# If a future supported Unix does not, we'll have to make changes.
#
sub lib_get_pid_info
{
my ($pid) = @_; # target PID
my ($ps_info); # output line from the ps(1) command
my (%pid_hash); # return: hashed output from the ps(1) command for $pid
open PS, "/bin/ps -fe 2>&1 |"
or warn ("Failed to run '/bin/ps -fe': $!"), return %pid_hash;
# ps -fe format is:
# UID PID PPID C STIME TTY TIME CMD
# 0 1 2 3 4 5 6 7
while ($ps_info = <PS>)
{
my (@array) = split /\s+/, $ps_info;
%pid_hash = (
USER => $array[0],
PID => $array[1],
PPID => $array[2],
C => $array[3],
STIME => $array[4],
TTY => $array[5],
TIME => $array[6],
CMD => $array[7],
);
if ($pid eq $pid_hash{'PID'})
{
close(PS);
return %pid_hash;
}
}
# Target PID not found.
close (PS);
undef %pid_hash;
return %pid_hash;
} # end lib_get_pid_info
# 9999 messages are not formatted - WYSIWYG.
#
sub report_pid_info
{
my (%info) = @_;
acfslib::lib_inform_print(9141, " COMMAND %s", $info{'CMD'});
acfslib::lib_inform_print(9143, " USER %s", $info{'USER'});
acfslib::lib_inform_print(9144, " CPU_TIME %s", $info{'TIME'});
acfslib::lib_inform_print(9142, " STATUS %s", $info{'C'});
}
# get_dev_mntpt_from_mount_line
#
# Get the device and mountpoint from a mount command output line.
#
# Different Unixs have different mount command output formats.
# mount | grep mnt1
# /dev/asm/foo-123 on /mnt1 ... Linux
# /dev/asm/foo-123 /mnt1 ... AIX
# /mnt1 on /dev/asm/foo-123 ... Solaris
#
sub get_dev_mntpt_from_mount_line
{
my ($line) = @_;
my ($device, $on, $mountpoint);
if ($OSNAME eq "linux")
{
# /dev/asm/xxx_yyy on /oracle_base/ofsdata/foo type ofs (rw)
($device, $on, $mountpoint) = split(/ /, $line);
}
elsif ($OSNAME eq "solaris")
{
# /dev/asm/xxx_yyy on /oracle_base/ofsdata/foo <options> on <date>
($mountpoint, $on, $device) = split(/ /, $line);
}
elsif ($OSNAME eq "aix")
{
# <node> /dev/asm/xxx_yyy /oracle_base/ofsdata/foo <type> <date> <options>
my ($node);
if ($line =~ /^ /)
{
# if the first char of the line is blank, we don't have a node.
$line =~ s/^\s+//; # remove leading spaces
$line =~ s/\s+/ /g; # remove extra spaces between fields
($device, $mountpoint) = split(/ /, $line);
}
else
{
$line =~ s/\s+/ /g; # remove spaces between fields
($node, $device, $mountpoint) = split(/ /, $line);
}
}
else
{
# should never get here
lib_error_print(9149,
"unable to determine device mount status - unsupported OS name '%s'",
$OSNAME);
}
return ($device, $mountpoint);
} # end get_dev_mntpt_from_mount_line
# This is a Windows function
sub lib_osds_get_drive_info
{
return 0;
}
# lib_osds_is_abs_path
sub lib_osds_is_abs_path
{
my $path = shift;
if ( $path =~ /^\// )
{
return 1;
}
return 0;
}
#Function used to avoid comparing kernels as strings
#Just like strcmp in C, if a data, in this case numbers, returns the difference
#between the first non-equal elements, being positive if the first argument's
#is greater than the second's.
#Perl's stringwise operators appear to compare each character of the string to
#see if it has a higher ascii value than the other. So, "76" < "8". "101 <
#800". "501" > "4900". Thus, we need to compare each value using a numeric
#compare.
sub lib_osds_krncmp {
my ($kver_a, $kver_b, $splitter) = @_;
#We use the same functions for dotted and dashed kernel values
if(!defined($splitter)){
$splitter = "\\.";
}
my (@splitted_a) = split(/$splitter/, $kver_a);
my (@splitted_b) = split(/$splitter/, $kver_b);
#Let's compare each part, assuming that significance decreases to the right.
for my $i (0 .. $#splitted_a) {
if (scalar @splitted_b <= $i)
{
#We have more elements in a.
last;
}
if($splitted_a[$i] =~ /-/ || $splitted_b[$i] =~ /-/) {
# Values have dashes
my $dashed = lib_osds_krncmp($splitted_a[$i], $splitted_b[$i], "-");
if($dashed != 0) {
return $dashed
}
}
else {
#String Values
if (($splitted_a[$i] =~ /\D/) &&
($splitted_b[$i] =~ /\D/))
{
return $splitted_a[$i] cmp $splitted_b[$i];
}
#A is string compares vs 0
elsif ($splitted_a[$i] =~ /\D/)
{
return 0 - ($splitted_b[$i]+0);
}
#B is string compares vs 0
elsif ($splitted_b[$i] =~ /\D/)
{
return ($splitted_a[$i]+0);
}
# Numeric Values
# +0 forces it to treat the value as a number.
elsif (($splitted_a[$i]+0) - ($splitted_b[$i]+0) != 0){
return ($splitted_a[$i]+0) - ($splitted_b[$i]+0);
}
}
}
#If we checked all elements and they're equal or the 2nd kernel has more
#values then it means a bigger kernel version.
return scalar @splitted_a - scalar @splitted_b;
}
# Returns 1 if both files are the same, either through a hard link or through
# a symbolic link path. Linux/Unix specific.
use File::stat;
sub lib_osds_are_same_file
{
my ($source, $target) = @_;
my ($s_sb, $t_sb);
if ( -e $source && -e $target ){
$t_sb = stat($target);
$s_sb = stat($source);
if ( $t_sb->dev==$s_sb->dev && $t_sb->ino==$s_sb->ino ){
return 1;
}
}
return 0;
}
#After we touch the devices, we'll check for the right permissions
#In Linux, permissions come from a udev rule file
#If the permissions are not right, we'll try to reload the rules
sub lib_osds_trigger_udev_rules
{
my ($ctl_path, $asmadmin) = @_;
if ($OSNAME eq "linux")
{
my $max_wait_seconds = 10;
my $found = 0;
acfslib::lib_trace( 9999, "Detecting permissions for devices using udev rules.");
while (($max_wait_seconds > 0) && (!$found))
{
my ($ctl_line);
open LSCTLDIR, "ls -Lla $ctl_path |";
while ($ctl_line = <LSCTLDIR>)
{
chomp($ctl_line);
#Line has to include the permissions "brwxrwx---" and asmadminname
if ((index($ctl_line, "brwxrwx---") < 0) ||
(index($ctl_line, $asmadmin) < 0))
{
#Udev is not working. Reload rules
$found = 0;
acfslib::lib_trace( 9999, "Wrong Permissions: \t". $ctl_line);
# For restarting udev rules, we'll run the following commands
my $type = osds_acfslib::lib_osds_get_os_type();
if ($type =~ /EL5/)
{
system("udevcontrol start_exec_queue");
system("udevcontrol reload_rules");
system("udevtrigger");
}
else
{
system("udevadm control --start-exec-queue");
if ($type =~ /SLES12/)
{
system("udevadm control --reload");
}
else
{
system("udevadm control --reload-rules");
}
system("udevadm trigger");
}
sleep 1;
$max_wait_seconds -= 1;
last;
}
else
{
$found = 1;
}
}
close (LSCTLDIR);
}
}
}
#
# HANFS Unix Locking
#
sub lib_osds_get_nfs_path
{
if ($OSNAME eq "linux")
{
return "/var/lib/nfs";
}
elsif ($OSNAME eq "solaris")
{
return "/var/share/nfs";
}
#TODO: AIX Support
acfslib::lib_error_print(9615, "NFS_PATH is not supported.");
return "";
}
# We need to check and create the following directories for HANFS
# Directories only for Linux
sub lib_osds_get_nfs_directories
{
if ($OSNAME eq "linux")
{
return ("v4root","v4recovery","rpc_pipefs");
}
return ();
}
#lib_check_nfs_path
#
# Check if $NFS_PATH is an ACFS File Sytem.
#
sub lib_check_nfs_path
{
my ($result) = 0;
my $NFS_PATH = lib_osds_get_nfs_path();
if (acfslib::lib_is_mounted($NFS_PATH))
{
my ($cmd_out);
my $cmd = "$ACFSUTIL info fs " . OPT_CHR . "o ismountpoint " . $NFS_PATH;
$cmd_out= `$cmd`;
if (!defined($cmd_out))
{
$cmd_out="<No Error Text Returned>";
}
if ($? == 0)
{
# Execution successful, cmd_out will hold 0 or 1.
if ($cmd_out != 1 )
{
$result = 0;
}
else
{ #mount is available.
$result = 1;
}
}
else
{
$result = 0;
}
}
return $result;
} #end lib_check_nfs_path
# lib_install_nfs_path
#
# Install HANFS Locking support
#
sub lib_install_nfs_path
{
my ($force, $volume) = (@_);
my ($result) = USM_FAIL;
my ($rc) = 0;
my ($output) = "";
my $isNFSPathMounted = FALSE;
my $isNFSPathSymlinked = FALSE;
my $isVolumeMounted = FALSE;
my $NFS_PATH = lib_osds_get_nfs_path();
my $zip_file_name = "/tmp/hanfs.nfs.zip";
my $cmd = "";
if (acfslib::lib_is_mounted($NFS_PATH))
{
$isNFSPathMounted = TRUE;
}
if (-l $NFS_PATH)
{
$isNFSPathSymlinked = TRUE;
}
if (acfslib::lib_is_mounted($volume))
{
$isVolumeMounted = TRUE;
}
# Ensure that an NFS resource does not already exist. If it does, either
# # '-addnode' must be run, or NFSv4 locking is already configured in this cluster.
my ($volumeResourceDependency, $nfsVolume, $isrunningNFS, $existsNFS) = getNFSResourceStatus();
if ($existsNFS == -1)
{
return USM_FAIL;
}
# Check for filesystem resource
my $isrunningFS = 0;
my $existsFS = 0;
open SRVCTL_FS, "$ORACLE_HOME/bin/srvctl status filesystem -d $volume |";
while (<SRVCTL_FS>)
{
if (/PRKO-2012/)
{
# "No Oracle Clusterware components configured."
acfslib::lib_error_print_noalert(5219,
"Cannot proceed because CRS stack is not up.");
$result = USM_FAIL;
last;
}
elsif (/PRCA-1070/)
{
# "Could not find resource filesystem"
$result = USM_SUCCESS;
last;
}
elsif (/ADVM-03180/)
{
# Unable to obtain ASM volume device information for volume
$result = USM_SUCCESS;
last;
}
elsif (/is mounted on nodes/)
{
$existsFS = 1;
$isrunningFS = 1;
$result = USM_SUCCESS;
}
elsif (/is not mounted/)
{
$existsFS = 1;
$isrunningFS = 0;
$result = USM_SUCCESS;
}
}
close SRVCTL_FS;
if ($result == USM_FAIL)
{
return $result;
}
elsif (!$force)
{
# If !force, warn the user that the following actions will be taken:
# Modification of NFS OS Startup scripts so that NFS does not
# automatically start.
# Modification of $NFS_PATH to be on stable storage.
# Format of the volume.
# Creation of a file system resource.
# Short message regarding ongoing management of NFS by the CRS stack.
# (Print a list of appropriate srvctl commands to use.)
#
acfslib::lib_inform_print_noalert(9603, "The script will do the following actions:");
acfslib::lib_inform_print_noalert(9604,
" - Update the operating system startup procedure so that NFS does not automatically start.");
acfslib::lib_inform_print_noalert(9605,
" Management of the NFS daemons will be moved to Oracle Clusterware.");
acfslib::lib_inform_print_noalert(9606, " - Format the volume: %s.", $volume);
acfslib::lib_inform_print_noalert(9607,
" - Create an ACFS resource for the file system.");
acfslib::lib_inform_print_noalert(9608,
" - Mount the ACFS file system on '%s'.", $NFS_PATH);
acfslib::lib_inform_print_noalert(9609, " Continue the installation? [1=yes,2=no]:");
# Request confirmation of intent to continue, unless ???force option
# is provided.
my $answer = <STDIN>;
chomp($answer);
if ($answer == 1)
{
$result = USM_SUCCESS;
}
else
{
return USM_FAIL;
}
}
#Before starting with the installation, will do a couple of validations.
my $isFirstNode = 0;
if (!$existsNFS && !$existsFS)
{
$isFirstNode = 1;
}
elsif ($existsNFS && $existsFS)
{
$isFirstNode = 0;
}
elsif ($existsNFS && !$existsFS)
{
#This is weird but possible, we'll need to create the filesystem resource
}
elsif (!$existsNFS && $existsFS)
{
# Possible but file system should not be used for any other application
# We'll send an error.
acfslib::lib_error_print_noalert( 9610,
"Error - the ACFS resource for the specified file system is already in use.");
return USM_FAIL;
}
# (First node) Ensure that an ACFS file system is not mounted on or
#symlinked from $NFS_PATH.
if ($isFirstNode && ($isNFSPathMounted || $isNFSPathSymlinked))
{
acfslib::lib_error_print_noalert(9602,
"Installation cannot proceed: path '%s' is mounted or it is a symlink.",
$NFS_PATH);
return USM_FAIL;
}
# (First node) Ensure that the volume is not mounted.
if ($isFirstNode && $isVolumeMounted)
{
acfslib::lib_error_print_noalert(2125, "volume '%s' is already in use", $volume);
return USM_FAIL;
}
# (All nodes) Stop the OS NFS daemons.
acfslib::lib_inform_print_noalert(9612, "Stopping NFS Service.");
if (!actionNFSServer("stop"))
{
$result = USM_FAIL;
}
# Before to mount, create a zip file in /tmp/ including all $NFS_PATH files.
# We want to mantain the same structure that the customer has.
if ($result != USM_FAIL)
{
my $current_directory = Cwd::cwd();
my $ziptool = "/usr/bin/zip";
if (-e $zip_file_name)
{
unlink($zip_file_name);
}
#Check the zip tool
if (!-e $ziptool)
{
# If the absolute path doesn't exist, we'll try with the rleative command
$ziptool = "zip";
}
chdir($NFS_PATH);
$cmd = "$ziptool -qr $zip_file_name *";
$rc = system($cmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', " .
"output = '%s'", $cmd, ($? >> 8));
chdir($current_directory);
}
my ($disk_group) = "";
my ($vol) = "";
# (All nodes) Verify the volume device is enabled.
open ADVMUTIL, "$ADVMUTIL volinfo $volume |";
if ($?)
{
# It's not enabled or it's not an ADVM Volume
acfslib::lib_error_print_noalert( 520, "%s is not an ADVM volume.", $volume);
$result = USM_FAIL;
}
else
{
while (<ADVMUTIL>)
{
if (/Disk Group/)
{
$disk_group = ($_);
chomp($disk_group);
$disk_group = acfslib::trim(substr($disk_group, index($disk_group, ':') + 1));
$disk_group =~ s/^\s+//;
}
elsif (/Volume/)
{
$vol = ($_);
chomp($vol);
$vol = acfslib::trim(substr($vol, index($vol, ':') + 1));
$vol =~ s/^\s+//;
}
}
}
# If NFS Resource exists, we need to check that
# they are using the same Volume Resource Dependency
if ($existsNFS &&
(lc($volumeResourceDependency) ne lc("ora.$disk_group.$vol.acfs")))
{
# If the NFS Resource exists, we'll use the dependency from there
($volume, $isrunningFS, $existsFS) = getVolumeName($volumeResourceDependency);
if ($existsFS == -1)
{
$result = USM_FAIL;
}
# if the filesystem Resource exists and NFS Resource exist
# # will check the volume path
if ($existsFS &&
(lc($volume) ne lc($nfsVolume)))
{
$result = USM_FAIL;
}
}
if ($result != USM_FAIL)
{
# (First node only) Format the volume provided.
if (!$existsFS || !$isrunningFS)
{
acfslib::lib_inform_print_noalert(9614, "Formatting the volume device.");
# For Linux
$cmd = $MKFS_ACFS . " " . OPT_CHR . "f " . $volume;
if ($OSNAME eq "solaris")
{
$cmd = $MKFS . " " . OPT_CHR . "F acfs " . OPT_CHR . "o f " . $volume;
}
$rc = system($cmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'", $cmd, ($? >> 8));
if ($rc != 0)
{
$result = USM_FAIL;
}
}
}
if ($result != USM_FAIL)
{
# (First node only) Create a file system resource for the storage path and
# configure it to be mounted on $NFS_PATH.
# Create it globally disabled, enablig it on a node by node basis.
if (!$existsFS)
{
if (!actionFileSystemResource("add", $volume))
{
$result = USM_FAIL;
}
}
}
# As far I can see, we don't need to use a temporary path.
# We can mount directly in $NFS_PATH and the information will not be lost.
# The previous contents (if any), owner, and mode of dir become invisible,
# and as long as this filesystem remains mounted, the pathname dir refers
# to the root of the filesystem on device.
# So maybe, we don't need to run the following steps.
# (First node only) Mount the file system in a temporary location.
# (First node only) Copy $NFS_PATH to file system.
# (All nodes) Move $NFS_PATH to $NFS_PATH.old_before_oracle_hanfs_<date>
# (All nodes) Create a new $NFS_PATH
# (First node only) Unmount the temporary location of the file system.
if ($result != USM_FAIL)
{
# (All nodes) Enable and start the file system resource ??? on current node only
if (!actionFileSystemResource("start", $volume))
{
$result = USM_FAIL;
}
}
if ($result != USM_FAIL)
{
if (-e $zip_file_name)
{
my $unziptool = "/usr/bin/unzip";
#Check the unzip tool
if (!-e $unziptool)
{
# If the absolute path doesn't exist, we'll try with the rleative command
$unziptool = "unzip";
}
$cmd = "$unziptool -uoq $zip_file_name -d $NFS_PATH";
$rc = system($cmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', " .
"output = '%s'", $cmd, ($? >> 8));
}
# Filesystem is mounted, we need to check for NFS Customer and
# NFSv4 Directories. We could have duplicated entries but it doesn't
# matter. If the directory exists we skip it.
my @directories = lib_osds_get_nfs_directories();
foreach my $directory (@directories)
{
if (!(-e "$NFS_PATH/$directory"))
{
$cmd = "mkdir $NFS_PATH/$directory";
$rc = system($cmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', " .
"output = '%s'", $cmd, ($? >> 8));
if ($?)
{
lib_error_print(9345, "Unable to create directory: '%s'.",
"$NFS_PATH/$directory");
return USM_FAIL;
}
}
}
}
# (First node only) Call appropriate srvctl command to add the NFS resource
# and adjust current export dependencies.
if ($result != USM_FAIL)
{
if (!$existsNFS)
{
#Add NFS Resouce
if (!actionNFSResource("add", $volume))
{
$result = USM_FAIL;
}
}
}
# (All nodes) Start the NFS resource on current node.
acfslib::lib_inform_print_noalert(9611, "Starting NFS Service.");
if (!actionNFSServer("start"))
{
$result = USM_FAIL;
}
# Before starting the NFS resource, make sure the
# service is online, in Solaris the server does not get online
# if there no share resources or there is no work to do.
# in that case create a share folder with limited access.
if ( $OSNAME eq "solaris" )
{
# Check that there is no share resources
$output = `wc -l < /etc/dfs/sharetab`;
chomp($output);
if ( $output eq '1' )
{
$cmd = "/usr/bin/mkdir $NFS_PATH/nfs_share";
$rc = system($cmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
$cmd, ($? >> 8));
$cmd = "$USR_SBIN_DIR/share -F nfs -o ro,anon=-1 $NFS_PATH/nfs_share";
$rc = system($cmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
$cmd, ($? >> 8));
}
}
if ($result != USM_FAIL)
{
if ($isrunningNFS)
{
#Stop
if (!actionNFSResource("stop"))
{
$result = USM_FAIL;
}
}
#Start
if (!actionNFSResource("start"))
{
$result = USM_FAIL;
}
}
return $result;
} #end lib_install_nfs_path
# lib_uninstall_nfs_path
#
# Uninstall HANFS Locking support
#
sub lib_uninstall_nfs_path
{
my ($result) = USM_FAIL;
my ($rc) = 0;
my ($cmd) = "";
my $NFS_PATH = lib_osds_get_nfs_path();
my ($volumeResourceDependency, $nfsVolume, $isrunningNFS, $existsNFS) = getNFSResourceStatus();
if ($existsNFS == -1)
{
return USM_FAIL;
}
# Check for filesystem resource
my ($volume, $isrunningFS, $existsFS) = ("", 0, 0);
if ($existsNFS)
{
($volume, $isrunningFS, $existsFS) = getVolumeName($volumeResourceDependency);
}
if ($existsFS == -1)
{
$result = USM_FAIL;
}
else
{
$result = USM_SUCCESS;
}
# In solaris a share a folder with limited access is created in order
# to get the service online. Here we unshare the folder so the system
# returns to a clean state.
if ( $OSNAME eq "solaris" )
{
open SHARE, "$USR_SBIN_DIR/share |";
while (<SHARE>)
{
if (/nfs_share/)
{
$cmd = "$USR_SBIN_DIR/unshare $NFS_PATH/nfs_share";
$rc = system($cmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
$cmd, ($? >> 8));
}
}
}
# Stop the service
if (!actionNFSServer("stop"))
{
$result = USM_FAIL;
}
# Even when services rpcidmapd and NFS are down, directory rpc_pipefs is
# still mounted, mainly in EL6. For Linux umount rpc_pipefs
if ($OSNAME eq "linux")
{
lib_osds_unmount("$NFS_PATH/rpc_pipefs");
}
# (All nodes) Stop the NFS service using srvctl with the force option
# on the current node.
if ($result != USM_FAIL)
{
if ($isrunningNFS)
{
#Stop
if (!actionNFSResource("stop"))
{
$result = USM_FAIL;
}
}
}
# (All nodes) Stop the ACFS file system for stable storage on
# the current node only.
if ($result != USM_FAIL)
{
if ($isrunningFS)
{
if (!actionFileSystemResource("stop", $volume))
{
# It could fail, if not running in this node
# or if ACFS Resource can not umount
}
}
}
# Get the service online since restart only works with online services.
# Start the service
if (!actionNFSServer("start"))
{
$result = USM_FAIL;
}
# (All nodes) Disable the NFS service resource on this node.
# Just for srvctl
# (All nodes) Disable the ACFS file system for stable storage on
# the current node only.
# It doesn't work for node
# We don't need to do these steps.
# (All nodes) Move $NFS_PATH to a temporary directory.
# (All nodes) Create a new $NFS_PATH.
# (All nodes) Copy the old $NFS_PATH files from stable storage to
# the new $NFS_PATH.
# If the files don???t exist, use the backup in $NFS_PATH from
# installation.
# (Last node) Remove the NFS CRS agent using the appropriate srvctl remove
# command.
# (Last node) Remove the stable storage ACFS resource.
if ($result != USM_FAIL)
{
if ($existsFS && isLastNode($volume))
{
if (!actionNFSResource("remove") ||
!actionFileSystemResource("remove", $volume))
{
$result = USM_FAIL;
}
}
}
# (All nodes) Start the NFS services using the OS commands.
# (All nodes) Restore the OS management of NFS daemons.
acfslib::lib_inform_print_noalert(9613, "Restarting NFS Service.");
# In solaris if there is no shared resources the following action is
# going to fail since the service is disabled and the restart option
# only works with online services. The action is performed but the result is
# not going to affect the return status.
actionNFSServer("restart");
return $result;
} #end lib_uninstall_nfs_path
# actionNFSServer
# start/stop/restart NFS Server.
# Parameters
# $action - "stop"/"start"/"restart" actions for the NFS Server
# Returns TRUE if action was successful or FALSE if it failed
sub actionNFSServer
{
my $action = shift;
my $cmd = "";
my $auxcmd = "";
my $rc = 0;
if ($OSNAME eq "linux")
{
$cmd = "/sbin/service nfs ". $action;
$auxcmd = "/sbin/service rpcidmapd ". $action;
# In Linux, we need to stop and start rpc.idmapd before to
# nfs service.
$rc = system($auxcmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
$auxcmd, ($? >> 8));
}
elsif ($OSNAME eq "solaris")
{
# Translate for solaris disable for stop and enable for start.
# NOTE: restart only works on services that are in the online
# or degraded states
if( $action eq "start" )
{
$action = "enable";
}
elsif( $action eq "stop" )
{
$action = "disable";
}
$cmd = "/sbin/svcadm ". $action . " network/nfs/server";
}
$rc = system($cmd);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
$cmd, ($? >> 8));
if ($rc != 0)
{
return FALSE;
}
return TRUE;
} #end actionNFSServer
# actionFileSystemResource
# add/start/stop/disable/remove Filesystem Resource
# Parameters
# $action - "add"/"start"/"stop"/"disable"/"remove" actions
# for the Filesystem Resource
# $volume - Device to use
# Returns TRUE if action was successful or FALSE if it failed
sub actionFileSystemResource
{
my ($action) = shift;
my ($volume) = shift;
my ($rc) = 0;
my $NFS_PATH = lib_osds_get_nfs_path();
if ($action eq "add")
{
$rc = system("$ORACLE_HOME/bin/srvctl add filesystem -device " . $volume .
" -path " . $NFS_PATH);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
"$ORACLE_HOME/bin/srvctl add filesystem -device " .
$volume . " -path " . $NFS_PATH, ($? >> 8));
if ($rc != 0)
{
return FALSE;
}
}
else
{
my ($node_command) = "";
if ($action eq "start" ||
$action eq "stop")
{
$node_command = " -n $hostname";
}
$rc = system("$ORACLE_HOME/bin/srvctl " . $action . " filesystem -device " .
$volume . $node_command);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
"$ORACLE_HOME/bin/srvctl " . $action .
" filesystem -device " . $volume . $node_command,
($? >> 8));
if ($rc != 0)
{
return FALSE;
}
}
return TRUE
} #end actionFileSystemResource
# actionNFSResource
# start/stop/remove/add NFS Resource
# Parameters
# $action - "start"/"stop"/"remove"/"add" actions for the NFS Resource
# $attr - Attribute to add or modify. Just for add or modify action.
# Returns TRUE if action was successful or FALSE if it was failed
sub actionNFSResource
{
my ($action) = shift;
my ($rc) = 0;
if ($action eq "add")
{
my $device = shift;
my $command = "$ORACLE_HOME/bin/srvctl add netstorageservice -device "
. $device;
$rc = system($command);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
$command, ($? >> 8));
if ($rc != 0)
{
return FALSE;
}
}
else
{
my $command = "$ORACLE_HOME/bin/srvctl ". $action . " netstorageservice";
$rc = system($command);
acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
$command, ($? >> 8));
if ($rc != 0)
{
return FALSE;
}
}
return TRUE
} #end actionNFSResource
# getNFSResourceStatus
# Status for NFS Resource
sub getNFSResourceStatus
{
my $resource = "ora.netstorageservice";
my $volumeResourceDependency = "";
my $volume = "";
my $isrunningNFS = 0;
my $existsNFS = -1;
open CRSCTL, "$ORACLE_HOME/bin/crsctl stat res $resource -f |";
while (<CRSCTL>)
{
if (/CRS-4047/)
{
# "No Oracle Clusterware components configured."
acfslib::lib_error_print_noalert(5219, "Cannot proceed because CRS stack is not up.");
last;
}
elsif (/CRS-2613/)
{
# "Could not find resource ora.netstorageservice"
$existsNFS = 0;
last;
}
elsif (/STATE=OFFLINE/)
{
$existsNFS = 1;
}
elsif (/STATE=ONLINE/)
{
$existsNFS = 1;
$isrunningNFS = 1;
}
elsif (/START_DEPENDENCIES/)
{
$existsNFS = 1;
if (!(/START_DEPENDENCIES_RTE_INTERNAL/))
{
$volumeResourceDependency = ($_);
chomp($volumeResourceDependency);
$volumeResourceDependency = substr($volumeResourceDependency,
index($volumeResourceDependency, '(') + 1);
$volumeResourceDependency = substr($volumeResourceDependency,
index($volumeResourceDependency, '(') + 1);
$volumeResourceDependency = substr($volumeResourceDependency, 0,
length($volumeResourceDependency) - 1);
}
}
elsif (/STABLE_STORAGE/)
{
$volume = ($_);
chomp($volume);
$volume = substr($volume, index($volume, '=') + 1);
}
}
close CRSCTL;
return ($volumeResourceDependency, $volume, $isrunningNFS, $existsNFS);
} # end getNFSResourceStatus
sub getVolumeName
{
my ($volumeResourceDependency) = shift;
# Check for filesystem resource
my $isrunningFS = 0;
my $existsFS = -1;
my $volume = '';
open CRSCTL, "$ORACLE_HOME/bin/crsctl stat res $volumeResourceDependency -f |";
while (<CRSCTL>)
{
if (/CRS-4047/)
{
# "No Oracle Clusterware components configured."
acfslib::lib_error_print_noalert(5219, "Cannot proceed because CRS stack is not up.");
last;
}
elsif (/CRS-2613/)
{
# "Could not find resource
$existsFS = 0;
last;
}
elsif (/STATE=OFFLINE/)
{
$existsFS = 1;
}
elsif (/STATE=ONLINE/)
{
$existsFS = 1;
$isrunningFS = 1;
}
elsif (/VOLUME_DEVICE/)
{
$volume = ($_);
chomp($volume);
$volume = substr($volume, index($volume, '=') + 1);
}
}
close CRSCTL;
return ($volume, $isrunningFS, $existsFS);
}
sub isLastNode
{
my ($volume) = shift;
# Check for filesystem resource
open SRVCTL_FS, "$ORACLE_HOME/bin/srvctl status filesystem -d $volume |";
while (<SRVCTL_FS>)
{
if (/PRKO-2012/)
{
# "No Oracle Clusterware components configured."
acfslib::lib_error_print_noalert(5219, "Cannot proceed because CRS stack is not up.");
last;
}
elsif (/PRCA-1070/)
{
# "Could not find resource filesystem"
last;
}
elsif (/ADVM-03180/)
{
# Unable to obtain ASM volume device information for volume
last;
}
elsif (/is mounted on nodes/) #We stop the resource on this node
{
last;
}
elsif (/is not mounted/)
{
return TRUE;
}
}
close SRVCTL_FS;
return FALSE;
}
# vim:ts=2:expandtab
OHA YOOOO