#!/usr/local/bin/perl # # dbstate.pl # # Copyright (c) 2004, 2005, Oracle. All rights reserved. # # NAME # dbstate.pl # # DESCRIPTION # Database startup and shutdown functions for both single-instance and rac # databases. # # *** NOTE ***: any modifications/fixes to this file must replicated to # OMS-side file at: $EMDROOT/sysman/webapps/em/WEB-INF/perl/ha_dbstate_o.pl # This file is used for startup/shutdown job support. # # NOTES # # # MODIFIED (MM/DD/YY) # vkapur 09/23/05 - bug 4618693 # vkapur 07/25/05 - 4471901 - support open read only # sjconnol 07/12/05 - Bug 4440066 # kramarat 05/19/05 - Fix strict compilation issue - bug 4377718 # kramarat 05/05/05 - Turn on use strict and synch with ha_dbstate_o.pl # vkapur 03/16/05 - bug 4162728 # vkapur 02/22/05 - bug 4201722: don't pass connect string to srvctl # vkapur 10/25/04 - fix db_role typo # vkapur 10/07/04 - fix change_db_state # pbantis 09/30/04 - Handle os authentication. # vkapur 09/17/04 - fix get_srvctl_status # pbantis 09/01/04 - Don't use CHLD signal handler. # vkapur 07/01/04 - add srvctl status # vkapur 06/17/04 - add changeCWDForSrvctlIfNecessary, has_ec # vkapur 06/02/04 - rac-ify get_db_state, change_db_state # pbantis 05/18/04 - Change restart to return if shutdown failure. # pbantis 05/14/04 - Fix booleans. # pbantis 05/10/04 - Added get_db_state(), change_db_state(), cleanup. # vkapur 03/31/04 - misc fixes # vkapur 03/03/04 - add rac startup/shutdown # pbantis 02/21/04 - pbantis_rec040217 # pbantis 02/16/04 - Creation # use strict; use File::Spec; use POSIX "sys_wait_h"; use Cwd; require "emd_common.pl"; require "flush.pl"; require "$ENV{EMDROOT}/sysman/admin/scripts/db/db_common.pl"; use vars qw(@SQLCMDS $SQLPID $NT); use vars qw($DBSTATE_SUCCESS_CODE $DBSTATE_ERROR_CODE $DBSTATE_RETURN_CODE $IGNORE_SQL_ERROR $IGNORE_SQL_SHUTDOWN_ERROR $IGNORE_SQLPLUS_ERROR $orgCWD); use vars qw($TARGET_TYPE_ORACLE_DATABASE $TARGET_TYPE_RAC_DATABASE $SHUTDOWN_NORMAL $SHUTDOWN_IMMEDIATE $STARTUP_NOMOUNT $STARTUP_MOUNT $STARTUP_OPEN $DBROLE_NORMAL $DBROLE_SYSDBA $SQL_SHUTDOWN $STATE_UNKNOWN $STATE_SHUTDOWN $STATE_STARTED $STATE_MOUNTED $STATE_OPEN $SRVCTL_VERS_10 $SRVCTL_VERS_9_2 $SRVCTL_VERS_9_0 $SRVCTL_VERS_8 $srvctlVers $OPEN_MODE_READ_ONLY $RESTRICTED_MODE); $DBSTATE_SUCCESS_CODE = 0; # TBD - Should this be -1? $DBSTATE_ERROR_CODE = 1; $DBSTATE_RETURN_CODE = $DBSTATE_SUCCESS_CODE; # Ignore SQL errors for all commands. $IGNORE_SQL_ERROR = 0; # Ignore SQL errors for the shutdown command. $IGNORE_SQL_SHUTDOWN_ERROR = 1; # Continue when SQLPLUS errors. $IGNORE_SQLPLUS_ERROR = 1; $TARGET_TYPE_ORACLE_DATABASE = "oracle_database"; $TARGET_TYPE_RAC_DATABASE = "rac_database"; $SHUTDOWN_NORMAL = "normal"; $SHUTDOWN_IMMEDIATE = "immediate"; $STARTUP_NOMOUNT = "nomount"; $STARTUP_MOUNT = "mount"; $STARTUP_OPEN = "open"; $DBROLE_NORMAL = "normal"; $DBROLE_SYSDBA = "SYSDBA"; $SQL_SHUTDOWN = "shutdown"; $STATE_UNKNOWN = "UNKNOWN"; $STATE_SHUTDOWN = "SHUTDOWN"; $STATE_STARTED = "STARTED"; $STATE_MOUNTED = "MOUNTED"; $STATE_OPEN = "OPEN"; $OPEN_MODE_READ_ONLY = "READ ONLY"; $RESTRICTED_MODE = "RESTRICT"; $SRVCTL_VERS_10 = "10"; $SRVCTL_VERS_9_2 = "9.2"; $SRVCTL_VERS_9_0 = "9.0"; $SRVCTL_VERS_8 = "8"; sub printError { no strict 'subs'; print "$_[0]\n"; flush(STDOUT); EMD_PERL_ERROR($_[0]); } sub reaper { EMD_PERL_DEBUG("dbstate.reaper: SIGCHLD received for $SQLPID"); my $retpid = waitpid($SQLPID, WNOHANG); EMD_PERL_DEBUG("dbstate.reaper: retpid=$retpid"); if($retpid > 0) { EMD_PERL_DEBUG("dbstate.reaper: status available for $SQLPID"); if($? >> 8) { EMD_PERL_ERROR("dbstate.reaper: SQLPLUS proc $SQLPID failed; see job output for error messages"); ## There isn't likely to be anything in $! in this case; ## any error is simply in the SQLPLUS output in stdout $DBSTATE_RETURN_CODE = $DBSTATE_ERROR_CODE; if($IGNORE_SQLPLUS_ERROR) { EMD_PERL_DEBUG("dbstate.reaper: Continuing after SQLPLUS error"); } else { exit($DBSTATE_RETURN_CODE); } } } } # # Retrieve the database status. # (select status from v$instance) # # targetType The target type. # Values: oracle_database, rac_database # oracleHome The Oracle Home of the target database. # (oracle_database or rac_database) # localSID: SID on which to operate (local SID in case of RAC) # instanceList A list of instanceNames to retrieve the status of or NULL to retrieve # the status of all instances (RAC-only) # (oracle_database or rac_database) # dbUsername The username to log onto the database with. # (oracle_database or rac_database) # dbPassword The password for the username. # (oracle_database or rac_database) # dbRole The role of the database user. # (oracle_database or rac_database) # Values: normal, sysdba # tns The TNS descriptor. Optional. # (oracle_database or rac_database) # isDB10i True if the database version is 10g or higher? Optional. # (oracle_database or rac_database) # # Return value: # (1) $DBSTATE_SUCCESS_CODE with the database state of SHUTDOWN, STARTED, MOUNTED, or OPEN. # (2) $DBSTATE_ERROR_CODE with the database state of UNKNOWN and the output of # the SQLPLUS session written to standard output. # sub get_db_state { my($targetType, $oracleHome, $localSID, $dbName, $dbUsername, $dbPassword, $dbRole, $tns, $instanceList, $isDB10i) = @_; EMD_PERL_DEBUG("dbstate:get_db_state targetType=$targetType"); EMD_PERL_DEBUG("dbstate:get_db_state oracleHome=$oracleHome"); EMD_PERL_DEBUG("dbstate:get_db_state localSID=$localSID"); EMD_PERL_DEBUG("dbstate:get_db_state dbName=$dbName"); EMD_PERL_DEBUG("dbstate:get_db_state instanceList=$instanceList"); EMD_PERL_DEBUG("dbstate:get_db_state dbUsername=$dbUsername"); EMD_PERL_DEBUG("dbstate:get_db_state dbRole=$dbRole"); EMD_PERL_DEBUG("dbstate:get_db_state tns=$tns"); EMD_PERL_DEBUG("dbstate:get_db_state instanceList=$instanceList"); EMD_PERL_DEBUG("dbstate:get_db_state isDB10i=$isDB10i"); # set ORACLE_HOME and ORACLE_SID in env. &set_db_var($oracleHome, $localSID, $isDB10i); # construct db connection string my $connStr = &constructConnectStr($dbUsername, $dbPassword, $tns, $dbRole); # for now, call get_srvctl_status() for RAC case since sqlplus output # cannot reliably be retrieved for RAC my $errCode; my $initState; if ($targetType =~ /$TARGET_TYPE_ORACLE_DATABASE/i) { ($errCode, $initState) = &get_db_state_conn($targetType, $connStr); ## return(&get_db_state_conn($targetType, $connStr)); } elsif ($targetType =~ /$TARGET_TYPE_RAC_DATABASE/i) { ($errCode, $initState) = &get_srvctl_status($dbName, $instanceList); ## return(&get_srvctl_status($dbName, $instanceList)); } EMD_PERL_DEBUG("dbstate:get_db_state errCode=$errCode"); EMD_PERL_DEBUG("dbstate:get_db_state initState=$initState"); return($errCode, $initState); } # # Retrieve the database status. # (select status from v$instance) # # Callers should call the following method(s) before calling this routine: # &set_db_var(); # # targetType The target type. # Values: oracle_database, rac_database # connectStr The database connect string. # See &constructConnectStr() for mode details. # # Return value (oracle_database): # (1) $DBSTATE_SUCCESS_CODE with the database state of SHUTDOWN, STARTED, MOUNTED, or OPEN. # or (2) $DBSTATE_ERROR_CODE with the database state of UNKNOWN and the output of # the SQLPLUS session written to standard output. # # Return value (rac_database): # $DBSTATE_SUCCESS_CODE or $DBSTATE_ERROR_CODE # In both cases, the results of the SQLPLUS session are written to standard output. # In the case of success, the expected form is: # INSTANCE_NAME STATUS # ---------------- ------------ # racinst1 OPEN # racinst2 MOUNT # racinst3 STARTED # Note that if an instance is shutdown, it will NOT appear in the output. # sub get_db_state_conn { my($targetType, $connectStr) = @_; EMD_PERL_DEBUG("dbstate:get_db_state_conn targetType=$targetType"); my $outputFile = create_temp_file(); if ($targetType =~ /$TARGET_TYPE_ORACLE_DATABASE/i) { # single-instance @SQLCMDS = "select status from v\$instance"; $DBSTATE_RETURN_CODE = &executeSQLPlus($connectStr, $outputFile); } elsif ($targetType =~ /$TARGET_TYPE_RAC_DATABASE/i) { # RAC @SQLCMDS = "select instance_name,status from gv\$instance"; $DBSTATE_RETURN_CODE = &executeSQLPlus($connectStr); # output should be sent to stdout } else { EMD_PERL_ERROR("dbstate:get_db_state_conn invalid targetType=$targetType"); $DBSTATE_RETURN_CODE = $DBSTATE_ERROR_CODE; } my $dbState = $STATE_UNKNOWN; # parse SQLplus output for single-instance; RAC output will be parsed by caller if ($targetType =~ /$TARGET_TYPE_ORACLE_DATABASE/i) { # Clear out SQLCMDS. @SQLCMDS = (""); # Command did not get executed my $fileSize = getFileSize($outputFile); if ($fileSize == 0 || $fileSize == -1) { $DBSTATE_RETURN_CODE = $DBSTATE_ERROR_CODE; } else { open (DB_STATUS, "$outputFile") || ((printError("ERROR: Unable to open the temporary file in get_db_state()")) && (return $DBSTATE_ERROR_CODE)); while ($_ = ) { if (/$STATE_OPEN/i) { $dbState = $STATE_OPEN; EMD_PERL_DEBUG("dbstate:get_db_state_conn: database is in open mode, determining if it is read only"); # get open mode my $openModeFile = create_temp_file(); @SQLCMDS = "select open_mode from v\$database"; $DBSTATE_RETURN_CODE = &executeSQLPlus($connectStr, $openModeFile); my $fileSize2 = getFileSize($openModeFile); if (!($fileSize2 == 0 || $fileSize2 == -1)) { open (DB_OPEN_MODE, "$openModeFile") || ((printError("ERROR: Unable to open the temporary file in get_db_state()"))); while ($_ = ) { if (/$OPEN_MODE_READ_ONLY/i) { EMD_PERL_DEBUG("dbstate:get_db_state_conn: database is in open read only mode"); $dbState = "$dbState $OPEN_MODE_READ_ONLY"; } } } else { $DBSTATE_RETURN_CODE = $DBSTATE_ERROR_CODE; } unlink($openModeFile); $DBSTATE_RETURN_CODE = $DBSTATE_SUCCESS_CODE; last; } if (/$STATE_MOUNTED/i) { $dbState = $STATE_MOUNTED; $DBSTATE_RETURN_CODE = $DBSTATE_SUCCESS_CODE; last; } if (/$STATE_STARTED/i) { $dbState = $STATE_STARTED; $DBSTATE_RETURN_CODE = $DBSTATE_SUCCESS_CODE; last; } # ORA-01034: ORACLE not available if (/ORA-01034/i) { $dbState = $STATE_SHUTDOWN; $DBSTATE_RETURN_CODE = $DBSTATE_SUCCESS_CODE; last; } } # Clear out SQLCMDS. @SQLCMDS = (""); # get restricted mode if ($dbState =~ /$STATE_OPEN/i || $dbState =~ /$STATE_MOUNTED/i || $dbState =~ /$STATE_STARTED/i) { my $restrictModeFile = create_temp_file(); @SQLCMDS = "select logins from v\$instance"; $DBSTATE_RETURN_CODE = &executeSQLPlus($connectStr, $restrictModeFile); my $fileSize3 = getFileSize($restrictModeFile); if (!($fileSize3 == 0 || $fileSize3 == -1)) { open (DB_RESTRICT, "$restrictModeFile") || ((printError("ERROR: Unable to open the temporary file in get_db_state()"))); while ($_ = ) { if (/$RESTRICTED_MODE/i) { EMD_PERL_DEBUG("dbstate:get_db_state_conn: database is in restricted mode"); $dbState = "$dbState $RESTRICTED_MODE"; } } } else { $DBSTATE_RETURN_CODE = $DBSTATE_ERROR_CODE; } unlink($restrictModeFile); } if ($DBSTATE_RETURN_CODE != $DBSTATE_SUCCESS_CODE) { # Write the contents of the output file to standard output. seek(DB_STATUS, 0, 0); while ($_ = ) { print "$_"; } } close DB_STATUS; } unlink($outputFile); } else { # rac $DBSTATE_RETURN_CODE = $DBSTATE_SUCCESS_CODE; } EMD_PERL_DEBUG("dbstate.get_db_state_conn: return with code=$DBSTATE_RETURN_CODE, dbState=$dbState"); return($DBSTATE_RETURN_CODE, $dbState); } # Retrieve the status of a RAC database through SRVCTL # # connStr The database connect string # instanceList A list of instanceNames to retrieve the status of or NULL to retrieve # the status of all instances # # Returns output from srvctl status database -S 1 command. For 10g (r1 + r2) # this returns both config and status data; # # For pre-10g, config and status output will come from seperate commands, in a # different format. The caller needs to handle the version in its parser. # sub get_srvctl_status { my ($dbName, $instanceList) = @_; EMD_PERL_DEBUG("dbstate:get_srvctl_status dbName=$dbName"); EMD_PERL_DEBUG("dbstate:get_srvctl_status instanceList=$instanceList"); EMD_PERL_DEBUG("dbstate:get_srvctl_status version=$srvctlVers"); my $srvctl_cmd; if ($srvctlVers =~ $SRVCTL_VERS_10 || !$srvctlVers) { # For 10g, status returns both config and status data $srvctl_cmd = constructSRVCTLCmd($dbName, $instanceList, "", "status", "-S 1"); return (&executeSRVCTL($srvctl_cmd)); } else { # For pre-10g, need to run config, then status # note: srvctl config database does not take instanceList $srvctl_cmd = constructSRVCTLCmd($dbName, $instanceList, "", "config"); my $returnCode = &executeSRVCTL($srvctl_cmd); if ($returnCode != $DBSTATE_SUCCESS_CODE) { return $returnCode; } print "#@=endconfig\n"; # marker for end of config output $srvctl_cmd = constructSRVCTLCmd($dbName, $instanceList, "", "status", "-S 1"); return (&executeSRVCTL($srvctl_cmd)); } } # # Change the state of the database from the current state to the desired state. # # targetType The target type. # Values: oracle_database, rac_database # connectStr The database connect string. # See &constructConnectStr() for mode details. # instanceList A list of SID(s) to change the state of or NULL to change the state of all instances. # (rac_database) # desiredState The desired state of the database. UNKNOWN, SHUTDOWN, STARTED, MOUNTED, OPEN # # Callers should call the following method(s) before calling this routine: # &set_db_var(); # # Possible values for current/desired states: # Current State Desired State # UNKNOWN UNKNOWN # SHUTDOWN SHUTDOWN # STARTED STARTED # MOUNTED MOUNTED # OPEN OPEN # # Note for rac_database: # ALL instances of the database will be brought up, regardless of whether # they were up before. Further, all instances will be brought up to # identical states ($desiredState). # sub change_db_state { my($targetType, $connStr, $instanceList, $desiredState, $dbName) = @_; EMD_PERL_DEBUG("dbstate:change_db_state targetType=$targetType"); EMD_PERL_DEBUG("dbstate:change_db_state instanceList=$instanceList"); EMD_PERL_DEBUG("dbstate:change_db_state desiredState=$desiredState"); EMD_PERL_DEBUG("dbstate:change_db_state dbName=$dbName"); # If the desired state is unknown, then there's not much we can do. if ($desiredState eq $STATE_UNKNOWN) { return; } # Get the current state. my($errCode, $currState) = &get_db_state_conn($targetType, $connStr); EMD_PERL_DEBUG("dbstate:change_db_state currState=$currState"); # If the desired state is the same as the current state, then we've achieved our goal. if ($desiredState eq $currState) { EMD_PERL_DEBUG("dbstate:change_db_state: Current state is same as desired state"); return; } # Let's change the state from the current state to the desired state. # oracle_database: # SQL is generated as follows: # 1) Current state is shutdown --> issue a "startup " # 2) Current state is < desired state --> issue "alter database " # 3) Current state is > desired state --> issue a "shutdown immediate", then #1 if ($targetType =~ /$TARGET_TYPE_ORACLE_DATABASE/i) { if ($desiredState eq $STATE_SHUTDOWN) { push(@SQLCMDS, "shutdown immediate"); } elsif ($desiredState eq $STATE_STARTED) { if ($currState eq $STATE_SHUTDOWN) { push(@SQLCMDS, "startup nomount"); } else { push(@SQLCMDS, "shutdown immediate"); push(@SQLCMDS, "startup nomount"); } } elsif ($desiredState eq $STATE_MOUNTED) { if ($currState eq $STATE_SHUTDOWN) { push(@SQLCMDS, "startup mount"); } elsif ($currState eq $STATE_STARTED) { push(@SQLCMDS, "alter database mount"); } else { push(@SQLCMDS, "shutdown immediate"); push(@SQLCMDS, "startup mount"); } } elsif ($desiredState =~ /$STATE_OPEN/i) { my $openState ="$STATE_OPEN"; if ($desiredState =~ /$OPEN_MODE_READ_ONLY/i) { # so that we don't include restricted option here: $openState = "$openState $OPEN_MODE_READ_ONLY"; } if ($currState eq $STATE_SHUTDOWN) { push(@SQLCMDS, "startup $openState"); } elsif ($currState eq $STATE_STARTED) { push(@SQLCMDS, "alter database mount"); push(@SQLCMDS, "alter database $openState"); } elsif ($currState eq $STATE_MOUNTED) { push(@SQLCMDS, "alter database $openState"); } else { # may be reached if desiredState=open read only and currState=open push(@SQLCMDS, "shutdown immediate"); push(@SQLCMDS, "startup $openState"); } if ($desiredState =~ /$RESTRICTED_MODE/i) { # enable restricted mode push(@SQLCMDS, "alter system enable restricted session"); } } return(&executeSQLPlus($connStr)); } # rac_database: # SRVCTL is issued as follows: # 1) "srvctl stop database" followed by "srvctl start database -o " # Note the end result of this is that ALL instances of the database will be brought # up, regardless of whether they were up before. Further, all instances will be # brought up to identical states (= $desiredState) elsif ($targetType =~ /$TARGET_TYPE_RAC_DATABASE/i) { # shutdown immediate specified instances (or all if instanceList null) my $srvctl_cmd = constructSRVCTLCmd($dbName, $instanceList, "immediate", "stop"); &executeSRVCTL($srvctl_cmd, $connStr); my $startCmd = $desiredState; if ($desiredState =~ /$STATE_OPEN/i) { $startCmd = $STATE_OPEN; if ($desiredState =~ /$OPEN_MODE_READ_ONLY/i) { # because we need to move restrict to front of cmd for srvctl $startCmd = "$STATE_OPEN $OPEN_MODE_READ_ONLY"; } } elsif ($desiredState =~ /$STATE_MOUNTED/i) { $startCmd = $STARTUP_MOUNT; } elsif ($desiredState =~ /$STATE_STARTED/i) { $startCmd = $STARTUP_NOMOUNT; } if ($desiredState =~ /$RESTRICTED_MODE/i) { # add restrict to start command $startCmd = "$RESTRICTED_MODE $startCmd"; } # startup specified instances (or all) to desiredState $srvctl_cmd = constructSRVCTLCmd($dbName, $instanceList, $startCmd, "start"); &executeSRVCTL($srvctl_cmd, $connStr); } } # # Shutdown the database. # # targetType The target type. # Values: oracle_database, rac_database # Uses SQLPLUS for oracle_database target type # Uses SRVCTL for rac_database target type # oracleHome The Oracle Home of the target database. # (oracle_database or rac_database) # localSID: SID on which to operate (local SID in case of RAC) # (oracle_database or rac_database) # dbName The database name (db_unique_name) # (rac_database) # dbUsername The username to log onto the database with. # (oracle_database or rac_database) # dbPassword The password for the username. # (oracle_database or rac_database) # dbRole The role of the database user. # (oracle_database or rac_database) # Values: normal, sysdba # tns The TNS descriptor. Optional. # (oracle_database or rac_database) # instanceList A list of SID(s) to shutdown or NULL to shutdown all instances. # (rac_database) # isDB10i True if the database version is 10g or higher? Optional. # (oracle_database or rac_database) # shutdownOption - The shutdown command option for the SQLPLUS SHUTDOWN or SRVCTL STOP command. # (oracle_database or rac_database) # Values: normal, immediate, transactional, abort # Default: immediate # restoreDBState boolean (0/1), indicates that the database should be # restored to original running state if operation fails # initState_p If specified, and restoreDBState=1, db will be restored to this state. Not required. # preShutdownSql An array containing SQLPLUS script to run before shutting down the database. Optional. # (oracle_database) # Do NOT add semicolons (;) to the end of the statements. # # Return value: # $DBSTATE_SUCCESS_CODE or $DBSTATE_ERROR_CODE with the output of the # SQLPLUS session written to standard output. # sub shutdown_db { my($targetType, $oracleHome, $localSID, $dbName, $dbUsername, $dbPassword, $dbRole, $tns, $instanceList, $isDB10i, $shutdownOption, $restoreDBState, $initState_p, @preShutdownSql) = @_; EMD_PERL_DEBUG("dbstate:shutdown_db targetType=$targetType"); EMD_PERL_DEBUG("dbstate:shutdown_db oracleHome=$oracleHome"); EMD_PERL_DEBUG("dbstate:shutdown_db localSID=$localSID"); EMD_PERL_DEBUG("dbstate:shutdown_db dbName=$dbName"); EMD_PERL_DEBUG("dbstate:shutdown_db dbUsername=$dbUsername"); EMD_PERL_DEBUG("dbstate:shutdown_db dbRole=$dbRole"); EMD_PERL_DEBUG("dbstate:shutdown_db tns=$tns"); EMD_PERL_DEBUG("dbstate:shutdown_db instanceList=$instanceList"); EMD_PERL_DEBUG("dbstate:shutdown_db isDB10i=$isDB10i"); EMD_PERL_DEBUG("dbstate:shutdown_db shutdownOption=$shutdownOption"); EMD_PERL_DEBUG("dbstate:shutdown_db restoreDBState=$restoreDBState"); EMD_PERL_DEBUG("dbstate:shutdown_db initState_p=$initState_p"); EMD_PERL_DEBUG("dbstate:shutdown_db preShutdownSql=@preShutdownSql"); # set ORACLE_HOME and ORACLE_SID in env. &set_db_var($oracleHome, $localSID, $isDB10i); # construct db connection string my $connStr = &constructConnectStr($dbUsername, $dbPassword, $tns, $dbRole); my $returnCode = $DBSTATE_SUCCESS_CODE; my ($errCode, $initState); if ($restoreDBState) { # get current state, for later use in case of failure # use oracle_database targetType for both single-inst and RAC to get status # for local instance in case of RAC if (!$initState_p) { ($errCode, $initState) = &get_db_state_conn($TARGET_TYPE_ORACLE_DATABASE, $connStr, $instanceList); } else { $initState = $initState_p; } # Note:On failure, all specified instances for a RAC db will be returned # to initState (regardless of whether local instance is in $instanceList or not) EMD_PERL_DEBUG("dbstate:shutdown_db initState=$initState"); } if (@preShutdownSql) { @SQLCMDS = (@preShutdownSql); } if (!defined($shutdownOption) || $shutdownOption eq "") { $shutdownOption = $SHUTDOWN_IMMEDIATE; } # determine target db type (single-inst/RAC) and shut down accordingly if ($targetType =~ /$TARGET_TYPE_ORACLE_DATABASE/i) { ### shutdown single-instance database through sqlplus my $shutdownCmd = "shutdown $shutdownOption"; if (@preShutdownSql) { push(@SQLCMDS, $shutdownCmd); } else { @SQLCMDS = ($shutdownCmd); } EMD_PERL_DEBUG("dbstate:shutdown_db SQLCMDS=@SQLCMDS"); $returnCode = &executeSQLPlus($connStr); } elsif ($targetType =~ /$TARGET_TYPE_RAC_DATABASE/i) { ### shutdown RAC database through srvctl my $srvctl_cmd = constructSRVCTLCmd($dbName, $instanceList, $shutdownOption, "stop"); # execute preShutdown SQLplus: if (@preShutdownSql) { $returnCode = &executeSQLPlus($connStr); } # execute SRVCTL stop cmd (only if SQL execution was successful) if ($returnCode == $DBSTATE_SUCCESS_CODE) { $returnCode = &executeSRVCTL($srvctl_cmd, $connStr); } } else { EMD_PERL_ERROR("dbstate:shutdown_db invalid targetType=$targetType"); return($DBSTATE_ERROR_CODE); } # if operation fails and if restoreDBState flag is true, attempt to restore original state if (($returnCode != $DBSTATE_SUCCESS_CODE) && $restoreDBState) { &change_db_state($targetType, $connStr, $instanceList, $initState, $dbName); } return ($returnCode); } # # Startup the database. # # targetType The target type. # Values: oracle_database, rac_database # Uses SQLPLUS for oracle_database target type # Uses SRVCTL for rac_database target type # oracleHome The Oracle Home of the target database. # (oracle_database or rac_database) # localSID: SID on which to operate (local SID in case of RAC) # (oracle_database or rac_database) # dbName The database name (db_unique_name) # (rac_database) # dbUsername The username to log onto the database with. # (oracle_database or rac_database) # dbPassword The password for the username. # (oracle_database or rac_database) # dbRole The role of the database user. # (oracle_database or rac_database) # Values: normal, sysdba # tns The TNS descriptor. Optional. # (oracle_database or rac_database) # instanceList A list of SID(s) to shutdown or NULL to shutdown all instances. # (rac_database) # isDB10i True if the database version is 10g or higher? Optional. # (oracle_database or rac_database) # startupOption - The startup command option for the SQLPLUS STARTUP, SQLPLUS ALTER DATABASE, # or SRVCTL START command. Optionally append PFILE=filename. Optional. # (oracle_database or rac_database) # Values: nomount, mount, open, # force nomount, force mount, force open, # restrict nomount, restrict mount, restrict open # # For SQLPLUS ALTER DATABASE (used when sqlRunState is nomount or mount) # open, open read write, open read only, # open resetlogs, open noresetlogs, # open read write resetlogs, open read write noresetlogs # # To change the state of the database, for example, from nomount to mount, # pass in mount as the startupOption. This method will determine that the database # is not shutdown and therefore execute 'alter database mount', instead of 'startup mount'. # # sqlRunState What the state of the database should be in when running the postStartupSql. # Optionally append PFILE=filename. Optional. # (oracle_database) # values: nomount, mount, open # bounceAfterPostSQL boolean (0/1), if non-empty, indicates that database should be # restarted after running postStartupSql scripts # restoreDBState boolean (0/1), indicates that the database should be # restored to original running state if operation fails # initState_p If specified, and restoreDBState=1, db will be restored to this state. Not required. # postStartupSql An array containing SQLPLUS script to run when in the sqlRunState. Optional. # (oracle_database) # Do NOT add semicolons (;) to the end of the statements. # # Return value: # $DBSTATE_SUCCESS_CODE or $DBSTATE_ERROR_CODE with the output of the # SQLPLUS session written to standard output. # sub startup_db { my($targetType, $oracleHome, $localSID, $dbName, $dbUsername, $dbPassword, $dbRole, $tns, $instanceList, $isDB10i, $startupOption, $sqlRunState, $restoreDBState, $initState_p, $bounceAfterPostSQL, @postStartupSql) = @_; EMD_PERL_DEBUG("dbstate:startup_db targetType=$targetType"); EMD_PERL_DEBUG("dbstate:startup_db oracleHome=$oracleHome"); EMD_PERL_DEBUG("dbstate:startup_db localSID=$localSID"); EMD_PERL_DEBUG("dbstate:startup_db dbName=$dbName"); EMD_PERL_DEBUG("dbstate:startup_db dbUsername=$dbUsername"); EMD_PERL_DEBUG("dbstate:startup_db dbRole=$dbRole"); EMD_PERL_DEBUG("dbstate:startup_db tns=$tns"); EMD_PERL_DEBUG("dbstate:startup_db instanceList=$instanceList"); EMD_PERL_DEBUG("dbstate:startup_db isDB10i=$isDB10i"); EMD_PERL_DEBUG("dbstate:startup_db startupOption=$startupOption"); EMD_PERL_DEBUG("dbstate:startup_db sqlRunState=$sqlRunState"); EMD_PERL_DEBUG("dbstate:startup_db restoreDBState=$restoreDBState"); EMD_PERL_DEBUG("dbstate:startup_db initState_p=$initState_p"); EMD_PERL_DEBUG("dbstate:startup_db bounceAfterPostSQL=$bounceAfterPostSQL"); EMD_PERL_DEBUG("dbstate:startup_db postStartupSql=@postStartupSql"); # set ORACLE_HOME and ORACLE_SID in env. &set_db_var($oracleHome, $localSID, $isDB10i); if ($isDB10i) { # null TNS if DB 10i (or higher). # Cannot use listener to connect when db is down (for >= 10i db) $tns = ""; } if (!defined($startupOption) || $startupOption eq "") { $startupOption = $STARTUP_OPEN; } # construct db connection string my $connStr = &constructConnectStr($dbUsername, $dbPassword, $tns, $dbRole); my $returnCode = $DBSTATE_SUCCESS_CODE; # get current state, for later use in case of failure and # to also determine whether to do a 'startup' or 'alter database' command. # use oracle_database targetType for both single-inst and RAC to get status # for local instance in case of RAC my($errCode, $curState, $initState); ($errCode, $curState) = &get_db_state_conn($TARGET_TYPE_ORACLE_DATABASE, $connStr, $instanceList); EMD_PERL_DEBUG("dbstate:startup_db curState=$curState"); if (!$initState_p) { $initState = $curState; } else { $initState = $initState_p; } EMD_PERL_DEBUG("dbstate:startup_db initState=$initState"); # add postStartupSql to SQL cmd stack: # for RAC, the postStartupSql will be run on the local instance # Also the local inst will be brought up to $startupOption state; SRVCTL will # bring up the rest of the instances up later (and no-op on the started inst). if (@postStartupSql) { if ($sqlRunState =~ /$STARTUP_NOMOUNT|$STARTUP_MOUNT|$STARTUP_OPEN/i) { @SQLCMDS = ("startup $sqlRunState"); ## This assumes instance is currently DOWN... push(@SQLCMDS, @postStartupSql); if ($bounceAfterPostSQL) { push(@SQLCMDS, "shutdown immediate"); } else { # no bounce, just alter database if ($startupOption =~ /$sqlRunState/i) { # startupOption == sqlRunState # not an error, just a no-op # note that special options in startupOptions will be ignored EMD_PERL_DEBUG("dbstate:startup_db startupOption and sqlRunState are at same level, no alter database performed."); } else { # This assumes startupOption state is > sqlRunState --> caller gets around it by setting bounceAfterPostSql # If the caller does try sqlRunState > startupOption, caller will get errors from SQLplus push(@SQLCMDS, "alter database $startupOption"); } } } else { EMD_PERL_ERROR("dbstate:startup_db invalid sqlRunState=$sqlRunState"); return($DBSTATE_ERROR_CODE); } } # determine target db type (single-inst/RAC) and startup accordingly if ($targetType =~ /$TARGET_TYPE_ORACLE_DATABASE/i) { ### startup single-instance database through sqlplus if (@postStartupSql) { if ($bounceAfterPostSQL) { push(@SQLCMDS, "startup $startupOption"); } } else { # no SQL to run after startup: if (($curState eq $STATE_UNKNOWN) || ($curState eq $STATE_SHUTDOWN)) { @SQLCMDS = ("startup $startupOption"); } else { @SQLCMDS = ("alter database $startupOption"); } } EMD_PERL_DEBUG("dbstate:startup_db SQLCMDS=@SQLCMDS"); $returnCode = &executeSQLPlus($connStr); } elsif ($targetType =~ /$TARGET_TYPE_RAC_DATABASE/i) { ### startup RAC database through srvctl my $srvctl_cmd = constructSRVCTLCmd($dbName, $instanceList, $startupOption, "start"); # execute postStartup SQL: if (@postStartupSql) { $returnCode = &executeSQLPlus($connStr); } # execute SRVCTL start cmd (only if SQL execution was successful) if ($returnCode == $DBSTATE_SUCCESS_CODE) { $returnCode = &executeSRVCTL($srvctl_cmd, $connStr); } } else { EMD_PERL_ERROR("dbstate:startup_db invalid targetType=$targetType"); return($DBSTATE_ERROR_CODE); } # if operation fails and if restoreDBState flag is true, attempt to restore original state if (($returnCode != $DBSTATE_SUCCESS_CODE) && $restoreDBState) { &change_db_state($targetType, $connStr, $instanceList, $initState, $dbName); } return($returnCode); } # # Restart the database # # targetType The target type. # Values: oracle_database, rac_database # Uses SQLPLUS for oracle_database target type # Uses SRVCTL for rac_database target type # oracleHome The Oracle Home of the target database. # (oracle_database or rac_database) # localSID: SID on which to operate (local SID in case of RAC) # (oracle_database or rac_database) # dbName The database name. # (rac_database) # dbUsername The username to log onto the database with. # (oracle_database or rac_database) # dbPassword The password for the username. # (oracle_database or rac_database) # dbRole The role of the database user. # (oracle_database or rac_database) # Values: normal, sysdba # tns The TNS descriptor. Optional. # (oracle_database or rac_database) # instanceList A list of SID(s) to shutdown or NULL to shutdown all instances. # (rac_database) # isDB10i True if the database version is 10g or higher? Optional. # (oracle_database or rac_database) # restoreDBState boolean (0/1), indicates that the database should be # restored to original running state if operation fails. # shutdownOption - The shutdown command option for the SQLPLUS SHUTDOWN or SRVCTL STOP command. Optional. # (oracle_database or rac_database) # Values: normal, immediate, transactional, abort # Default: immediate # preShutdownSql A reference to an array containing SQLPLUS script to run before shutting down the database. Optional. # (oracle_database) # Do NOT add semicolons (;) to the end of the statements. # startupOption - The startup command option for the SQLPLUS STARTUP, SQLPLUS ALTER DATABASE, # or SRVCTL START command. Optionally append PFILE=filename. Optional. # (oracle_database or rac_database) # Values: nomount, mount, open, # force nomount, force mount, force open, # restrict nomount, restrict mount, restrict open # For SQLPLUS ALTER DATABASE (used when sqlRunState is nomount or mount) # open, open read write, open read only, # open resetlogs, open noresetlogs, # open read write resetlogs, open read write noresetlogs # postStartupSql A reference to an array containing SQLPLUS script to run when in the sqlRunState. Optional. # (oracle_database) # Do NOT add semicolons (;) to the end of the statements. # sqlRunState What the state of the database should be in when running the SQL. # Optionally append PFILE=filename. Optional. # (oracle_database) # values: nomount, mount, open # bounceAfterPostSQL boolean (0/1), if non-empty, indicates that database should be # restarted after running postStartupSql scripts # # Return value: # $DBSTATE_SUCCESS_CODE or $DBSTATE_ERROR_CODE with the output of the # SQLPLUS session written to standard output. # sub restart_db { my($targetType, $oracleHome, $localSID, $dbName, $dbUsername, $dbPassword, $dbRole, $tns, $instanceList, $isDB10i, $shutdownOption, $preShutdownSql, $startupOption, $postStartupSql, $sqlRunState, $restoreDBState, $bounceAfterPostSQL) = @_; EMD_PERL_DEBUG("dbstate:restart_db targetType=$targetType"); EMD_PERL_DEBUG("dbstate:restart_db oracleHome=$oracleHome"); EMD_PERL_DEBUG("dbstate:restart_db localSID=$localSID"); EMD_PERL_DEBUG("dbstate:restart_db dbName=$dbName"); EMD_PERL_DEBUG("dbstate:restart_db dbUsername=$dbUsername"); EMD_PERL_DEBUG("dbstate:restart_db dbRole=$dbRole"); EMD_PERL_DEBUG("dbstate:restart_db tns=$tns"); EMD_PERL_DEBUG("dbstate:restart_db instanceList=$instanceList"); EMD_PERL_DEBUG("dbstate:restart_db isDB10i=$isDB10i"); EMD_PERL_DEBUG("dbstate:restart_db shutdownOption=$shutdownOption"); EMD_PERL_DEBUG("dbstate:restart_db preShutdownSql=@$preShutdownSql"); EMD_PERL_DEBUG("dbstate:restart_db startupOption=$startupOption"); EMD_PERL_DEBUG("dbstate:restart_db postStartupSql=@$postStartupSql"); EMD_PERL_DEBUG("dbstate:restart_db sqlRunState=$sqlRunState"); EMD_PERL_DEBUG("dbstate:restart_db restoreDBState=$restoreDBState"); EMD_PERL_DEBUG("dbstate:restart_db bounceAfterPostSQL=$bounceAfterPostSQL"); my $errCode; my $initState; if ($restoreDBState) { # get current state, for later use in case of failure # The database should be returned to this state in case of failure during # shutdown OR startup # use oracle_database targetType for both single-inst and RAC to get status # for local instance in case of RAC ($errCode, $initState) = &get_db_state($TARGET_TYPE_ORACLE_DATABASE, $oracleHome, $localSID, $dbName, $dbUsername, $dbPassword, $dbRole, $tns, $instanceList, $isDB10i); } EMD_PERL_DEBUG("dbstate:restart_db restart_db errCode returned=$errCode"); EMD_PERL_DEBUG("dbstate:restart_db restart_db initState returned=$initState"); # Dereference preShutdownSql and postStartupSql variables. my $result = &shutdown_db($targetType, $oracleHome, $localSID, $dbName, $dbUsername, $dbPassword, $dbRole, $tns, $instanceList, $isDB10i, $shutdownOption, $restoreDBState, $initState, @$preShutdownSql); # only do startup if shutdown did not receive errors if ($result == $DBSTATE_SUCCESS_CODE) { $result = &startup_db($targetType, $oracleHome, $localSID, $dbName, $dbUsername, $dbPassword, $dbRole, $tns, $instanceList, $isDB10i, $startupOption, $sqlRunState, $restoreDBState, $initState, $bounceAfterPostSQL, @$postStartupSql); } return($result); } # Retrieve version of SRVCTL software # # oracleHome $OH of target database # # Returns 'srvctl config -V' # sub get_srvctl_version_dbhome { my ($oracleHome) = @_; $ENV{ORACLE_HOME} = $oracleHome; EMD_PERL_DEBUG("dbstate:get_srvctl_version oracleHome=$oracleHome"); my $srvctl_cmd = "$oracleHome/bin/srvctl config -V"; return (&executeSRVCTL($srvctl_cmd)); } # # Execute the SQL commands as SYSDBA user. # # dbUsername The username to log onto the database with. # (oracle_database) # dbPassword The password for the username. # (oracle_database) # tns The TNS descriptor. Optional. # (oracle_database) # outputFile Redirect output to the named file. Optional. # (oracle_database) # # Return value: # $DBSTATE_SUCCESS_CODE or $DBSTATE_ERROR_CODE with the output of the # SQLPLUS session written to standard output or to the file passed in. # sub executeSQLPlusSYSDBA { my($dbUsername, $dbPassword, $tns, $outputFile) = @_; my $connStr = &constructConnectStr($dbUsername, $dbPassword, $tns, $DBROLE_SYSDBA); return(&executeSQLPlus($connStr, $outputFile)); } # # Execute the SQL commands using the database connect string. # # connectStr The database connection string. See &constructConnectStr for details. # (oracle_database) # outputFile Redirect the output from SQLPLUS to the named file. Optional. # Otherwise redirect the output to standard output. # (oracle_database) # # Return value: # $DBSTATE_SUCCESS_CODE or $DBSTATE_ERROR_CODE with the output of the # SQLPLUS session written to standard output or to the file passed in. # sub executeSQLPlus { # local($SIG{'CHLD'}) = \&reaper; my($connStr, $outputFile) = @_; EMD_PERL_DEBUG("dbstate.executeSQLPlus"); # Redirect the output to a file. if (defined($outputFile) && ($outputFile ne "")){ $SQLPID = open(SQL, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog > $outputFile"); } # Redirect the output to standard output. else{ $SQLPID = open(SQL, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog"); } if(!$SQLPID){ printError("ERROR: could not run SQLPLUS"); return($DBSTATE_ERROR_CODE); } EMD_PERL_DEBUG("dbstate.executeSQLPlus: SQLPID=$SQLPID"); my($old_fh) = select(SQL); $| = 1; select($old_fh); ## Cause exit on any ORA errors unless IGNORE_SQL_ERROR is set if($IGNORE_SQL_ERROR){ print SQL "whenever sqlerror continue\n"; EMD_PERL_DEBUG("dbstate.executeSQLPlus: whenever sqlerror continue"); } else{ print SQL "whenever sqlerror exit sql.sqlcode\n"; EMD_PERL_DEBUG("dbstate.executeSQLPlus: whenever sqlerror exit sql.sqlcode"); } print SQL "connect $connStr\n"; print SQL "set echo on\n"; EMD_PERL_DEBUG("dbstate.executeSQLPlus: Connected"); # Run thru all commands (@SQLCMDS is global set by caller) if(defined(@SQLCMDS)){ my($cmd); foreach $cmd (@SQLCMDS) { ## Turn off error exit for shutdown, as ORA ## errors are expected in some cases (e.g., shutdown of standby) if(!$IGNORE_SQL_ERROR) { if($IGNORE_SQL_SHUTDOWN_ERROR && ($cmd =~ /$SQL_SHUTDOWN/i)) { print SQL "whenever sqlerror continue\n"; print SQL "${cmd};\n"; print SQL "whenever sqlerror exit sql.sqlcode\n"; EMD_PERL_DEBUG("dbstate.executeSQLPlus: whenever sqlerror continue"); EMD_PERL_DEBUG("dbstate.executeSQLPlus: ${cmd}"); EMD_PERL_DEBUG("dbstate.executeSQLPlus: whenever sqlerror exit sql.sqlcode"); next; } } print SQL "${cmd};\n"; EMD_PERL_DEBUG("dbstate.executeSQLPlus: ${cmd}"); } } close(SQL); ## For NT, signal handling not operable, so must determine if ## if error occurred here. ## if($NT && ($? >> 8)){ ## Use following code for all platforms instead of signal handler. ## In signal handler, waitpid() always returns a pid of -1 on Linux ## in conjuction with the open() call. if($?){ ## SQLPLUS error is in stdout EMD_PERL_ERROR("dbstate.executeSQLPlus: SQLPLUS error; see job output for error messages"); $DBSTATE_RETURN_CODE = $DBSTATE_ERROR_CODE; if($IGNORE_SQLPLUS_ERROR){ EMD_PERL_DEBUG("dbstate.executeSQLPlus: Continuing after SQLPLUS error"); } else{ exit($DBSTATE_RETURN_CODE); } } ## Normally returns 0, ## unless IGNORE_SQLPLUS_ERROR is on and SQLPLUS exited on error EMD_PERL_DEBUG("dbstate.executeSQLPlus: return with code=$DBSTATE_RETURN_CODE"); return $DBSTATE_RETURN_CODE; } ## Execute SRVCTL command # Pipe command to tmp file first, to avoid exposing $connStr credentials # Print output to stdout # Grep output for error codes to ensure correct error reporting ## sub executeSRVCTL { my ($srvctl_cmd, $connStr) = @_; # append db connection string # bug 4201722: don't pass connect string to srvctl # if ($connStr) # { # $srvctl_cmd= $srvctl_cmd." -c \"$connStr\""; # } # create temp file for password piping my $dir = tempdir(CLEANUP => 1); my ($srvctl_fh, $srvctl_filename); if (!$NT) { ($srvctl_fh, $srvctl_filename) = tempfile( DIR => $dir ); } else { $srvctl_filename = "$dir\\"."rac.srvctl.$$"; ## create unique file name } open(SRVCTL_WRITER, "|$srvctl_cmd > $srvctl_filename") || die "dbstate.executeSRVCTL: Can not open pipe for srvctl command"; close SRVCTL_WRITER; my $isErr = 0; if ($? != 0) { $isErr = 1; } open (SRVCTL_STATUS, "$srvctl_filename") || die "dbstate.executeSRVCTL: Unable to open temp file: $srvctl_filename"; my $result =""; while ($_ = ) { #EMD_PERL_DEBUG("$_"); #print "$_"; $result= $result."$_"; } close SRVCTL_STATUS; if (!$NT) { close $srvctl_fh; # need to explicity close tmp file in NT } removeFile ($srvctl_filename); print "$result\n"; EMD_PERL_DEBUG("srvctl result = $result"); if ($isErr) { return ($? >> 8); } return has_ec($result); } # Constructs a SRVCTL cmd to perform a database shutdown or startup (RAC only) # # dbName The database name (db_unique_name) # instanceList A list of SID(s) to shutdown or NULL to shutdown all instances. # srvctlOption '-o' Options for startup/shutdown (see comments for startup_db/ # shutdown_db for details) # opCmd SRVCTL command verb: {start, stop, status, config} # extraOptions Any extra command-line options (e.g., "-S 1") # # Returns string of form: # 1) srvctl $opCmd database -d $dbName -o $srvctlOption $extraOptions # or 2) srvctl $opCmd database -d $dbName -o $srvctlOption -i $instanceList $extraOptions # or 3) srvctl $opCmd database -d $dbName $extraOptions sub constructSRVCTLCmd() { my($dbName, $instanceList, $srvctlOption, $opCmd, $extraOptions) = @_; EMD_PERL_DEBUG("dbstate:constructSRVCTLCmd dbName=$dbName"); EMD_PERL_DEBUG("dbstate:constructSRVCTLCmd instanceList=$instanceList"); EMD_PERL_DEBUG("dbstate:constructSRVCTLCmd srvctlOption=$srvctlOption"); EMD_PERL_DEBUG("dbstate:constructSRVCTLCmd opCmd=$opCmd"); EMD_PERL_DEBUG("dbstate:constructSRVCTLCmd extraOptions=$extraOptions"); my $srvctl_cmd; if (!defined($instanceList) || $instanceList eq "") { # all instances $srvctl_cmd = "$ENV{ORACLE_HOME}/bin/srvctl $opCmd database -d $dbName"; } else { # specify instanceList $srvctl_cmd = "$ENV{ORACLE_HOME}/bin/srvctl $opCmd instance -d $dbName -i \"$instanceList\""; } # append '-o' options if ($srvctlOption && !($srvctlOption eq "")) { # note: do not use \" here because of the way it is called from startup/shutdown jobs $srvctl_cmd = $srvctl_cmd." -o $srvctlOption"; } # append extra options if ($extraOptions && !($extraOptions eq "")) { $srvctl_cmd = $srvctl_cmd." $extraOptions"; } EMD_PERL_DEBUG("dbstate:constructSRVCTLCmd srvctl_cmd=$srvctl_cmd"); return $srvctl_cmd; } sub set_db_var() { my($target_home, $target_sid, $is10i) = @_; if (defined($is10i) && $is10i) { set_env_var($target_home, $target_sid, "TRUE"); } else { set_env_var($target_home, $target_sid, "FALSE"); } } ## Construct the database connection string. Possible return formats: # $dbUsername/$dbPassword@$tns as $dbRole # $dbUsername/$dbPassword as $dbRole # $dbUsername/$dbPassword@$tns # $dbUsername/$dbPassword # / as $dbRole ## sub constructConnectStr { my($dbUsername, $dbPassword, $tns, $dbRole) = @_; my $connStr; if(!$dbUsername && !$dbPassword){ # assume OS authentication if user/pass are null EMD_PERL_DEBUG("dbstate.constructConnectStr: No db credentials, assuming OS authentication"); ## Ensure these params are empty strings $dbUsername = ""; $dbPassword = ""; # Need to preserve TNS for RAC full connect descriptors: # $tns = ""; } if (!($dbRole =~ /$DBROLE_SYSDBA/i)){ # assume OS authentication if the db role is NOT SYSDBA (needed by backup/recovery) ## Ensure these params are empty strings $dbUsername = ""; $dbPassword = ""; $dbRole = $DBROLE_SYSDBA; } if ($dbRole =~ /$DBROLE_NORMAL/i || $dbRole eq '') { # no $dbRole supplied if(defined($tns) && $tns ne ""){ $connStr = "${dbUsername}/${dbPassword}\@${tns}"; } else{ $connStr = "${dbUsername}/${dbPassword}"; } } else{ # $dbRole supplied if(defined($tns) && $tns ne ""){ $connStr = "${dbUsername}/${dbPassword}\@${tns} as $dbRole"; } else{ $connStr = "${dbUsername}/${dbPassword} as $dbRole"; } } return $connStr; } # Purpose: find following error codes in input string: # "PRKP" # "PRKH" # "PRKO" # "CRS" # "ORA" sub has_ec { my($in_str) = $_[0]; # print "error code detector: $in_str\n"; if ($in_str =~ /PRKP-/ || $in_str =~ /PRKH-/ || $in_str =~ /PRKO-/ || $in_str =~ /CRS-/ || $in_str =~ /ORA-/) { return(-1); # error mesg } else { return(0); } } ## The following method is written to workaround bug 3469118. ## For pre-10g NT rac, if PATH env doesn't include $oracleHome/bin, ## the srvctl will return error when invoked not within $oracleHome/bin. ## The workaround for this bug is to change the current dir to $oracleHome/bin ## before calling srvctl and set it back when finished ## # Return the original current dir if it's changed # Otherwise return "" sub changeCWDForSrvctlIfNecessary { my ($oracleHome, $srvctlVersion) = @_; my $orgDIR = ""; if ($NT && $srvctlVersion eq '9.2') { # This is a NT pre-10g srvctl $orgDIR = getcwd; chdir "$oracleHome/bin"; } $orgDIR; } ## Determine if the specified database in a Data Guard ## broker configuration. sub isDGBrokerConfig { @SQLCMDS = (""); my ($user, $pw, $tns, $ohome, $sid) = @_; ## If $ohome and $sid are defined, this is OS auth; connect ## descriptor not needed if($ohome && $sid){ &set_db_var($ohome, $sid, 1); } my $dir = tempdir(CLEANUP => 1); my ($tempfh, $tempfile); if (!$NT) { ($tempfh, $tempfile) = tempfile( DIR => $dir ); } else { $tempfile = "$dir\\"."dgtest.$$"; ## create unique file name } my $sql = "set echo off;\n"; $sql .= "set serveroutput on;\n"; $sql .= "variable result number;\n"; $sql .= "BEGIN\n"; $sql .= " select count(*) INTO :result from x\$vinst;\n"; $sql .= " dbms_output.put_line('result='||:result);\n"; $sql .= "END;\n"; $sql .= "/\n"; $sql .= "EXIT;\n"; push(@SQLCMDS, $sql); executeSQLPlusSYSDBA($user, $pw, $tns, $tempfile); open (DGOUTPUT, "$tempfile") || die "dbstate.isDGBrokerConfig: Unable to open temp file: $tempfile"; my @output; my $result = 0; while (){ if($_ =~ /=/){ @output = split /=/, $_; $result = $output[1]; last; } } close(DGOUTPUT); removeFile ($tempfile); if($result > 0){ EMD_PERL_DEBUG("dbstate.isDGBrokerConfig: is broker config "); return "1"; } EMD_PERL_DEBUG("dbstate.isDGBrokerConfig: not broker config "); return "0"; } ## Determine if the specified database is in a Data Guard ## configuration, NOT managed by DG broker. sub isDGNonBrokerConfig { @SQLCMDS = (""); my ($user, $pw, $tns, $ohome, $sid) = @_; ## If $ohome and $sid are defined, this is OS auth; connect ## descriptor not needed if($ohome && $sid){ &set_db_var($ohome, $sid, 1); } my $dir = tempdir(CLEANUP => 1); my ($tempfh, $tempfile); if (!$NT) { ($tempfh, $tempfile) = tempfile( DIR => $dir ); } else { $tempfile = "$dir\\"."dgtest.$$"; ## create unique file name } my $sql = "set echo off;\n"; $sql .= "set serveroutput on;\n"; $sql .= "variable result number;\n"; $sql .= "BEGIN\n"; $sql .= " select case when (((select count(*) from v\$archive_dest where target='STANDBY') > 0) OR (database_role like '\%STANDBY')) and ((select count(*) from x\$vinst) = 0) then '1' else '0' end INTO :result from v\$database;\n"; $sql .= " dbms_output.put_line('result='||:result);\n"; $sql .= "END;\n"; $sql .= "/\n"; push(@SQLCMDS, $sql); executeSQLPlusSYSDBA($user, $pw, $tns, $tempfile); open (DGOUTPUT, "$tempfile") || die "dbstate.isDGNonBrokerConfig: Unable to open temp file: $tempfile"; my @output; my $result = 0; while (){ if($_ =~ /=/){ @output = split /=/, $_; $result = $output[1]; last; } } close(DGOUTPUT); removeFile ($tempfile); if($result == 1){ EMD_PERL_DEBUG("dbstate.isDGNonBrokerConfig: is non-broker config "); return "1"; } EMD_PERL_DEBUG("dbstate.isDGNonBrokerConfig: not non-broker config "); return "0"; } ## Determine if the specified database is in a Data Guard ## configuration, broker or non-broker sub isDGConfig{ @SQLCMDS = (""); my ($user, $pw, $tns, $ohome, $sid) = @_; if(isDGBrokerConfig($user, $pw, $tns, $ohome, $sid) || isDGNonBrokerConfig($user, $pw, $tns, $ohome, $sid)){ EMD_PERL_DEBUG("dbstate.isDGConfig: is DG config "); return "1"; } EMD_PERL_DEBUG("dbstate.isDGConfig: not DG config "); return "0"; } ## Determine if the specified database is ## participating in a fast-start failover configuration sub isFSFOEnabled { @SQLCMDS = (""); my ($user, $pw, $tns, $ohome, $sid) = @_; ## If $ohome and $sid are defined, this is OS auth; connect ## descriptor not needed if($ohome && $sid){ &set_db_var($ohome, $sid, 1); } my $dir = tempdir(CLEANUP => 1); my ($tempfh, $tempfile); if (!$NT) { ($tempfh, $tempfile) = tempfile( DIR => $dir ); } else { $tempfile = "$dir\\"."dgtest.$$"; ## create unique file name } my $sql = "set echo off;\n"; $sql .= "set serveroutput on;\n"; $sql .= "variable result varchar2(64);\n"; $sql .= "BEGIN\n"; $sql .= " select FS_FAILOVER_STATUS INTO :result from v\$database;\n"; $sql .= " dbms_output.put_line('result='||:result);\n"; $sql .= "END;\n"; $sql .= "/\n"; push(@SQLCMDS, $sql); executeSQLPlusSYSDBA($user, $pw, $tns, $tempfile); open (DGOUTPUT, "$tempfile") || die "dbstate.isFSFOEnabled: Unable to open temp file: $tempfile"; my @output; my $result = 0; while (){ if($_ =~ /=/){ @output = split /=/, $_; $result = $output[1]; last; } } close(DGOUTPUT); removeFile ($tempfile); if($result !~ /DISABLED/){ EMD_PERL_DEBUG("dbstate.isFSFOEnabled: FSFO enabled"); return "1"; } EMD_PERL_DEBUG("dbstate.isFSFOEnabled: FSFO disabled"); return "0"; } 1;