# $Header: has/install/crsconfig/oraasm.pm /st_has_12.2.0.1.0ocwpsu/1 2017/05/15 09:52:07 yilhu Exp $ # # oraasm.pm # # Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. # # NAME # oraasm.pm - # # DESCRIPTION # An ASM component to manage ASM related operations during install, # upgrade, downgrade and deconfiguration. # # NOTES # Required variables from the config object $CFG: # $CFG->ASM_STORAGE_USED # $CFG->oldconfig('ASM_CONFIGURED') # $CFG->oldconfig('LOCAL_NODE_NUM') # $CFG->oldconfig('ORA_CRS_VERSION') # $CFG->oldconfig('ORA_CRS_HOME') # $CFG->params('ASM_DISCOVERY_STRING') # $CFG->params('CDATA_DISK_GROUP') # $CFG->params('CDATA_DISKS') # $CFG->params('CDATA_REDUNDANCY') # $CFG->params('CDATA_AUSIZE') # $CFG->params('CDATA_SITES') # $CFG->params('ASM_DISCOVERY_STRING') # $CFG->params('ORATAB_LOC') # $CFG->params('ORACLE_OWNER') # $CFG->params('ORACLE_BASE') # $CFG->ORA_CRS_HOME # $CFG->oldconfig('NODE_CONFIG_ROLE') # $CFG->OLD_CRS_HOME # $CFG->params('CDATA_BACKUP_DISK_GROUP') # $CFG->params('CDATA_BACKUP_DISKS') # $CFG->params('CDATA_BACKUP_FAILURE_GROUPS') # $CFG->params('CDATA_BACKUP_QUORUM_GROUPS') # $CFG->params('EXTENDED_CLUSTER_SITES') # $CFG->params('CDATA_BACKUP_REDUNDANCY') # $CFG->params('CDATA_BACKUP_AUSIZE') # $CFG->params('CDATA_BACKUP_SITES') # # MODIFIED (MM/DD/YY) # apfwkr 05/05/17 - Backport yilhu_blr_backport_25556203_12.2.0.1.0 from # st_has_12.2.0.1.0 # yilhu 02/24/17 - Backport yilhu_bug-25556203 from main # yilhu 08/19/16 - Fix bug 24491216 # yilhu 08/09/16 - Fix bug 24410674 # yilhu 02/16/17 - Fix bug 25556203 # sanselva 07/30/16 - 24358034: fix up asmcmd log dir ownerships/perms # yilhu 07/14/16 - Fix bug 24292731 # yilhu 07/13/16 - Fix bug 23727991 # muhe 07/03/16 - Fix bug 23645703 # yilhu 06/14/16 - Fix bug 19585275 # yilhu 05/25/16 - Fix bug 23343967, 23297287 and 23317840 # yilhu 04/18/16 - Fix bug 21877400 and bug 21914183 # muhe 04/14/16 - Fix bug 23080048 # ssprasad 04/11/16 - Fix bug 22964480 # bbeelamk 04/05/16 - Fix bug 23053427 # muhe 03/23/16 - Fix bug 22870681 # bbeelamk 03/08/16 - Fix srvctl error # luoli 03/03/16 - Modify subroutine create_backup_dg() # xyuan 12/16/15 - Fix a typo # bbeelamk 11/19/15 - Fix bug 22227798 # luoli 11/18/15 - Fix bug 22185539 # bbeelamk 10/27/15 - Fix typo # bbeelamk 10/07/15 - Fix bug 21878673 # bbeelamk 09/03/15 - Fix bug 21761307 # jmarcias 07/24/15 - Fix bug 21492602 # jmarcias 07/21/15 - Fix bug 21471669 # muhe 06/10/15 - Fix bug 21214341 # luoli 06/09/15 - Fix bug 21188637 # muhe 06/01/15 - Fix bug 21064827 # muhe 05/10/15 - Fix bug 21039599 # bbeelamk 04/29/15 - Fix bug 20970145 # muhe 04/14/15 - Add interface functions body for upgrade and patch # jmarcias 03/17/15 - Fix bug 20569941 # xyuan 02/27/15 - Fix bug 20614945 # luoli 12/22/14 - rsc modeling for downgrade/deconfig # bbeelamk 12/01/14 - upgrade from std cluster # bbeelamk 11/03/14 - Fix bug 19849644 # bbeelamk 10/30/14 - Fix bug 19688282 # xyuan 09/17/14 - Incorporate review comments # luoli 07/24/14 - Creation # package oraClusterwareComp::oraasm; use parent 'oraClusterwareComp'; use strict; use English; use Carp; use File::Copy; use File::Path; use File::Find; use File::Basename; use File::Spec::Functions; use Env qw(SRVM_TRACE); use File::Glob qw(:glob); use crsutils; use s_crsutils; use crsgpnp; ### # Constants for attribute values returned from getvalFromManifestFile() ### # Attribute value for ASM_ACCESS_MODE use constant ASM_DSKMODE_INVALID => '0'; use constant ASM_DSKMODE_INDIRECT => '1'; use constant ASM_DSKMODE_DIRECT => '2'; # Attribute value for AFD_STATE use constant AFD_STATE_NOAFD => '0'; use constant AFD_STATE_CONFIGURED => '1'; use constant AFD_STATE_LOADED => '2'; use constant AFD_STATE_UNKNOWN => '3'; # Steps to configure ASM on the first node use constant IMPORT_ASM_CREDS_FARASM => 'asm_ConfigFirstNode_step_1'; use constant CONFIGURE_ASM => 'asm_ConfigFirstNode_step_2'; # Steps to configure ASM on every node use constant REDIRECT_ASMAPXIOS_AUDIT_LOGS => 'asm_ConfigCurrentNode_step_1'; # Steps to upgrade ASM on the first node use constant UPGRADE_ASM => 'asm_UpgradeFirstNode_step_1'; # Steps to downgrade ASM on the first node use constant DOWNGRADE_ASM => 'asm_DowngradeCurrentNode_step_1'; sub new { my $class = shift; # Pass the component name into the constructor my $componentName = @_; my $self = $class->SUPER::new(@_); $self->_initialize(); return $self; } # Initialization sub _initialize { my $self = shift; my $compName = $self->compName; trace("Perform initialization tasks before configuring $compName"); } # # Each specific component, which inherits from this class, can # reimplement (override) the following methods: # Is the component supported based on platform and user input sub isSupported { # Supported on all platforms return TRUE; } # Which component does this depend on sub dependsOn { my @dependency; push @dependency, "CSS"; return @dependency; } # Has the component already been configured sub isConfigured { #TODO return FALSE; } # # Methods for install # # Function: Redirect the ASM/IOS/APX audit logs into the SYSLOG # in case of Domain Services Cluster on Linux, AIX, # and Solaris-X64, otherwise skipped # This configuration needs to be done on every node # Args : None # Returns : SUCCESS if success # FAILED if failed sub redirectAuditLogs { my $fh; my $restartcmd; my $syslog_conf; my $service_name; my $ckrsyslog; my $cksyslog; my $syslog_str; my $asmrotate_conf; my $iosrotate_conf; my $apxrotate_conf; my $asmrotate_str; my $iosrotate_str; my $apxrotate_str; # Only redirect audit logs if DSC is configured if(!isDSCConfigured()) { trace("Skip configuring audit log redirection " . "becuase DSC is not configured"); return SUCCESS; } # Configure /etc/syslog.conf for ASM auditing # Check the OS to edit files and execute commands accordingly if($^O eq "linux") { # use the backtics to execute cmd and capture its stdout $ckrsyslog = `/sbin/service rsyslog status`; $cksyslog = `/sbin/service syslog status`; if($ckrsyslog =~ /running/) { trace("rsyslog service is running"); $syslog_conf = "/etc/rsyslog.conf"; $service_name = "rsyslog"; } elsif($cksyslog =~ /running/) { trace("syslog service is running"); $syslog_conf = "/etc/syslog.conf"; $service_name = "syslog"; } else { trace("Could not find syslog or rsyslog service"); return FAILED; } # Must use TABs to separate pointers and log files $syslog_str = "local0.none;local1.none;local2.none\t\t/var/log/messages\n"; $asmrotate_conf = "/etc/logrotate.d/oraasmaudit"; $iosrotate_conf = "/etc/logrotate.d/oraiosaudit"; $apxrotate_conf = "/etc/logrotate.d/oraapxaudit"; # Configure logrotate to manage syslog log files $asmrotate_str = "/var/log/oraasmaudit.log {\n" . " weekly\n" . " rotate 4\n" . " compress\n" . " copytruncate\n" . " delaycompress\n" . " notifempty\n" . "}"; $iosrotate_str = "/var/log/oraiosaudit.log {\n" . " weekly\n" . " rotate 4\n" . " compress\n" . " copytruncate\n" . " delaycompress\n" . " notifempty\n" . "}"; $apxrotate_str = "/var/log/oraapxaudit.log {\n" . " weekly\n" . " rotate 4\n" . " compress\n" . " copytruncate\n" . " delaycompress\n" . " notifempty\n" . "}"; # Set the command to restart syslog/rsyslog service $restartcmd = "/sbin/service " . $service_name . " restart"; } elsif($^O eq "solaris") { # Only redirect logs for Solaris.X64 my $version = `uname -a`; if($version =~ /sparc/i) { trace("Running OS detected as Solaris SPARC, thus skipping " . "configuring audit log redirection"); return SUCCESS; } $cksyslog = `svcs -a | grep system-log:default`; $ckrsyslog = `svcs -a | grep system-log:rsyslog`; # root@ca:~# svcs -a | grep "system-log" # disabled 6:13:13 svc:/system/system-log:rsyslog # online 6:47:53 svc:/system/system-log:default if($ckrsyslog =~ /online/) { trace("rsyslog service is running"); $syslog_conf = "/etc/rsyslog.conf"; } elsif($cksyslog =~ /online/) { trace("syslog service is running"); $syslog_conf = "/etc/syslog.conf"; } else { trace("Could not find syslog or rsyslog service"); return FAILED; } # Must use TABs to separate pointers and files $syslog_str = "local0.none;local1.none;local2.none\t\t/var/adm/messages\n"; $restartcmd = "/usr/sbin/svcadm disable svc:/system/system-log && " . "/usr/sbin/svcadm enable svc:/system/system-log"; } elsif($^O eq "aix") { $ckrsyslog = `lssrc -s rsyslogd`; $cksyslog = `lssrc -s syslogd`; # bash-4.2$ lssrc -a | grep log sample output: # syslogd ras 3670100 active # tracelogd inoperative if($ckrsyslog =~ /active/) { trace("rsyslog service is running"); $syslog_conf = "/etc/rsyslog.conf"; $service_name = "rsyslogd"; } elsif($cksyslog =~ /active/) { trace("syslog service is running"); $syslog_conf = "/etc/syslog.conf"; $service_name = "syslogd"; } else { trace("Could not find syslog or rsyslog service"); return FAILED; } # Must use TABs to separate pointers and files $syslog_str = "local0.none;local1.none;local2.none\t\t/var/adm/messages\n"; $restartcmd = "refresh -s " . $service_name; } else { # Skip configuring log redirection if other operating systems trace("The OS is $^O, thus skipping redirecting audit logs"); return SUCCESS; } # Read configuration file into an array for processing my $check_fh; my $match; open($check_fh, "<", $syslog_conf) or return FAILED; my @file_lines = <$check_fh>; close($check_fh); # Check if the target levels are already in use or not if((scalar(grep(/local0.info/, @file_lines) > 0)) || (scalar(grep(/local1.info/, @file_lines) > 0)) || (scalar(grep(/local2.info/, @file_lines) > 0))) { # Some versions of OS do not support -e option or egrep my $ckcmd = "grep local0.info $syslog_conf &&" . "grep local1.info $syslog_conf &&" . "grep local2.info $syslog_conf"; $match = `$ckcmd`; trace("Found conflicts in $syslog_conf : $match"); trace("The levels for redirecting ASM/IOS/APX audit logs " . "are already in use. Please edit the config file to " . "make sure that local0-2 are available for use by Oracle"); return FAILED; } # Create the initial log file for ASM audit records to redirect in my $asmlogcmd = system("touch /var/log/oraasmaudit.log"); my $ioslogcmd = system("touch /var/log/oraiosaudit.log"); my $apxlogcmd = system("touch /var/log/oraapxaudit.log"); if($asmlogcmd != 0 || $ioslogcmd != 0 || $apxlogcmd != 0) { trace("touch /var/log/asmaudit.log return code: $asmlogcmd \n" . "touch /var/log/iosaudit.log return code: $ioslogcmd \n" . "touch /var/log/apxaudit.log return code: $apxlogcmd \n"); trace("Failed to create the audit logs for ASM instances"); return FAILED; } # Set owner and permission of audit log files my @auditlogarr = ('/var/log/oraasmaudit.log', '/var/log/oraiosaudit.log', '/var/log/oraapxaudit.log'); foreach my $auditlog (@auditlogarr) { if(-e $auditlog) { s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_ASM_GROUP'), $auditlog); s_set_perms ("0644", $auditlog); } } my $audit_str = "\nlocal0.info\t\t\t/var/log/oraasmaudit.log\n" . "local1.info\t\t\t/var/log/oraiosaudit.log\n" . "local2.info\t\t\t/var/log/oraapxaudit.log\n"; open($fh, '>>', $syslog_conf) or return FAILED; print $fh $audit_str; my $read_fh; my $write_fh; # Read configuration file into an array for processing open($read_fh, "<", $syslog_conf) or return FAILED; my @filelines = <$read_fh>; close($read_fh); open($write_fh, ">", $syslog_conf) or return FAILED; my $found_flag = 0; foreach my $line (@filelines) { # If the configuration file already contains 'messages' # Insert the setting in the line instead of appending if (($line =~ m@/var/log/messages@) || ($line =~ m@/var/adm/messages@)) { my @parts = split(/\s+/, $line); $line = $parts[0] . ";local0.none;local1.none;local2.none" . "\t\t$parts[1]\n"; trace("Writing $line into $syslog_conf"); $found_flag = 1; } print {$write_fh} $line; } close($write_fh); if($found_flag == 0) { # Append the new config string in the end of the configuration file open($fh, '>>', $syslog_conf) or return FAILED; trace("Writing $syslog_str into $syslog_conf"); print $fh $syslog_str; } # Create the logrotate files and write the rotation rules # This will only be configured for linux if($^O eq "linux") { open($fh, '>', $asmrotate_conf); print $fh $asmrotate_str; open($fh, '>', $iosrotate_conf); print $fh $iosrotate_str; open($fh, '>', $apxrotate_conf); print $fh $apxrotate_str; } close($fh); # Restart the syslog/rsyslog service to bring new # configuration into effect my $temp = system($restartcmd); if($temp !=0) { trace("Failed to restart syslog/rsyslog service with $restartcmd"); return FAILED; } return SUCCESS; } # Function: Deconfiguration for redirecting the ASM/IOS/APX audit # logs into the SYSLOG in case of Domain Services Cluster # on Linux, AIX, and Solaris-X64, otherwise skipped # This deconfiguration needs to be done on every node # Args : None # Returns : SUCCESS if success # FAILED if failed sub deconfigureRedirectAuditLogs { my $read_fh; my $write_fh; my $restartcmd; my $syslog_conf; my $service_name; my $ckrsyslog; my $cksyslog; my $config_line; my $service_flag = 0; # Deconfigure audit logs only if DSC is configured if(!isDSCConfigured()) { trace("Skip deconfiguring audit log redirection " . "becuase DSC is not configured"); return SUCCESS; } if($^O eq "linux") { # use the backtics to execute cmd and capture its stdout $ckrsyslog = `/sbin/service rsyslog status`; $cksyslog = `/sbin/service syslog status`; if($ckrsyslog =~ /running/) { trace("rsyslog service is running"); $syslog_conf = "/etc/rsyslog.conf"; $service_name = "rsyslog"; } elsif($cksyslog =~ /running/) { trace("syslog service is running"); $syslog_conf = "/etc/syslog.conf"; $service_name = "syslog"; } else { # Mark the flag if it could not find running service $service_flag = 1; } # Remove the log rotation config files my @files = ("/etc/logrotate.d/oraasmaudit", "/etc/logrotate.d/oraiosaudit", "/etc/logrotate.d/oraapxaudit"); # Attempt to delete files only if they exist to prevent false return foreach my $file (@files) { if(-e $file) { trace("Deleting log rotation file : $file"); unlink($file) or return FAILED; } } # Set the target line to remove based on running operating system $config_line = "local0.none;local1.none;local2.none\t\t/var/log/messages"; # Set the command to restart syslog/rsyslog service $restartcmd = "/sbin/service " . $service_name . " restart"; } elsif($^O eq "solaris") { my $version = `uname -a`; if($version =~ /sparc/i) { trace("Running OS detected as Solaris SPARC, thus skipping " . "deconfiguring audit log redirection"); return SUCCESS; } $cksyslog = `svcs -a | grep system-log:default`; $ckrsyslog = `svcs -a | grep system-log:rsyslog`; # root@ca:~# svcs -a | grep "system-log" # disabled 6:13:13 svc:/system/system-log:rsyslog # online 6:47:53 svc:/system/system-log:default if($ckrsyslog =~ /online/) { trace("rsyslog service is running"); $syslog_conf = "/etc/rsyslog.conf"; } elsif($cksyslog =~ /online/) { trace("syslog service is running"); $syslog_conf = "/etc/syslog.conf"; } else { $service_flag = 1; } $config_line = "local0.none;local1.none;local2.none\t\t/var/adm/messages"; $restartcmd = "/usr/sbin/svcadm disable svc:/system/system-log && " . "/usr/sbin/svcadm enable svc:/system/system-log"; } elsif($^O eq "aix") { $ckrsyslog = `lssrc -s rsyslogd`; $cksyslog = `lssrc -s syslogd`; # bash-4.2$ lssrc -a | grep log sample output: # syslogd ras 3670100 active # tracelogd inoperative if($ckrsyslog =~ /active/) { trace("rsyslog service is running"); $syslog_conf = "/etc/rsyslog.conf"; $service_name = "rsyslogd"; } elsif($cksyslog =~ /active/) { trace("syslog service is running"); $syslog_conf = "/etc/syslog.conf"; $service_name = "syslogd"; } else { $service_flag = 1; } $config_line = "local0.none;local1.none;local2.none\t\t/var/adm/messages"; $restartcmd = "refresh -s " . $service_name; } else { # Skip deconfiguring log redirection if other operating systems trace("The OS is $^O, skipping deconfiguring audit log redirection"); return SUCCESS; } # Remove the redirected audit log files my @audfiles = ("/var/log/oraasmaudit.log", "/var/log/oraiosaudit.log", "/var/log/oraapxaudit.log"); # Attempt to delete files only if they exist to prevent false return foreach my $audfile (@audfiles) { if(-e $audfile) { trace("Deleting redirected audit log file : $audfile"); unlink($audfile) or return FAILED; } } # If service flag marked as it could not find service, # Skip deconfiguring the system service configuration file if($service_flag == 0) { # Read configuration file into an array for processing open($read_fh, "<", $syslog_conf) or return FAILED; my @file_lines = <$read_fh>; close($read_fh); # Rewrite configuration file with the line removed open($write_fh, ">", $syslog_conf) or return FAILED; # Set the flag to prevent the config file from deleting existing # configuration for other use if not redirected for ASM/IOS/APX audit logs my $redirect_flag = 0; # Iterate through the lines and skip writing target lines to delete foreach my $line (@file_lines) { # Remove the setting if the configuration already has 'messages' if($line =~ m@;$config_line@) { $line =~ s/;local0.none;local1.none;local2.none//; } # Use @ instead of / for matching to avoid conflict with directory path if(($line =~ m@local0.info\t\t\t/var/log/oraasmaudit.log@) || ($line =~ m@local1.info\t\t\t/var/log/oraiosaudit.log@) || ($line =~ m@local2.info\t\t\t/var/log/oraapxaudit.log@)) { trace("Deleting configuration line : $line"); $redirect_flag = 1; next; # continue } # Prevent the config file from deleting existing information # if not redirected for audit logs if($redirect_flag == 1 && $line =~ m@$config_line@) { trace("Deleting configuration line : $line"); next; # continue } # Preserve all other lines in the configuration file print {$write_fh} $line; } close($write_fh); } # restart the syslog/rsyslog service to bring deconfiguration into effect my $temp = system($restartcmd); if($temp !=0) { trace("Failed to restart syslog/rsyslog service with $restartcmd"); return FAILED; } return SUCCESS; } # Function: Back up the ASM PWfile in OCR backup disk group # Args : [0] - CRS home to use. # Returns : SUCCESS if success # FAILED if failed sub backupASMPWfile { my ($crshome) = @_; if (! $crshome) { print_error(4); die(dieformat(004)); } if (!(-d $crshome)) { print_error(5, $crshome); die(dieformat(005, $crshome)); } trace ("Oracle CRS home = $crshome"); my $dest; my $PWfile; my $asm_mode; my $ocrconfig = catfile($crshome, 'bin', 'ocrconfig'); my $asmcmd = catfile($crshome, "bin", "asmcmd"); my $oraocr = $CFG->compOCR; my $currentHome = saveOraHome(); my $backuploc = ""; my $backuplocflag = 0; my @cpcmd; my @cpout; my @splitout; my $cprc; # Bug 23297287 RE-RAN ROOTUPGRADE.SH HIT ERROR CLSRSC-509: INVALID ASM MODE if ($CFG->UPGRADE) { $asm_mode = $CFG->oldconfig('ASM_MODE'); if (!isOldVersionLT121()) { $asmcmd = $asmcmd . " --nocp"; } } else { $asm_mode = getASMMode(); $asmcmd = $asmcmd . " --nocp"; } setOraHomeSID($asm_mode, $crshome); # For UPGRADE stage - discover OCR backup disk group by ocrconfig cmd if ($CFG->UPGRADE) { trace("Backing up ASM pwfile in upgrade stage..."); # Backup the ASM PWfile only if ASM version is 12.1 or later # Skip ASM PWfile backup if ASM version is 12.1 or later in preUpgradeCheck trace("ASM version >= 12.1"); # Fetch the current ASM PWfile before back it up in OCR backup diskgroup $PWfile = getASMPWfile($crshome); trace("The ASM pwfile to be copied for asmcmd cp is $PWfile"); my $cmd = "$ocrconfig -showbackuploc"; trace("ocrconfig -showbackuploc to get ASM PWfile backup location"); my ($rc, @output) = system_cmd_capture($cmd); if ($rc == 0) { if ((scalar(grep(/\+/, @output)) > 0) && ($output[0] =~ /\[(.*?)\]/)) { trace("OCR backup location is already set to DG $1"); # Append "+" before the diskgroup name $backuploc = "+" . $output[0]; # Set the flag to 1 if showbackuploc returns a DG path $backuplocflag = 1; } } # Get the OCR DG location if showbackuploc returns a local directory # Or if showbackuploc failed with non-zero return code if (($rc != 0) || ($backuplocflag == 0)) { trace("$ocrconfig -showbackuploc Return code: $rc \nOutput: @output"); trace("Calling getOCRLoc() to get ASM PWfile backup location"); my @ocroutput = $oraocr->getOCRLoc(); my $ocrType = shift @ocroutput; if ($ocrType != 2) { # 0: Error, fail to get OCR location from /etc/oracle/ocr.loc file; # 1: OCR is on NAS; # 2: OCR is on DG. trace("OCR is not on DG, hence ASM password file backup failed"); return FAILED; } my $value = shift @ocroutput; # Check if the compatible.asm of diskgroup is 12.1 or later # Skip backing up ASM PWfile if compatible.asm is less than 12.1 # asmcmd lsattr -G DATA -l compatible.asm sample output: # Name Value # compatible.asm 11.2.0.2.0 my @ckcmd = ($asmcmd, "lsattr", "-G", $value, "-l", "compatible.asm"); # Use backticks instead of open() to execute external command 'asmcmd' # due to bug 18493777 my @ckout = run_cmd_as_usr_with_backticks(\@ckcmd, $CFG->params('ORACLE_OWNER')); my $ckrc = shift(@ckout); my $compatibleVersion; if ($ckrc == 0) { foreach my $line (@ckout) { chomp($line); if ($line =~ /compatible\.asm/) { my @word = split(/\s+/, $line); $compatibleVersion = $word[1]; last; # break } } } trace("ASM compatibility version: $compatibleVersion"); if (! $compatibleVersion) { trace("Unable to get ASM compatibility version"); return FAILED; } # Skip backing up ASM PWfile if compatible.asm is less than 12.1 my $compareVersion = "12.1.0.0.0"; if (versionComparison($compatibleVersion, $compareVersion) == -1) { trace("ASM PWfile backup on DG was skipped as the operation " . "requires compatible.asm = 12.1 but diskgroup '$value'" . " has compatible.asm = $compatibleVersion"); return SUCCESS; } trace("Found available OCR backup DG $value"); # Append "+" before the diskgroup name $backuploc = "+" . $value; } # Hardcode the name of ASM password backup copy to prevent illegal # tokens such as "+" when copying from local to diskgroup $dest = $backuploc . "/orapwASM_backup"; trace("The destination for asmcmd cp in upgrade stage is $dest"); } else { trace("Backing up ASM pwfile in install stage..."); # Fetch the current ASM PWfile before backup it in OCR backup diskgroup $PWfile = $CFG->ASM_PWD_FILE; trace("The ASM pwfile to be copied for asmcmd cp is $PWfile"); # For INSTALL stage - get OCR backup dg from config parameter if ($CFG->params('CDATA_BACKUP_DISK_GROUP')) { $backuploc = $CFG->params('CDATA_BACKUP_DISK_GROUP'); } else { $backuploc = $CFG->params('CDATA_DISK_GROUP'); } $dest = "+" . $backuploc . "/orapwASM_backup"; trace("The destination for asmcmd cp in install stage is $dest") } # Copy the pwfile into OCR backup diskgroup to backup pwfile @cpcmd = ($asmcmd, "cp", $PWfile, $dest); trace("Invoking asmcmd cp $PWfile $dest"); $cprc = run_as_user2($CFG->params('ORACLE_OWNER'), \@cpout, @cpcmd); trace("asmcmd cp Return code: $cprc \nOutput: @cpout"); if ($cprc != 0) { print_lines(@cpout); return FAILED; } trace("Successfully backed up ASM PWfile $PWfile at $dest"); # Write the ASM PWfile backup copy location to global checkpoint updateASMPWBackupCkpt($dest); resetOraHome($currentHome); trace("Successfully wrote the ASM PWfile backup copy location to " . "global checkpoint"); return SUCCESS; } # Function: Update the global checkpoint for ASM PWfile backup # Args : [0] - The value of ASM PWfile backup location to # write in global checkpoint. # Returns : None. sub updateASMPWBackupCkpt { my ($value) = @_; # Write the ASM PWfile backup copy location or a default value for # skipping backup ASM PWfile to global checkpoint my $ckptName = "ROOTCRS_ASMPWBACKUP"; if (!isCkptexist($ckptName, "-global")) { trace("Writing checkpoint for ASM PWfile backup"); writeGlobalCkpt($ckptName, CKPTSTART, "-global"); writeCkptProperty("ROOTCRS_ASMPWBACKUP", "LOCATION", $value, "-global"); $CFG->wipCkptName("ROOTCRS_ASMPWBACKUP"); } else { if (!isCkptPropertyExists("ROOTCRS_ASMPWBACKUP", "LOCATION","-global")) { trace("Removing older checkpoints"); remove_checkpoints(); writeCkpt("ROOTCRS_ASMPWBACKUP", CKPTSTART, "-global"); writeCkptProperty("ROOTCRS_ASMPWBACKUP", "LOCATION", $value, "-global"); $CFG->wipCkptName("ROOTCRS_ASMPWBACKUP"); } } return; } # Configure actions on first node sub configureFirstNode { my $self = shift; my $stepIndicator = shift; my $compName = $self->compName; trace("Executing the step [$stepIndicator] to configure $compName ". "on the first node"); if (REDIRECT_ASMAPXIOS_AUDIT_LOGS eq $stepIndicator) { # Redirect ASM|IOS|APX audit logs my $redirectsuccess = SUCCESS; $redirectsuccess = redirectAuditLogs(); if ($redirectsuccess != SUCCESS) { trace("Redirecting ASM/IOS/APX audit logs to SYSLOG failed"); } return SUCCESS; } elsif (IMPORT_ASM_CREDS_FARASM eq $stepIndicator) { if(isFarASM()) { # Copy and import ASM credentials # The credentials need to be imported before starting CSS in X mode trace("Copy and import ASM credentials on the first node of an ASM client cluster"); copy_asm_credentials(); } return SUCCESS; } elsif (CONFIGURE_ASM eq $stepIndicator) { if ((! isFarASM()) && $CFG->ASM_STORAGE_USED) { trace("Start to configure legacy/near ASM on the first node ..."); # configure ASM if legacy ASM or near ASM # Dependency: CSS (CSS need to be started in exclusive mode before # configuring ASM.) return configureASM(); } return SUCCESS; } else { croak "Step indicator out of bounds"; } } # Configure actions on every node other than first node sub configureNonFirstNode { my $self = shift; my $stepIndicator = shift; my $compName = $self->compName; if (REDIRECT_ASMAPXIOS_AUDIT_LOGS eq $stepIndicator) { trace("Executing the step [$stepIndicator] to configure $compName ". "on the non-first node"); # Redirect ASM|IOS|APX audit logs my $redirectsuccess = SUCCESS; $redirectsuccess = redirectAuditLogs(); if ($redirectsuccess != SUCCESS) { trace("Redirecting ASM/IOS/APX audit logs to SYSLOG failed"); } return SUCCESS; } elsif (! isLegacyASM()) { # Import credentials for ASM on non-first nodes, including rim nodes # The ASM mode doesn't get updated in the local gpnp profile # until the remote ASM is enabled. trace("Importing asm credentials"); import_asm_credentials(); return SUCCESS; } } # Configure action on first node after the configured stack # has been started sub postConfigFirstNode { if ((! isFarASM()) && $CFG->ASM_STORAGE_USED) { # create backup DG if legacy ASM or near ASM my $success = create_backup_dg(); # Bug 24292731 - the ASM password backup should happen # after the backup DG was created during install stage # Only backup ASM password if backup diskgroup was # successfully created, otherwise skipped if ($success == TRUE) { # Backup the ASM password file my $crshome = $CFG->ORA_CRS_HOME; my $backupsuccess = SUCCESS; $backupsuccess = backupASMPWfile($crshome); if ($backupsuccess != SUCCESS) { trace("ASM password file backup failed..."); } } else { trace("Could not backup ASM PWfile onto diskgroup as " . "create_backup_dg() failed"); } return $success; } return SUCCESS; } # Function: Check whether ASM password file is set in CRS or not # Args : [0] - CRS home to use # Returns : TRUE on success # FALSE on failure sub isASMPWfileConfigured { my ($crshome) = @_; if(!$crshome) { print_error(4); die(dieformat(004)); } if(!(-d $crshome)) { print_error(5, $crshome); die(dieformat(005, $crshome)); } trace ("Oracle CRS home = $crshome"); my $asmPwdFile = ""; my $crsctl = catfile($crshome, 'bin', 'crsctl'); trace("Invoking crsctl stat res ora.asm -p to retrieve ASM password file..."); my @output = system_cmd_capture($crsctl, "stat", "res", "ora.asm", "-p"); my $rc = shift(@output); if (0 != $rc) { print_lines(@output); trace("crsctl stat res ora.asm -p failed with status $rc"); die(dieformat(180, "crsctl stat res ora.asm -p")); } foreach my $line (@output) { chomp $line; if ($line =~ /^PWFILE=(.*)$/) { if ($1 ne "") { $asmPwdFile = trim($1); trace ("Retrieved ASM Password file by crsctl is $asmPwdFile"); return (TRUE, $asmPwdFile); } else { return (FALSE, $asmPwdFile); } } } return (FALSE, $asmPwdFile); } # Configure action on other nodes than the first node after # the configured stack has been started sub postConfigNonFirstNode { return SUCCESS; } # Function: Get the ASM password file # Args : [0] - CRS home to use # Returns : $asmPwdFile - ASM password file sub getASMPWfile { my ($crshome) = @_; if (!$crshome) { print_error(4); die(dieformat(004)); } my $configured; my $asmPwdFile; ($configured, $asmPwdFile) = isASMPWfileConfigured($crshome); if ($configured) { trace ("Retrieved ASM Password file by crsctl is $asmPwdFile"); return $asmPwdFile; } else { trace ("The ASM password file location is not set in " . "the output of crsctl stat res ora.asm -p, " . "using default location instead..."); } # Use the default ASM password file location if the value of password file # in crsctl output is empty if ($CFG->platform_family eq "windows") { $asmPwdFile = catfile($crshome, 'database', "PWD+ASM.ora"); } else { $asmPwdFile = catfile($crshome, 'dbs', "orapw+ASM"); } if (!(-e $asmPwdFile)) { trace("ASM PW file " . $asmPwdFile . " inaccessible"); # CLSRSC-661, "The Oracle ASM password file does not exist # at location %(1)s." die(dieformat(661, $asmPwdFile)); } return $asmPwdFile; } #------------------------------------------------------------------------------ # Function: ASM PreUpgrade checks # Args : None # Returns : SUCCESS if success # FAILED if failed # # The method for global checks related to ASM component before upgrade # This method is called only on the first node when the stack is up. # # Notes : # The ASM SP file has to be accessible. # The disk group in which the ASM SP file must be mounted. # # Check upgrading from 12.1 and later. # If the ASM PWD file is configured, it must be accessible. # If the ASM PWD file is in a disk group, the disk group must be # mounted. # # This method should use commands from the OLD_CRS_HOME #------------------------------------------------------------------------------ sub preUpgradeCheck { my @diskgroups; my $asmcmd = catfile($CFG->OLD_CRS_HOME, "bin", "asmcmd"); my @spcmd = ($asmcmd, "spget"); my @lsdgcmd = ($asmcmd, "lsdg", "--suppressheader"); my @lsdgcapout; my @parse; my @dgcmd; my @spcapout; my @dgcapout; my @splitout; my $asm_mode = $CFG->oldconfig('ASM_MODE'); my $currentHome = saveOraHome(); my $dgname; my $lsdgrc; my $sprc; my $dgrc; my $PWfile; my $SPfile; my $PWfound = 0; my $SPfound = 0; my $PWdg = 0; my $crshome = $CFG->OLD_CRS_HOME; setOraHomeSID($asm_mode, $crshome); # bug 24358034 - reset asmcmd diag directory permissions if it exists my $asmcmddiagdir = catfile($crshome, "log", "diag", "asmcmd"); if (check_dir($asmcmddiagdir) == SUCCESS) { trace("ASMCMD directory $asmcmddiagdir exists, fix ownerships" . "$CFG->params('ORACLE_OWNER') $CFG->params('ORA_DBA_GROUP')"); s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $asmcmddiagdir) || die(dieformat(152, $asmcmddiagdir)); s_set_perms("0775", $asmcmddiagdir) || die(dieformat(153, $asmcmddiagdir)); } # In a Production env check to see if the ASM password is configured in CRS. # If old version is pre 12.1, and check to see if there are multiple files # in the directory that match the prefix. Fail with error message if there # are multiple password files. # e.g. $OH/dbs/orapw+ASM1, $OH/dbs/orapw+ASM2 # If old version is 12.1, then check if the password file is configured in CRS # If it configured in CRS and in a disk group. Nothing to check. # If it is configured in CRS and in file system, check if there are multiple # files that match the prefix, and if there are, ask user to delete other # files. This is because ASMCA will pick up only one file from the old home. # If it is not configured, check if there are multiple files in the directory # that match the prefix. If there are multiple files, fail with a message # to remove all files and keep only the file with the user data. if (!is_dev_env()) { my $directory; my $prefix; my $OH = $CFG->params('ORACLE_HOME'); # Set the directory and prefix of ASM password file to search based on OS if($CFG->platform_family eq "windows") { $directory = $OH . "/database"; $prefix = "PWD+ASM*.ora"; } else { $directory = $OH . "/dbs"; $prefix = "orapw+ASM*"; } # Count the number of ASM password file with target prefix in directory my @pwdfileList = <$directory/$prefix>; my $pwdfileListCount = scalar @pwdfileList; trace("Found $pwdfileListCount ASM password files under $directory"); # Stop upgrade if the number of ASM password files found is more than one # Prompt user to keep only one ASM password file and remove others if ($pwdfileListCount > 1) { trace("Multiple ASM password files found under $directory, stop upgrade"); # CLSRSC-667, "found multiple ASM password files under '%s'" die(dieformat(667, $directory)); } } # Check the PW file only if the version is 12.1 or later if(!isOldVersionLT121()) { # Use --nocp optione in asmcmd if ASM version is 12.1 or later $asmcmd = $asmcmd . " --nocp"; @spcmd = ($asmcmd, "spget"); @lsdgcmd = ($asmcmd, "lsdg", "--suppressheader"); $PWfile = getASMPWfile($crshome); trace("The ASM PW file returned by getASMPWfile() is $PWfile"); # Check whether PW file is in a disk group or not if($PWfile =~ m#\+(\S+)/#) { $PWdg = 1; @splitout = split('/', $PWfile); $splitout[0] =~ /\+(\S+)/; $PWfile = $1; } # getASMPWfile already checked the existence of ASM password file if not # on the diskgroup else { $PWfound = 1; } # Backup the ASM PWfile if ASM version is 12.1 or later my $backupsuccess = SUCCESS; $backupsuccess = backupASMPWfile($crshome); if($backupsuccess != SUCCESS) { trace("ASM password file backup failed..."); } } else { # Do Not check PW file if the version is less than 12.1 $PWfound = 1; # Do Not backup PW file if the version is less than 12.1 my $skipflag = "SkipBackup"; updateASMPWBackupCkpt($skipflag); } trace("Invoking asmcmd spget"); $sprc = run_as_user2($CFG->params('ORACLE_OWNER'), \@spcapout, @spcmd); trace("asmcmd spget Return code: $sprc \nOutput: @spcapout"); if($sprc != 0) { print_lines(@spcapout); # CLSRSC-659, "failed to retrieve the Oracle ASM SPFILE location" die(dieformat(659)); } @splitout = split('/', $spcapout[0]); $splitout[0] =~ /\+(\S+)/; $SPfile = $1; # Fetch the information of each diskgroup trace("Invoking asmcmd lsdg --suppressheader"); $lsdgrc = run_as_user2($CFG->params('ORACLE_OWNER'), \@lsdgcapout, @lsdgcmd); if($lsdgrc != 0) { trace("Failed to get disk groups \n asmcmd lsdg Return code: $lsdgrc " . "Output: @lsdgcapout \n"); return FAILED; } # Iterate through each row in output and get the name of each diskgroup foreach my $row (@lsdgcapout) { @parse = split(' ', $row); $dgname = $parse[-1]; # Remove the additional slash in the end of diskgroup name @parse = split('/', $dgname); $dgname = $parse[0]; # Add the parsed diskgroup name into the array push(@diskgroups, $dgname); } trace("All parsed diskgroup names: @diskgroups"); # Iterate through the disk list to check whether PW and SP files exist foreach my $diskgroup (@diskgroups) { # Bug-25556203: Remove asmcmd chkdg because it is too costly # Eventually this will be done by scrubbing OCR file which will be # added later if($PWdg == 1) { if($diskgroup =~ /^($PWfile)\b/i) { $PWfound = 1; } } if($diskgroup =~ /^($SPfile)\b/i) { $SPfound = 1; } } if(($PWdg) && (!$PWfound)) { trace("ASM PW file " . $PWfile . " inaccessible"); # CLSRSC-656, "The disk group '%(1)s' containing the Oracle ASM password # file '%(2)s' is not mounted." die(dieformat(656, $PWfile)); } if(!$SPfound) { trace("ASM SP file " . $SPfile . " inaccessible"); # CLSRSC-657, "The disk group '%(1)s' containing the Oracle ASM SPFILE # '%(2)s' is not mounted." die(dieformat(657, $SPfile)); } resetOraHome($currentHome); return ($PWfound && $SPfound) ? SUCCESS : FAILED; } # The method for local checks related to ASM component before upgrading the first node sub upgradeCheckFirstNode { return SUCCESS; } # The method for local checks related to ASM component before upgrading the middle node sub upgradeCheckMiddleNode { return SUCCESS; } # The method for local checks related to ASM component before upgrading the last node sub upgradeCheckLastNode { return SUCCESS; } # Upgrade action on first node sub upgradeFirstNode { my $self = shift; my $stepIndicator = shift; my $compName = $self->compName; trace("Executing the step [$stepIndicator] to upgrade $compName ". "on the first node"); # Handle upgrades for different source versions. if (UPGRADE_ASM eq $stepIndicator) { my $startRolling = (isOldVersionLT121() && isRolling()) ? TRUE: FALSE; trace("ASMCA startRolling: $startRolling"); my $asmConf = $CFG->oldconfig('ASM_CONFIGURED'); my @old_ver = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $oldCRSVersion = join('.',@old_ver); trace("ASM_CONFIGURED: $asmConf"); trace("Old CRS version: <$oldCRSVersion>"); if (isRolling()) { trace("First node to upgrade in rolling mode"); if ($asmConf) { print_info(468); } else { print_info(524); } # CRSCTL sets Clusterware and ASM to rolling migration if source GI # version >= 12.1.0.1, otherwise, it is ASMCA's duty if ((! isOldVersionLT121()) && (! startRollingUpgrade())) { die(dieformat(511)); } if (($asmConf) && (! upgradeASM(TRUE, TRUE, $CFG->oldconfig('ORA_CRS_HOME'), $startRolling, $oldCRSVersion))) { die(dieformat(305)); } if ($asmConf) { print_info(469); } else { print_info(525); } } else { trace("First node to upgrade in non-rolling mode"); if ($asmConf) { print_info(470); if (! upgradeASM(TRUE, FALSE, $CFG->oldconfig('ORA_CRS_HOME'), $startRolling, $oldCRSVersion)) { die(dieformat(306)); } print_info(471); } } } else { croak "Step indicator out of bounds"; } return SUCCESS; } # Upgrade action on every node other than first and last node sub upgradeMiddleNode { my $self = shift; upgradeASMonNonFirstNodes(); return SUCCESS; } # Upgrade action on last node sub upgradeLastNode { my $self = shift; upgradeASMonNonFirstNodes(); return SUCCESS; } # Upgrade action on the first node after the higher version stack # has been started # # 1. Rename the ASM password file to the default password file name if it # is not in a disk group before copying. # 2. Update the resource (this step works because it is issued after # orasrvm->postConfigFirstNode()) and set the new location sub postUpgradeFirstNode { my $configured; my $asmPwdFile; # Check the name of the ASM password file, and rename if it is not in # disk group $asmPwdFile = getASMPWfile($CFG->ORA_CRS_HOME); trace("The ASM PW file returned by getASMPWfile() is $asmPwdFile"); # Check if PWD file is in a disk group if ($asmPwdFile !~ m#\+(\S+)/#) { my $OH = $CFG->params('ORACLE_HOME'); my $run_as_owner = TRUE; my $defaultname; my $status; # Get the default location of the ASM password file # Set the default name of ASM password file based on OS if($CFG->platform_family eq "windows") { $defaultname = "$OH/database/PWD+ASM.ora"; } else { $defaultname = "$OH/dbs/orapw+ASM"; } # Rename the password file name to be default rename $asmPwdFile, $defaultname; # Set the ASM PWfile location to the attribute in ora.asm trace("Executing srvctl modify asm -pwfile $defaultname"); $status = srvctl($run_as_owner, "modify asm -pwfile $defaultname", $CFG->ORA_CRS_HOME); if ($status == TRUE) { trace("srvctl modify asm -pwfile $defaultname ... success"); } else { print_error(164); trace("Setting ASM PWfile location $defaultname to the attribute " . "in ora.asm resource failed..."); die(dieformat(164)); } } # Copy ASM password file to all remote nodes # Upgrade from non-flex ASM if ((isLegacyASM())) { if (isOracleHomeShared()) { trace("GI home is shared, hence not copying ASM password file to " . "remote nodes"); } else { # This should return the either the password file in the disk group # or the default password file $asmPwdFile = getASMPWfile($CFG->ORA_CRS_HOME); trace("The ASM PW file returned by getASMPWfile() is $asmPwdFile"); # Copy PW to other nodes only if it is not in the disk group if($asmPwdFile !~ m#\+(\S+)/#) { trace("Copying ASM password file $asmPwdFile to all other nodes ..."); my $nodelist = $CFG->oldconfig('NODENAME_LIST'); copy_file_to_nodes($asmPwdFile, $asmPwdFile, TRUE, $nodelist) || die(dieformat(364)); } else { trace("ASM password file is on the diskgroup at " . $asmPwdFile . ", hence not copying it across nodes"); } } } # Set the ASM PWfile backup location to the attribute in ora.asm trace("Setting ASM PWfile backup attribute in upgrade stage..."); my $attrsuccess = SUCCESS; $attrsuccess = setASMPWfileBackupAttr(); if($attrsuccess != SUCCESS) { trace("Setting ASM PWfile backup location to the attribute in ora.asm " . "resouce failed..."); } return SUCCESS; } # Function: Get the ASM password file backup location from global checkpoint # Set the value to the attribute PWFILE_BACKUP in ora.asm # Skip setting the value to attribute PWFILE_BACKUP in ora.asm # if the version is less than 12.1 # Args : None # Returns : SUCCESS if success # FAILED if failed sub setASMPWfileBackupAttr { # Set the Attribute either after the upgrade on first node # Or in the install stage on first node my $crshome = $CFG->ORA_CRS_HOME; my $crsctl = catfile($crshome, "bin", "crsctl"); my $ckptName = "ROOTCRS_ASMPWBACKUP"; # Fetch the ASM PWfile backup location from global checkpoint if(isCkptexist($ckptName, "-global")) { if(isCkptPropertyExists("ROOTCRS_ASMPWBACKUP", "LOCATION","-global")) { my $backuploc = getCkptPropertyValue("ROOTCRS_ASMPWBACKUP", "LOCATION", "-global"); trace("The backup location read from global chkpt is $backuploc"); # Skip backing up ASM pwfile and setting attribute if version < 12.1 if($backuploc eq "SkipBackup") { trace("ASM version is less than 12.1, thus skip backing up " . "ASM password file."); return SUCCESS; } my $initstr = "PWFILE_BACKUP=" . $backuploc; my @out = system_cmd_capture($crsctl, "modify", "resource", "ora.asm", "-attr", $initstr, "-unsupported"); my $status = shift @out; trace("initstr is $initstr"); trace("The command $crsctl modify resource ora.asm " . "-attr $initstr returned wih $status"); if ($status != 0) { print_lines(@out); trace("$crsctl modify resource ora.asm -attr" . "failed with status $status"); return FAILED; } } else { trace("Failed to retrieve ASM PWfile backup location from global chkpt" . "because the property does not exist"); return FAILED; } } else { trace("Failed to retrieve ASM PWfile backup location from global chkpt" . "because the global chkpt does not exist"); return FAILED; } trace("Successfully set the ASM PWfile backup location to the attribute " . "PWFILE_BACKUP in ora.asm resource"); return SUCCESS; } # Upgrade action on every node other than first and last node # after the higher version stack has been started sub postUpgradeMiddleNode { return SUCCESS; } # Upgrade action on the last node after the higher version stack # has been started sub postUpgradeLastNode { return SUCCESS; } # # Methods for downgrade # # downgrade actions on nodes other than the last node sub downgradeNonLastNode { my $self = shift; my $stepIndicator = shift; my $compName = $self->compName; trace("Executing the step [$stepIndicator] to downgrade $compName ". "on nodes other than the last node."); if (DOWNGRADE_ASM eq $stepIndicator) { # ASM downgrade for online downgrade if (($CFG->ASMCADOWNGRADE) && isOldVersionLT121() && (FAILED == asmcaDowngrade())) { die(dieformat(458)); } } else { croak "Step indicator out of bounds"; } return SUCCESS; } # downgrade actions on the last node sub downgradeLastNode { my $self = shift; my $stepIndicator = shift; my $compName = $self->compName; trace("Executing the step [$stepIndicator] to downgrade $compName ". "on the last node"); if (DOWNGRADE_ASM eq $stepIndicator) { trace("Performing ASM downgrade on the last node ..."); # ASM downgrade for offline downgrade if (isOldVersionLT121() && isOCRonASM() && (FAILED == asmcaDowngrade())) { die(dieformat(458)); } } else { croak "Step indicator out of bounds"; } return SUCCESS; } # Whether or not the system reboot is needed after ASM is configured sub rebootRequired { return FALSE; } # How to start the component sub start { my $run_as_oracle_owner = SUCCESS; my $status = srvctl($run_as_oracle_owner, "start asm"); if ($status) { trace ("start asm ... success"); } else { print_error(113); return FAILED; } return SUCCESS; } # How to stop the component sub stop { my $run_as_oracle_owner = SUCCESS; my $status = srvctl($run_as_oracle_owner, "stop asm -f"); if ($status) { trace ("stop asm ... success"); } else { print_error(114); return FAILED; } return SUCCESS; } # # Methods must be implemented for deconfiguration # # Deconfiguration action on nodes other than the last node sub deconfigureNonLastNode { my $deconfigsuccess = SUCCESS; $deconfigsuccess = deconfigureRedirectAuditLogs(); if ($deconfigsuccess != SUCCESS) { trace("Deconfiguring the syslog files used for ASM/IOS/APX " . "audit log redirection failed"); } } # Deconfiguration action on the last node sub deconfigureLastNode { my $deconfigsuccess = SUCCESS; $deconfigsuccess = deconfigureRedirectAuditLogs(); if ($deconfigsuccess != SUCCESS) { trace("Deconfiguring the syslog files used for ASM/IOS/APX " . "audit log redirection failed"); } } # # Private methods # # private methods called by above APIs # configure ASM on the first node during fresh install sub configureASM { my $success = SUCCESS; trace("Configuring ASM for fresh install ..."); if (isODA()) { $success = configure_ASM_oda(); } else { $success = configure_ASM(); } return $success; } =head2 configure_ASM Creates or updates ASM =head3 Parameters None =head3 Returns TRUE - ASM configuration was created or updated FALSE - ASM configuration was not created or updated =head3 Notes This will start ASM as part of the configuration if it is successful =cut sub configure_ASM { my $status; my $ASMDISKS = $CFG->params('CDATA_DISKS'); my $ASM_DISCOVERY_STRING = $CFG->params('ASM_DISCOVERY_STRING'); my $success = TRUE; my $diskgroup; my $dollarDG = FALSE; trace ("Configuring ASM via ASMCA"); # Do not change the order of these parameters as asmca requires the # parameters to be in a specific order or it will fail my @runasmca = (catfile ($CFG->ORA_CRS_HOME, "bin", "asmca"), '-silent'); if ($CFG->params('CDATA_DISK_GROUP') ){ $diskgroup = $CFG->params('CDATA_DISK_GROUP'); if ($diskgroup =~ /\$/) { # if diskgroup contains '$', put single-quotes around it quoteDiskGroup($diskgroup); push @runasmca, '-diskGroupName', "'$diskgroup'"; $dollarDG = TRUE; } else { push @runasmca, '-diskGroupName', $diskgroup; } } # When this is run as superuser if ($CFG->params('CDATA_DISKS')) { push @runasmca, '-diskList', "'$ASMDISKS'"; } if ($CFG->params('CDATA_FAILURE_GROUPS')) { push @runasmca, '-failuregroups', $CFG->params('CDATA_FAILURE_GROUPS'); } if ($CFG->params('CDATA_QUORUM_GROUPS')) { push @runasmca, '-quorumfailuregroups'; push @runasmca, $CFG->params('CDATA_QUORUM_GROUPS'); } if ($CFG->params('CDATA_SITES')) { push @runasmca, '-sites', $CFG->params('CDATA_SITES'); } if ($CFG->params('CDATA_REDUNDANCY')) { push @runasmca, '-redundancy', $CFG->params('CDATA_REDUNDANCY'); } if ($CFG->params('ASM_DISCOVERY_STRING')) { push @runasmca, '-diskString', "'$ASM_DISCOVERY_STRING'"; } if (isFirstNodeToStart()) { push (@runasmca, ('-configureLocalASM')); if ($dollarDG) { push @runasmca, '-passwordFileLocation', "'+$diskgroup/orapwASM'"; } else { push @runasmca, '-passwordFileLocation', "+$diskgroup/orapwASM"; } } if (isReusedg()) { push (@runasmca, ('-reuseDiskGROUP')); } if ($CFG->params('CDATA_AUSIZE')) { push @runasmca, '-au_size', $CFG->params('CDATA_AUSIZE'); } if ($CFG->defined_param('ORATAB_LOC')) { push (@runasmca, ('-oratabLocation'), $CFG->params('ORATAB_LOC')); } # Set compatible rdbms version and enable disk group access control # for domain services cluster if (isDSCConfigured()) { # Bug 23080048: For flex and extended redundancy diskgroups, # server is defaulting compatible.rdbms to 12.2 and hence # root script should not supply lower version compatibility. my $redundancy = lc($CFG->params('CDATA_REDUNDANCY')); if (! ($redundancy eq 'flex' || $redundancy eq 'extended')) { push @runasmca, '-attribute', 'COMPATIBLE.RDBMS=11.2.0.0'; } push @runasmca, '-attribute', 'ACCESS_CONTROL.ENABLED=TRUE'; } #Adding additional arbitrary parameter to ASMCA. push @runasmca, $CFG->params('ASMCA_ARGS'); trace ("Executing as " . $CFG->params('ORACLE_OWNER') . ": @runasmca"); $status = run_as_user($CFG->params('ORACLE_OWNER'), @runasmca); my $asmca_log = catdir($CFG->params('ORACLE_BASE'), 'cfgtoollogs', 'asmca'); if ($status != 0) { $success = FALSE; print_error(184); trace("See asmca logs at $asmca_log for details."); } else { #Bug - 10417856 if (check_service("ora.asm", 20)) { $success = TRUE; } else { print_error(12); $success = FALSE; } } return $success; } =head2 configure_ASM_oda Creates or updates ASM for ODA =head3 Parameters None =head3 Returns TRUE - ASM configuration was created or updated FALSE - ASM configuration was not created or updated =head3 Notes This will start ASM as part of the configuration if it is successful =cut sub configure_ASM_oda { my $ORA_CRS_HOME = $CFG->ORA_CRS_HOME; my $success = TRUE; my $status; my $ASMDISKS = $CFG->params('CDATA_DISKS'); my $ASM_DISCOVERY_STRING = $CFG->params('ASM_DISCOVERY_STRING'); trace ("Configuring ASM for ODA"); my @runodaDg = ("/opt/oracle/oak/onecmd/runodaDg.pl"); if ($CFG->params('CDATA_DISK_GROUP') ){ my $diskgroup = $CFG->params('CDATA_DISK_GROUP'); if ($diskgroup =~ /\$/) { # if diskgroup contains '$', put single-quotes around it quoteDiskGroup($diskgroup); push @runodaDg, '-d', "'$diskgroup'"; } else { push @runodaDg, '-d', $diskgroup; } } # When this is run as superuser if ($CFG->params('CDATA_DISKS')) { push @runodaDg, '-l', "'$ASMDISKS'"; } if ($CFG->params('CDATA_REDUNDANCY')) { push @runodaDg, '-r', $CFG->params('CDATA_REDUNDANCY'); } if ($CFG->params('ASM_DISCOVERY_STRING')) { push @runodaDg, '-s', "'$ASM_DISCOVERY_STRING'"; } push @runodaDg, '-o', $ORA_CRS_HOME; trace ("Executing as " . $CFG->params('ORACLE_OWNER') . ": @runodaDg"); $status = run_as_user($CFG->params('ORACLE_OWNER'), @runodaDg); if ($status != 0) { $success = FALSE; error ("Configuration of ODA ASM ... failed"); } else { #Bug - 10417856 if (check_service("ora.asm", 20)) { $success = TRUE; } else { error ("The ora.asm resource is not ONLINE"); $success = FALSE; } } return $success; } =head2 create_backup_dg Creates backup disk group =head3 Parameters None =head3 Returns TRUE - Backup disk group was created FALSE - Backup disk group was not created =cut sub create_backup_dg { my $success; my $backupDg = $CFG->params('CDATA_BACKUP_DISK_GROUP'); my $diskList = $CFG->params('CDATA_BACKUP_DISKS'); my $failureGroups = $CFG->params('CDATA_BACKUP_FAILURE_GROUPS'); my $quorumGroups = $CFG->params('CDATA_BACKUP_QUORUM_GROUPS'); my $redundancy = $CFG->params('CDATA_BACKUP_REDUNDANCY'); my $sites = $CFG->params('CDATA_BACKUP_SITES'); my $au_size = $CFG->params('CDATA_BACKUP_AUSIZE'); trace("Check whether the backup disk group has been created"); my $asm_mode = getASMMode(); setOraHomeSID($asm_mode, $CFG->ORA_CRS_HOME); my $asmcmd = catfile($CFG->ORA_CRS_HOME, "bin", "asmcmd"); my @cmd = ($asmcmd, "lsdg", "--suppressheader", $backupDg); my @capout; my $rc_asmcmd = run_as_user2($CFG->params('ORACLE_OWNER'), \@capout, @cmd); trace("Return code: $rc_asmcmd \nOutput: @capout"); if($rc_asmcmd == 0 && scalar(@capout) > 0) { # @capout looks like: # MOUNTED EXTERN N 512 512 4096 1048576 33730 # 33476 0 33476 0 Y DATA079/ trace("Backup disk group: $backupDg is created already."); return TRUE; } else { if (scalar(grep(/8001/, @capout)) > 0) { # @capout looks like: # ASMCMD-8001: diskgroup 'DATA079' does not exist or is not mounted trace("Backup disk group: $backupDg has not been created."); } else { trace("Failed to check the configured diskgroup."); die(dieformat(614)); } } trace("Creating backup disk group"); my @runasmca = (catfile($CFG->ORA_CRS_HOME, "bin", "asmca"), '-silent', '-createDiskgroup'); if ($backupDg) { if ($backupDg =~ /\$/) { # if backup diskgroup exists and contains '$', put single-quotes around it quoteDiskGroup($backupDg); push @runasmca, '-diskGroupName', "'$backupDg'"; } else { push @runasmca, '-diskGroupName', $backupDg; } } else { trace("No backup disk group specified, there's no need to create."); return TRUE; } if ($diskList) { push @runasmca, '-diskList', "'$diskList'"; } if ($failureGroups) { push @runasmca, '-failuregroups', "$failureGroups"; } if ($quorumGroups) { push @runasmca, '-quorumfailuregroups', "$quorumGroups"; } if ($sites) { push @runasmca, '-sites', "$sites"; } if ($redundancy) { push @runasmca, '-redundancy', $redundancy; } if ($au_size) { push @runasmca, '-au_size', $au_size; } push @runasmca, '-autolabel'; if (isReusedg()) { push (@runasmca, ('-reuseDiskGROUP')); } # Set compatible rdbms version and enable disk group access control # for domain services cluster if (isDSCConfigured()) { # Bug 23080048: For flex and extended redundancy diskgroups, # server is defaulting compatible.rdbms to 12.2 and hence # root script should not supply lower version compatibility. $redundancy = lc($redundancy); if (! ($redundancy eq 'flex' || $redundancy eq 'extended')) { push @runasmca, '-attribute', 'COMPATIBLE.RDBMS=11.2.0.0'; } push @runasmca, '-attribute', 'ACCESS_CONTROL.ENABLED=TRUE'; } # Add additional arbitrary parameters to ASMCA. push @runasmca, $CFG->params('ASMCA_ARGS'); trace("Executing as " . $CFG->params('ORACLE_OWNER') . ": @runasmca"); my $status = run_as_user($CFG->params('ORACLE_OWNER'), @runasmca); if ($status != 0) { my $asmca_log = catdir($CFG->params('ORACLE_BASE'), 'cfgtoollogs', 'asmca'); trace("failed to create the backup disk group, " ."check asmca logs at $asmca_log for details."); $success = FALSE; } else { trace("Successfully created the backup disk group."); $success = TRUE; } return $success; } sub upgradeASMonNonFirstNodes { my $startRolling = (isOldVersionLT121() && isRolling()) ? TRUE: FALSE; trace("ASMCA startRolling: $startRolling"); my @old_ver = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $oldCRSVersion = join('.',@old_ver); my $isRolling = (isRolling()) ? TRUE : FALSE; trace("Old CRS version: <$oldCRSVersion>"); trace("Is rolling: $isRolling"); my $rc = upgradeASM(FALSE, $isRolling, $CFG->oldconfig('ORA_CRS_HOME'), $startRolling, $oldCRSVersion); if (! $rc) { ($isRolling) ? die(dieformat(305)) : die(dieformat(306)); } } sub storeCurrentCardinality { my $srvctl; my $runsrvctl; my @output; my $status; my $found; my @tokens; my $token; my $cardinality; my $ret = SUCCESS; my $crsctl; my $attrdetails; my @runcrsctl; if (!isCkptPropertyExists ("ROOTCRS_SETASMCARD", "CARDINALITY", "-global")) # Bug 21761307 (Will get wrong cardinality in second run, if it # fails before ROOTCRS_SETASMCARD ckpt success in first run). # Fix is that, write $cardinality into check point property CARDINALITY # in first run, In second run onwards, we can directly get from ckpt property. { # following srvctl command will return '-1' if ASM cardinality is 'ALL' $runsrvctl = "config asm -inner 1"; my $env = $ENV{'SRVM_TRACE'}; undef $SRVM_TRACE; $status = srvctl_capture(TRUE, \@output, $runsrvctl, $CFG->OLD_CRS_HOME); $ENV{'SRVM_TRACE'} = $env; if (($status == 0) || ($status == 2)) { $found = FALSE; @tokens = split(/[=\s{}]+/, $output[0]); foreach $token (@tokens) { if ($found) { $cardinality = $token; # The 'modify asm -count' accepts 'ALL' instead of '-1' to reset ASM # during last node upgrade if the original ASM cardinality is ALL. # For straight-forward, root script stores 'ALL' instead of '-1'. if ($cardinality == -1) { $cardinality = "ALL"; } last; # break } if ($token eq "count") { $found = TRUE; } } trace("ASM cardinality is: $cardinality"); } else { print_lines(@output); print_error(180, "srvctl config asm"); print_error(164); $ret = FAILED; return $ret; } # Bug 18147522. In some situations the SRVCTL output does not contain # the cardinality value in the first line. These are all error situations # and only happen due to a misconfiguration. if (!$found) { trace("Output of command srvctl $runsrvctl did not contain the cardinality"); trace("srvctl config asm failed with status $status"); print_error(180, "srvctl config asm"); print_error(164); $ret = FAILED; return $ret; } # To handle bug 21761307 write $cardinality into check point property writeCkptProperty ("ROOTCRS_SETASMCARD", "CARDINALITY", $cardinality, "-global", "-transferfile"); } else { $cardinality = getCkptPropertyValue("ROOTCRS_SETASMCARD", "CARDINALITY", "-global"); trace("ASM cardinality: $cardinality retrieved from the global CKPT file."); } return $ret; } sub isASMCardSet { my $ckptName = "ROOTCRS_SETASMCARD"; if (isCkptexist($ckptName), "-global") { my $ckptStatus = getCkptStatus($ckptName, "-global"); trace("'$ckptName' state is '$ckptStatus'"); if (isCkptSuccess($ckptName, "-global")) { trace("ASM cardinality already set"); $CFG->wipCkptName("ROOTCRS_STACK"); return TRUE; } } trace("Setting ASM cardinality now ..."); writeGlobalCkpt($ckptName, CKPTSTART, "-transferfile"); $CFG->wipCkptName($ckptName); return FALSE; } =head2 upgradeASM Perform ASM rolling/non-rolling upgrade In case of Flex ASM and when upgrading from 12.1, retrieve the cardinality of ASM, store it in CRS, and set ASM cardinality to ALL =head3 Parameters [0] firstNode : TRUE or FALSE [1] isRolling : TRUE or FALSE [2] oldCRSHOME: old CRS home [3] nodeNumber: local node number, which is only needed on the first node; it defaults to 0 on non-first nodes. [4] startRolling: TRUE if the older version < 12.1.0.1; otherwise false [5] oldCRSVersion: old CRS version =head3 Returns SUCCESS or FAILED =cut sub upgradeASM { my $firstNode = $_[0]; my $isRolling = $_[1]; my $oldCRSHome = $_[2]; my $startRolling = $_[3]; my $oldCRSVersion = $_[4]; my $upgMode; my $fstNode; my $startRollingMode; my $asmca; my @runasmca; my $status; my $ret = SUCCESS; my $run_as_owner = TRUE; my $srvctl; my $ckptName = "ROOTCRS_SETASMCARD"; trace("firstNode = $firstNode"); trace("isRolling = $isRolling"); trace("oldCRSHome = <$oldCRSHome>"); trace("startRolling = $startRolling"); trace("oldCRSVersion = <$oldCRSVersion>"); if ((! isOldVersionLT121()) && (NODE_ROLE_RIM eq $CFG->oldconfig('NODE_CONFIG_ROLE'))) { trace("Skip the invocation of ASMCA on Leaf nodes"); return SUCCESS; } $asmca = catfile($CFG->ORA_CRS_HOME, "bin", "asmca"); # Do not change the order of these parameters as asmca requires the # parameters to be in a specific order or it will fail ($firstNode) ? ($fstNode = "true") : ($fstNode = "false"); ($isRolling) ? ($upgMode = "false") : ($upgMode = "true"); ($startRolling) ? ($startRollingMode = "true") : ($startRollingMode = "false"); # Bug 17778985. Flex ASM upgrade # Already : cluster # In case of rolling upgrade, first node to upgrade # When upgrading from 12.1 with Flex ASM, # retrieve the cardinality of ASM, store it in the global checkpoint, and # set ASM cardinality to ALL if (($isRolling) && ($firstNode) && (isNearASM()) && isOldVersion121()) { if (isASMCardSet()) { trace("ASM cardinality already set to ALL"); goto DONE_SET_ASM_CARDINALITY; } # Retrieve ASM cardinality and store it # Use SRVCTL from the old CRS HOME # $ srvctl config asm -inner 1 # #@=result[0]: res_name={ora.asm} oh={} enabled={true} \ # enabled_nodes={} disabled_nodes={} count={3} asm_listener={} # Get the value for 'count' if (FAILED == storeCurrentCardinality()) { writeGlobalCkpt($ckptName, CKPTFAIL, "-transferfile"); $ret = FAILED; return $ret; } my $num_of_nodes = "ALL"; trace("Executing srvctl modify asm -count $num_of_nodes"); $status = srvctl($run_as_owner, "modify asm -count $num_of_nodes", $CFG->OLD_CRS_HOME); if ($status == TRUE) { trace("srvctl modify asm -count $num_of_nodes ... success"); } else { print_error(164); writeGlobalCkpt($ckptName, CKPTFAIL, "-transferfile"); $ret = FAILED; return $ret; } # Write the ROOTCRS_SETASMCARD checkpoint writeGlobalCkpt($ckptName, CKPTSUC, "-transferfile"); $CFG->wipCkptName("ROOTCRS_STACK"); DONE_SET_ASM_CARDINALITY: } # Issue ASMCA trace("Calling 'asmca' to upgrade ASM ..."); @runasmca = ($asmca, '-silent', '-upgradeNodeASM', '-nonRolling', $upgMode, '-oldCRSHome', $oldCRSHome, '-oldCRSVersion', $oldCRSVersion, '-firstNode', $fstNode, '-startRolling', $startRollingMode, $CFG->params('ASMCA_ARGS')); if ($firstNode) { my $cmdStr = join(' ', @runasmca); print_info(482, $cmdStr); } trace ("Executing as " . $CFG->params('ORACLE_OWNER') . ": @runasmca"); $status = run_as_user($CFG->params('ORACLE_OWNER'), @runasmca); if (0 != $status) { $ret = FAILED; print_error(164); return $ret; } return $ret; } =head2 startRollingUpgrade The function for setting Oracle Clusterware and ASM to rolling upgrade mode. This is only applicable to upgrades from 12.1 or later to a higher version. =head3 Parameters None =head4 Returns SUCCESS or FAILED =cut sub startRollingUpgrade { my $success = FAILED; my $CRSCTL = catfile($CFG->OLD_CRS_HOME, 'bin', 'crsctl'); my $newversion = getcrsrelver($CFG->ORA_CRS_HOME); trace("Setting Oracle Clusterware and ASM in rolling upgrade mode ..."); trace("Current CRS release version: $newversion"); my $cmdStr = "$CRSCTL start rollingupgrade $newversion"; print_info(482, $cmdStr); my @output = system_cmd_capture($CRSCTL, "start", "rollingupgrade", $newversion); my $rc = shift @output; if (0 == $rc) { $success = SUCCESS; trace("Successfully set Oracle Clusterware and ASM to rolling upgrade mode"); print_lines(@output); } else { if (scalar(grep(/CRS-1132/, @output)) > 0) { $success = SUCCESS; trace("Oracle Clusterware and ASM are already in rolling upgrade mode"); } } if (! $success) { print_lines(@output); } return $success; } # Restore the ASM diagnostic destination during the downgrade to 11.2; # Call this only on the first node when 12.1 stack is up. sub asmcaDowngrade { my $oldGIHome = $CFG->OLD_CRS_HOME; my $asmca = catfile($CFG->ORA_CRS_HOME, "bin", "asmca"); my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); my @runasmca = ($asmca, '-silent', '-downgradeASMConfig', '-oldCRSHome', $oldGIHome, '-oldCRSVersion', $verinfo, $CFG->params('ASMCA_ARGS')); trace ("Executing as " . $CFG->params('ORACLE_OWNER') . ": @runasmca"); my $status = run_as_user($CFG->params('ORACLE_OWNER'), @runasmca); if (0 != $status) { trace("Failed to set the diagnostic destination back to the old Oracle base"); trace("Failed to downgrade ASM configuration"); return FAILED; } trace("ASM diagnostic destination reverted to the old Oracle base"); trace("Succeeded to downgrade ASM configuration"); return SUCCESS; } =head2 getvalFromManifestFile Get an attribute's value from manifest file by parsing o/p of 'kfod op=credlist wrap= _boot=TRUE' command. This function is used in other modules from $oraasm object =head3 Parameters Attribute name (AFD_STATE, ASM_ACCESS_MODE, AFD_DISKSTRING etc.) =head4 Returns Attribute's value =cut sub getvalFromManifestFile { my $oraasm = shift; my $attrname = shift; my $attrval = ""; my $KFOD = catfile($CFG->ORA_CRS_HOME, 'bin', 'kfod'); my $credfile_user = $CFG->params('ASM_CREDENTIALS'); my $credfile_home = get_asm_cred_file(); my $wrap = 'wrap=' . $credfile_home; my @output; my @cmd; # If Manifest file is not found under GIHOME, look at user provided path. if (!(-e $credfile_home)) { trace("ASM credentials file does not exist : $credfile_home"); $wrap = 'wrap=' . $credfile_user; } @cmd = ($KFOD, 'op=credlist', $wrap, '_boot=TRUE'); trace("Extracting $attrname from Manifest File. Cmd : @cmd"); my $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @cmd); trace("kfod op=credlist $wrap _boot=TRUE rc : $rc"); if (0 != $rc) { print_lines(@output); die(dieformat(180, join(' ', @cmd))); } foreach my $line (@output) { if ($line =~ /$attrname/) { trace("$attrname line: '$line'"); my @tokens = split(/:/, $line); $attrval = trim($tokens[2]); trace("$attrname: $attrval"); last; } } if ($attrval eq "") { trace("No value found for $attrname"); } return $attrval; } 1;