# Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. # # NAME # crsutils.pm # # DESCRIPTION # This module contains common functions for root scripts # # NOTES # # # MODIFIED (MM/DD/YY) # apfwkr 08/07/17 - Backport madoming_bug-25518447 from main # luoli 01/10/17 - XbranchMerge luoli_bug-24665035 from main # xyuan 10/31/16 - Backport xyuan_bug-24692493 from main # luoli 10/18/16 - Add and start RHP client on Member Cluster # xyuan 09/30/16 - Fix bug 24656800 # muhe 09/29/16 - Fix bug 24754558 # muhe 09/06/16 - Fix bug 24495989 # luoli 10/11/16 - Fix bug 24665035 # jesugonz 08/11/16 - Fix bug 24445119 # yilhu 08/09/16 - ASM on NAS change # luoli 07/28/16 - Fix bug 24311045 # bbeelamk 07/28/16 - Fix bug 24334544 # xyuan 07/21/16 - Fix bug 24314345 # bbeelamk 06/29/16 - Fix bug 23706784 # xyuan 06/26/16 - Fix bug 23633680 # muhe 06/23/16 - Fix bug 23623032 # luoli 06/22/16 - Fix bug 23629277 # samjo 06/17/16 - Add NAS support for CW files # jesugonz 06/13/16 - Fix bug 23526657 # luoli 05/31/16 - Fix bug 23317915 # bbeelamk 05/26/16 - Fix bug 23273294 # xyuan 05/25/16 - Fix bug 23344250 # luoli 05/18/16 - Fix bug 23279919 # jesugonz 05/17/16 - Add Extended Cluster Utils # luoli 05/16/16 - Fix bug 23294894 # xyuan 05/09/16 - Fix bug 22382998 # xyuan 04/21/16 - ODA Lite/SIP/IaaS # bbeelamk 04/13/16 - Fix bug 23095140 # ssprasad 04/11/16 - Move getASMDiscStr to oraafd.pm # luoli 04/10/16 - Fix bug 23050583 # bbeelamk 04/10/16 - Fix bug 23073616 # bbeelamk 04/05/16 - Fix bug 23053427 # xyuan 04/03/16 - Fix bug 23001869 # luoli 03/31/16 - Fix bug 23000412 # muhe 03/29/16 - Fix bug 23004889 # xyuan 03/29/16 - Fix bug 23014561 # bbeelamk 03/16/16 - Fix bug 22685861 # luoli 03/15/16 - Fix bug 22894620 # mperezh 03/10/16 - Add QueryRes # muhe 03/09/16 - Fix bug 22894801 # luoli 03/08/16 - Modify add_rim_listener() # bbeelamk 03/06/16 - Fix srvctl error # luoli 03/03/16 - Fix bug 22810833 # luoli 03/02/16 - Fix bug 22855739 # luoli 03/01/16 - Fix bug 22825685 # jorhuert 02/25/16 - Export isODADomu # muhe 02/25/16 - Fix bug 22831385 # tankumar 02/23/16 - Fixing bug 22486914 # luoli 02/17/16 - Fix bug 22700293 # muhe 02/17/16 - Fix bug 22751110 # xyuan 02/14/16 - Fix bug 22613675 # bbeelamk 02/07/16 - Fix bug 22642839 # bbeelamk 02/03/16 - Fix bug 22571472 # bbeelamk 02/03/16 - Display srvctl command output # bbeelamk 01/24/16 - Fix bug 22478963 # luoli 01/18/16 - Fix bug 22554179 # muhe 01/17/16 - Fix bug 22557655 # bbeelamk 01/12/16 - Fix bug 22522193 # xyuan 01/08/16 - Fix lrg 18794387 # bbeelamk 01/07/16 - Fix bug 21640948 # luoli 01/07/16 - Fix bug 22509821 # muhe 12/23/15 - Fix bug 22459788 # bbeelamk 12/21/15 - Fix bug 22124821 # luoli 12/21/15 - Fix bug 22447381 # muhe 12/17/15 - Fix bug 22381392 # luoli 12/15/15 - Fix bug 21921412 # bbeelamk 12/16/15 - Fix bug 20848569 # muhe 12/14/15 - Fix bug 22360512 # luoli 12/13/15 - Fix bug 22375526 # papatel 12/13/15/- Fix Bug 20635302 # luoli 12/09/15 - Fix bug 22341211 # sbezawad 12/08/15 - Implement isDSCConfigured # luoli 12/07/15 - Fix bug 21836771 # bbeelamk 12/07/15 - Fix bug 22306693 # xyuan 12/03/15 - Fix bug 22283181 # muhe 11/30/15 - Fix bug 22259753 # madoming 11/27/15 - Bug 22273934: Moved getACFSVolumeDevice to oraacfs # muhe 11/27/15 - Fix bug 22230467 # xyuan 11/26/15 - Fix bug 22200719 # bbeelamk 11/22/15 - fix bug 21626275 # takiba 11/19/15 - bug22195657: dont kill non-numeric PIDs # sbezawad 11/17/15 - Bug 21919798: Add cluster properties to clscfg # xyuan 11/18/15 - Fix bug 22223328 # sbezawad 11/17/15 - Bug 21919798: Add cluster properties to clscfg # muhe 11/11/15 - Fix bug 22148653, 22162692 # samjo 11/10/15 - Fix bug 22171603 Add isDSCConfigured() # samjo 10/29/15 - Fix bug 21336850 Issue 'asmcmd mount -a' # xyuan 11/10/15 - Fix bug 22177601 # luoli 11/09/15 - Fix bug 22171962 # muhe 11/08/15 - Fix bug 21745923 # luoli 11/04/15 - Fix bug 21981335 # luoli 10/28/15 - Fix bug 21971395 # ashutagr 10/28/15 - Use emulated hostname in dev env-node2site mapping # bbeelamk 10/27/15 - Fix bug 21942884 # luoli 10/27/15 - Remove some useless code segment in sub # createDiskgroupRes() # xyuan 10/25/15 - Fix bug 22088866 # xyuan 10/22/15 - Fix bug 21688235 # muhe 10/20/15 - Fix bug 22078576 # mperezh 10/19/15 - 21867358 - Disble proxy # xyuan 10/18/15 - Fix bug 22016711 # jmarcias 10/16/15 - Move cluutil calls to crsutils # luoli 10/13/15 - Fix bug 22006277 # jmarcias 10/08/15 - Fix bug 21964314 # bbeelamk 10/08/15 - Fix bug 21689973 # bbeelamk 09/30/15 - Fix bug 20679385 # bbeelamk 09/29/15 - Fix bug 21907866 # jmarcias 09/21/15 - Fix bug 21869791 # bbeelamk 09/15/15 - Changing srvctl method # bbeelamk 09/09/15 - Remove duplicate file name isRolling # jmarcias 09/02/15 - Fix bug 21755091 # luoli 08/31/15 - Fix bug 21687333 # arpshukl 08/27/15 - Disable OPC # wmiyoshi 08/24/15 - Fix bug 21685231 - do not add CHA resource # madoming 08/19/15 - Fix bug 21572384 - Add function isOldVersionLT11204 # luoli 08/17/15 - Fix bug 21640970 # luoli 08/16/15 - Fix bug 20861530 # jmarcias 08/13/15 - Fix bug 21618216 # xyuan 08/09/15 - Fix bug 21562230 # bbeelamk 08/04/15 - Set crsdeconfig log loc to dchome during deinstall # rdasari 07/31/15 - do not disable proxy asm after it is added # xyuan 07/29/15 - Fix bug 21521798 # luoli 07/28/15 - Add -y -z -h to CLSCFG command # yifyang 07/17/15 - bug-17551588 # bbeelamk 07/20/15 - Fix lrg 17723321 # jmarcias 07/16/15 - Fix bug 21299632 # muhe 07/09/15 - Fix bug 21047431 # jmarcias 07/09/15 - Add JNI and native trace to cluutil # bbeelamk 07/09/15 - Fix bug 21269876 # jmarcias 07/06/15 - Fix bug 21186706 # luoli 07/01/15 - Fix bug 21324323 # jmarcias 06/25/15 - Fix bug 21137634 # bbeelamk 06/23/15 - Fix bug 21237070 # bbeelamk 06/23/15 - Fix bug 21299295 # shullur 06/21/15 - For migrating CHM modules to new root script changes # luoli 06/19/15 - Fix bug 21281093 # jmarcias 06/16/15 - Fix bug 21256573 # xyuan 06/15/15 - Fix bug 21253660 # luoli 06/08/15 - Fix bug 21213444 # jmarcias 06/05/15 - Fix bug 21124493 # agorla 06/04/15 - add subroutine isOldVersionLT122 # muhe 06/03/15 - Fix bug 21182047 # jmarcias 05/30/15 - Fix bug 21137012 # ksviswan 05/27/15 - move the copyfiles routine to crscpcfg.pm # bbeelamk 05/26/15 - Fix bug 21147131 # espgarci 05/08/15 - Remove crsoc4j reference # muhe 05/08/15 - Fix bug 21039599 # muhe 05/04/15 - add_scan change # xyuan 04/24/15 - Fix bug 20954213 # luoli 04/23/15 - Fix bug 20947747 # jmarcias 04/22/15 - Fix bug 20899966 # muhe 04/20/15 - Fix bug 20916630 # madoming 04/17/15 - Fix bug 20769757 # muhe 04/15/15 - Add node attribute constants for deconfig # jinjche 04/14/15 - Add definition of subroutines isVersionGTE122 # jmarcias 04/14/15 - Fix bug 20871314 # xyuan 04/09/15 - Add sub isOLRConfiguredCkpt # xyuan 04/09/15 - Fix bug 20855726,20855292 # madoming 04/06/15 - Move isACFSSupported, isACFSInstalled and # isACFSLoaded functions to oraacfs.pm # gnagiah 04/03/15 - Common setup routines for diagsnap # jmarcias 04/06/15 - Fix bug 20818194 # kiraj 04/03/15 - Fix bug 20811588 # bbeelamk 04/01/15 - Fix bug 20567041 # jmarcias 03/20/15 - Fix bug 20734170 # muhe 03/19/15 - Fix bug 20726559 # xyuan 03/17/15 - Changes for ugrading std ASM # madoming 03/16/15 - Changes for new framework # jmarcias 03/11/15 - Fix bug 20569941 # jmarcias 03/10/15 - Fix bug 20614790 # muhe 03/09/15 - Fix bug 20373088 # luoli 03/04/15 - Fix bug 20598131 # xyuan 02/28/15 - Fix bug 20614945 # bbeelamk 02/06/15 - Fix bug 20465849 # madoming 02/12/15 - Exporting isOPCDomu and isOPCDom0 functions # luoli 02/09/15 - Fix bug 19232406 # luoli 02/04/15 - Fix bug 20415229 # muhe 01/30/15 - Remove global checkpoint file only on lastnode # jmarcias 01/30/15 - Fix bug 20441873 # xyuan 01/22/15 - Fix bug 20130937 # luoli 01/13/15 - Separate downgrade/deconfig flow # bbeelamk 12/29/14 - Fix bug 19951778 # xyuan 12/29/14 - Fix bug 20276459 # luoli 12/23/14 - rsc modeling for downgrade/deconfig # xyuan 12/21/14 - Add sub setClusterType # jmarcias 12/19/14 - Fix bug 20042014 # xyuan 12/15/14 - Fix bug 20206616 # sbezawad 12/18/14 - Bug 20019354: Migrate OCR and OLR to new framework # xyuan 12/13/14 - Fix bug 20202025 # muhe 12/10/14 - Fix bug 20187693 # muhe 12/09/14 - Fix bug 20099320 # bbeelamk 12/05/14 - Fix bug 20126353 # jmarcias 12/04/14 - Fix bug 20099783 # bbeelamk 11/25/14 - upgrade from std cluster # jmarcias 11/24/14 - Fix bug 20061168 # emarron 11/21/14 - Check return value of commands in crsctlResource # jmarcias 11/18/14 - Fix bug 20041911 # jmarcias 11/14/14 - Fix bug 20019390 # xyuan 11/13/14 - Application Cluster # papatel 11/06/14 - Bug 19530755 # muhe 11/04/14 - Fix bug 18844632 # xyuan 11/04/14 - Add orasrvm.pm # bbeelamk 10/30/14 - Fix bug 19688282 # luoli 10/28/14 - Fix bug 19908346 # minzhu 10/17/14 - handle anchor kill # muhe 10/16/14 - Fix bug 19804032 # jmarcias 10/13/14 - Fix bug 19684316 # bbeelamk 10/08/14 - Fix bug 19612597 # ssprasad 09/29/14 - Move isAFDSupported (crsutils.pm -> oraafd.pm) # luoli 09/27/14 - Skip checking 'lastnode' status in writeCkpt unless # it is last node upgrade # muhe 09/26/14 - Fix bug 19683886 # muhe 09/16/14 - Fix bug 19513650 # gnagiah 09/15/14 - Bug 19617592. handle node num 0 in case of vendor # clusterware # luoli 09/14/14 - Fix bug 19539418,19594906 # luoli 09/05/14 - Fix bug 19450633 # luoli 09/03/14 - Fix bug 19513351 # muhe 08/28/14 - Fix bug 19464978 # bbeelamk 08/26/14 - fix for 19280491 # muhe 08/21/14 - Fix bug 19471836 # luoli 08/08/14 - Fix bug 19170846 # xyuan 08/06/14 - Add node attribute to $CFG # jolascua 07/30/14 - Bug 18762843: validate null value in parameters # lcarvall 07/21/14 - Bug 13800615 - Use mnemonic string instead numeric values # rdasari 07/21/14 - remove checkpoint for osd setup # madoming 07/16/14 - Fix LRG's lrgsihaupg3 and lrgru11204x3h # jmarcias 07/08/14 - Modify copy_wrapper_scripts() # emarron 06/30/14 - add is_Exadata # madoming 06/30/14 - Fix bug 19068333 - Call reset_ACFS_wrapper_scripts # gnagiah 06/05/14 - Bug 18896138. rollback txn gnagiah_bug-18488955 # rdasari 06/03/14 - unset SRVM_TRACE in cluutil bug 18895766 # rdasari 05/29/14 - modify 10.2 db resources bug 18708349 # muhe 05/20/14 - Fix bug 18794049 # muhe 05/18/14 - Fix bug 18751257 # luoli 05/18/14 - Fix bug 18819158 # luoli 05/08/14 - Fix bug 18716571 # muhe 05/12/14 - Fix bug 18701111 # xyuan 05/06/14 - Fix bug 18493777 # muhe 04/30/14 - Fix bug 18675566 # rdasari 04/30/14 - bug 18346917, skip copying shrept.lst if it exists # muhe 04/23/14 - Fix bug 18643060 # xyuan 04/17/14 - Fix bug 18606913 # gnagiah 04/15/14 - Bug 18488955. delete the temp location before # instantiation # luoli 04/08/14 - Fix bug 18494975 # luoli 04/13/14 - Fix bug 18510735 # xyuan 04/07/14 - Fix bug 18507616 # minzhu 04/04/14 - 18508267, install HAIP when upgrade from 11.1 # luoli 03/31/14 - Fix bug 18480923 # alolau 03/28/14 - Support AFD installation on AIX # xyuan 03/26/14 - Add sub findActiveGIProcs & termActiveGIProcs # muhe 03/24/14 - Fix bug 17596579 # xyuan 03/19/14 - Fix bug 18415237 # luoli 03/19/14 - Fix bug 18364276 # rdasari 03/17/14 - modify isNodeAlive (bug 18336132) # muhe 03/13/14 - Fix bug 18386124 # luoli 03/11/14 - Fix bug 17967087 # xyuan 03/06/14 - Fix bug 17861637 # ssprasad 03/05/14 - Enable AFD on Windows # xyuan 03/02/14 - Fix bug 18279534 # xyuan 03/02/14 - Fix bug 18331275 # muhe 02/21/14 - Fix bug 18227454 # xyuan 02/21/14 - Fix bug 18278616 # xyuan 02/18/14 - Fix bug 18233693 # ssprasad 02/18/14 - #18265919: use kfod to query asm_diskstring # xyuan 02/12/14 - Fix bug 18128918 # rdasari 02/10/14 - remove version check in startOhasd_SIHA # luoli 02/06/14 - Fix bug 18161639 # ssprasad 02/03/14 - Enable AFD for Solaris # rdasari 01/31/14 - check for error from add asmlistener # ssprasad 01/27/14 - Fix bug 18102497 - correct asm_diskstring param # shullur 01/23/14 - Handle recursive directories in movedir() bug17991840 # luoli 01/14/14 - Ocr backup and restore # ssprasad 01/14/14 - Modify isAFDSupported to check for dev env # Add getASMDiscStr(), osd_setup() # luoli 01/13/14 - Fix bug 18039176 # xyuan 01/16/14 - Fix bug 17602658 - add sub getMgmtdbSPfile & # createMgmtdbPfile # csivanan 01/09/14 - bug/17778536 # luoli 01/09/14 - Fix bug 18054868 # xyuan 01/08/14 - Fix bug 18003879 - add sub kfodListDiskgroups # luoli 01/08/14 - Fix bug 17992711 # luoli 12/25/13 - Fix bug 18002693 # xyuan 12/25/13 - Fix lrg problem 11122672 # xyuan 12/23/13 - Fix bug 17787018 # madoming 12/17/13 - Fix bug 17956714 - Adding silent option to # isACFSInstalled and isACFSLoaded Functions # xyuan 12/13/13 - Fix bug 17818075 # xyuan 12/12/13 - Fix bug 17893782 # rdasari 12/10/13 - add OLR arugment to importASMCredentials # samjo 12/05/13 - Bug 17778985. Flex ASM upgrade # xyuan 12/04/13 - Fix bug 17899506 # xyuan 12/03/13 - Fix bug 17040372 # madoming 11/27/13 - Add isACFSInstalled and isACFSLoaded Functions # luoli 11/21/13 - write ckpts in ocr # xyuan 11/20/13 - Fix bug 17786777 # luoli 11/19/13 - Fix bug 17811621 # xyuan 11/15/13 - Fix bug 17787018 # xyuan 11/14/13 - Fix bug 17763351 # rdasari 11/12/13 - do not use exclude file for unlockHAhome # xyuan 11/11/13 - Fix bug 17158147 # luoli 11/11/13 - Fix bug 17342639 # minzhu 11/08/13 - 15847266, haip in upgrade to 12.1 # patching mgmtdb # xyuan 10/29/13 - Fix bug 17293244 - add sub deleteMGMTDB # xyuan 10/29/13 - Remove sub createMgmtdbDir_Old # cnagur 10/29/13 - Support for TFA # xyuan 10/28/13 - Fix bug 17649114 # shullur 10/28/13 - For fixing the warning issue in case of chdir call # xyuan 10/23/13 - Fix bug 17433179 # shullur 10/21/13 - For removing the check of MGMTDB. Bug 17500411 # xyuan 10/21/13 - Fix bug 17624555 # xyuan 10/11/13 - Fix bug 17469910 - add sub startOhasdOnlyInSIHA # siyarlag 10/09/13 - Bug17476998: AFD is not supported on Exadata # xyuan 10/07/13 - Move two utils functions into this module # yifyang 10/03/13 - bug-17337288 # xyuan 09/30/13 - Fix bug 17513885 - forward merge the code for # patching mgmtdb # xyuan 09/27/13 - Fix bug 17499662 & 17525572 # xyuan 09/23/13 - Fix bug 17444212 # xyuan 09/13/13 - Fix bug 17313549 # xyuan 09/10/13 - Add error handling for s_set_ownergroup in sub # unlockHome # xyuan 09/03/13 - Fix bug 17379236 # shullur 08/28/13 - For getting MGMTDB configuration status from srvctl # pckamath 08/21/13 - Bug13991403 # samjo 08/12/13 - Bug 17173973. Add isCHASupported() and # isCHAConfigured() # xyuan 08/08/13 - Fix bug 17288470 - fix typos # dpham 07/30/13 - export crscfg_trace, crscfg_trace_file, DEBUG # xyuan 07/30/13 - Fix sub access_counter # spavan 07/29/13 - fix bug17236427 # xyuan 07/26/13 - Fix bug 16695577 # xyuan 07/23/13 - Fix bug 16542792 - Add sub isHomeShared # xyuan 07/23/13 - Fix bug 15894162 # xyuan 07/23/13 - Fix bug 17194603 # xyuan 07/14/13 - Fix bug 16912647 # xyuan 07/04/13 - Remove unused variable $addfile # xyuan 07/02/13 - Add sub backupOCR # rdasari 06/27/13 - add ping_targets # xyuan 06/26/13 - Clear useless functions. # samjo 06/19/13 - Bug 13108621. Add add_cha # shullur 06/13/13 - For fixing bug 16055418 # xyuan 06/13/13 - Add sub print_info # xyuan 05/30/13 - Fix bug 14795873 # madoming 05/15/13 - Start CSS after starting ohasd in SIHA when you # are upgrading. # xyuan 04/25/13 - Remove the recursive call in # system_cmd_capture_noprint # rdasari 04/18/13 - add scan change # sidshank 04/07/13 - Apply LANG settings if passed . # madoming 03/25/13 - Add new functions for bouncing OHASD in SIHA: # bounce_ohasd_SIHA, stopFullStack_SIHA and # startOhasd_SIHA # xyuan 03/14/13 - Fix bug 16483300 # xyuan 03/04/13 - Fix bug 16088332 # xyuan 02/27/13 - Changes as part of fix for bug 16274133 # rdasari 02/21/13 - add ASM_MODE_CLIENT # rdasari 02/14/13 - XbranchMerge rdasari_bug-16101138_1 from # st_has_12.1.0.1 # vdandu 02/14/13 - Fix bug 16329583 # sidshank 02/12/13 - remove the checkpoint index.xml file during # deconfig. # xyuan 02/05/13 - Fix bug 14602984 - Add sub isSIHA # xyuan 01/24/13 - XbranchMerge xyuan_bug-16180969 from st_has_12.1.0.1 # rdasari 01/11/13 - fix to disable security bug # sidshank 01/10/13 - fix isODA function. # xyuan 01/10/13 - Call srvctl add listener without listener username # if GI user = listener username # xyuan 01/08/13 - XbranchMerge xyuan_bug-14768261 from st_has_12.1.0.1 # xyuan 01/06/13 - XbranchMerge xyuan_bug-16072476 from st_has_12.1.0.1 # xyuan 01/06/13 - XbranchMerge xyuan_bug-16026674 from st_has_12.1.0.1 # xyuan 01/04/13 - Fix bug 16072476 # xyuan 12/26/12 - Fix bug 14768261 # xyuan 12/20/12 - Fix bug 16026674 # samjo 12/10/12 - Bug 15980929. Correct list for Linux # xyuan 12/09/12 - Fix bug 15969912 - Add listener on the first node # xyuan 12/06/12 - Add CVU baseline command # xyuan 11/27/12 - Fix bug 14836852 # vdandu 11/21/12 - Bug 14513204: Fix permissions of RCALLDIR dir's # rdasari 11/14/12 - modify isODA # xyuan 10/29/12 - Fix bug 14796373 & 14827104 # xyuan 10/22/12 - Set the ckpt ROOTCRS_STACK to CKPTFAIL in sub # dietrap # xyuan 10/14/12 - Fix bug 14156740 - add sub compact_instlststr # xyuan 10/10/12 - Fix bug 14634138 # rdasari 10/10/12 - add ASM_MODE const # spavan 10/09/12 - fix bug14739163 - isCVUConfigured should use old crs # home # yizhang 10/08/12 - fix bug 14597440 # epineda 09/27/12 - Bugfix 13539130 # spavan 09/26/12 - fix bug14218734 - config cvu with interval parsing # shullur 09/25/12 - For moving getcrsrelver to crsutils.pm # xyuan 09/18/12 - Fix bug 14621340 - make error in # removing CVU fatal # shullur 09/17/12 - For adding the subroutine to check if the version is # greater than or equal to 12.1 # rdasari 09/13/12 - use GNS_TYPE to configure shared GNS # shullur 09/13/12 - For fixing the current working dir in # crf_delete_bdb. Bug 14614307. # rtamezd 09/12/12 - Fix bug 14115195 # rdasari 09/12/12 - remove skipping validation for some parameters # rdasari 09/12/12 - add disableRemoteASM() # xyuan 09/06/12 - Fix bug 14577135 # xyuan 09/04/12 - Fix bug 14560280 # xyuan 08/31/12 - Fix bug 14535011 - Add '-init' option # jmunozn 08/24/12 - Remove run_as_user3 and add get_qosctl_path # rdasari 08/22/12 - add isODA() # xyuan 08/22/12 - Fix bug 14523800 - Add 'force' for importing ASM # credentials # xyuan 08/20/12 - Fix bug 14507349 # rdasari 08/16/12 - add queryVoteDisks # csivanan 08/10/12 - bug/14115152 # xyuan 08/07/12 - Fix bug 9584563 # xyuan 08/03/12 - Fix bug 14392140 # xyuan 08/01/12 - Fix bug 13730408 # xyuan 07/31/12 - Correct the log file name for prepatch and postpatch # xyuan 07/30/12 - Fix bug 14386036 # rdasari 07/26/12 - change node role if start fails in hub mode # xyuan 07/24/12 - Fix bug 14328087 # rdasari 07/18/12 - add ioservers only for BC # rdasari 07/16/12 - set rc before running kfod # xyuan 07/12/12 - Fix bug 14302401 # rdasari 07/10/12 - do not add asm or create diskgroup on client cluster # xyuan 07/05/12 - Fix bug 14073012 # ssprasad 07/03/12 - Fix bug 14272326 # xyuan 06/21/12 - Fix bug 14223624 # rtamezd 06/18/12 - Fix bug 14121895 # shullur 06/15/12 - For handling default case of bdb location # xyuan 06/12/12 - Fix bug 14117806 # rdasari 06/04/12 - modify the .bin file check in set_file_perms # xyuan 05/24/12 - Fix bug 14111446 # xyuan 05/18/12 - Fix bug 14082413 - Add isFirstNodeToConfig # tvbabu 05/09/12 - bug 14010695 replacing POSIX tmpnam with tempfile # mprasant 05/03/12 - remove REPLICA in 12c # rtamezd 05/03/12 - Fix bug 13993142 # rtamezd 04/25/12 - Removed -subnet option from add_mgmt_db_listener # xyuan 04/24/12 - Add options for joining an upgraded cluster # sidshank 04/20/12 - fix for bug 13926993. # rdasari 04/19/12 - create ioserver before asm listener (bug 13892154) # rtamezd 04/15/12 - Fix bug 13953338 # xyuan 04/13/12 - Fix bug 13954041 # sidshank 04/09/12 - Removing the code setting ORACLE_OWNER from # environment on Windows. # xyuan 04/05/12 - Fix bug 13929451 # gmaldona 03/30/12 - Move oc4j setup functions to crsoc4j.pm # rtamezd 03/28/12 - Fix bug 13899926 # rtamezd 03/26/12 - Move stop_ohasd_resources from crsinstall.pm # rtamezd 03/23/12 - Fix bug 13868122 # rtamezd 03/22/12 - Fix bug 13807580 # anutripa 03/12/12 - Disable CRF if MGMTDB not enabled # xyuan 03/12/12 - Fix bug 13833629 # xyuan 03/10/12 - Fix the recursion problem that the trace routine # calls dieformat which in turn calls trace # rtamezd 03/09/12 - Fix bug 13822648 # xyuan 03/08/12 - Fix bug 13797791 # rtamezd 03/06/12 - Fix bug 13811490 # rsreekum 03/02/12 - Modify oifcfgNetworks to skip public for dev_env # rtamezd 02/23/12 - Fix bug 13768280 # xyuan 02/23/12 - Fix trace statements # rtamezd 02/20/12 - Fix bug 9660844 # xyuan 02/20/12 - Support new patching interfaces # rtamezd 02/17/12 - Fix bugs 13725099 and 13721908 # sidshank 02/09/12 - remove first_node_tasks subroutine # xyuan 02/07/12 - Add getNodeStatus subroutine # rtamezd 02/07/12 - Fix bug 13643262 # rdasari 02/07/12 - modify isFirstNodeToStart # rtamezd 01/26/12 - Fix bug 13621968 # rtamezd 01/24/12 - Make configFirstNode return at first failure # rtamezd 01/19/12 - Abort configure_OCR if OCR is already configured # rtamezd 01/17/12 - Moved add_localOlr_OlrConfig_OcrConfig from # crsinstall # gsanders 01/15/12 - Eliminate ACFS registry # sidshank 01/12/12 - Adding subroutine getCurrentNodenameList() # xyuan 01/08/12 - Add utility functions for downgrade # shullur 01/06/12 - XbranchMerge shullur_bug-12639016 from st_has_11.2.0 # shullur 01/06/12 - XbranchMerge shullur_bug-12976590 from st_has_11.2.0 # rdasari 01/05/12 - modify remove_checkpoints # rtamezd 01/05/12 - Fix bug 13560019 # xyuan 12/26/11 - Fixed bug 13532044 # spavan 12/22/11 - fix bug13401742 # xesquive 12/20/11 - add isAutoNode function # rtamezd 12/16/11 - Fix bug 13430204 # sidshank 12/13/11 - Adding platform check for windows for verifying the # executablity of crsctl/srvctl based on uid and gid # shullur 12/12/11 - XbranchMerge shullur_bug-12673587 from st_has_11.2.0 # xyuan 12/08/11 - Fix oifcfgNetworks so that it can deal with IPV6 # xyuan 12/04/11 - Fix bug 13091719 # agraves 11/30/11 - Add new constant for REBOOT. # rtamezd 11/30/11 - Fix bug 13439445 # xyuan 11/30/11 - Fix bug 13445306 # rdasari 11/30/11 - remove isFirstNode parameter # anjiwaji 11/29/11 - Add subroutine to lock ACFS files # xyuan 11/22/11 - Fix bug 13390751 # sidshank 11/21/11 - Adding startExclCRS subroutine # sidshank 11/15/11 - adding add_mgmt_db_listener subroutine. # sowong 11/14/11 - fix bug13109878 - revert oc4j to 11.2 state # xyuan 11/14/11 - Fix bug 13340630 # xyuan 11/05/11 - Fix bug 13329109 # shullur 11/03/11 - XbranchMerge shullur_bug-12614795 from st_has_11.2.0 # shullur 11/03/11 - XbranchMerge shullur_bug-12614795 from st_has_11.2.0 # shullur 11/03/11 - XbranchMerge shullur_bug_11852891 from st_has_11.2.0 # xyuan 11/02/11 - Fix bug 12945012 & 13251040 # xyuan 10/31/11 - Fix bug 13328777 # xyuan 10/25/11 - Fix bug 13006149 # xyuan 10/10/11 - Support Private and ASM networks # xyuan 09/29/11 - Improvements for 12c upgrade # rtamezd 09/26/11 - Added new parameters # wsun 09/08/11 - Add oc4j as root # xyuan 09/07/11 - Fix bug 12908387 # sidshank 08/11/11 - adding the fail condition for superUser check in # non-dev environment for bug 12739826 # xyuan 08/09/11 - Far ASM support # xyuan 08/04/11 - XbranchMerge xyuan_bug-12795595 from # st_has_11.2.0.3.0 # xyuan 08/03/11 - XbranchMerge xyuan_bug-12752359 from # st_has_11.2.0.3.0 # xyuan 08/03/11 - Fix bug 11851866 # xyuan 07/27/11 - XbranchMerge xyuan_bug-12585291 from # st_has_11.2.0.3.0 # xyuan 07/27/11 - XbranchMerge xyuan_bug-12727247 from # st_has_11.2.0.3.0 # xyuan 07/27/11 - XbranchMerge xyuan_bug-12701521 from # st_has_11.2.0.3.0 # xesquive 07/26/11 - Add functions set_bold and reset_bold # xyuan 07/17/11 - BC commands for 12c # spavan 06/19/11 - add method to remove cvu # rdasari 06/16/11 - add credentials routines # xyuan 06/10/11 - expose stack startup levels # rdasari 06/02/11 - skips validation for big cluster parameters # lmortime 05/25/11 - Making EVMD start first # dpham 05/23/11 - Modulize upgrade function # rdasari 05/20/11 - set_file_perms fix # dpham 03/28/11 - New for 12c # package crsutils; use strict; use English; use Exporter; use File::Copy; use File::Path; use File::Find; use File::Basename; use File::Spec::Functions; use File::Temp qw/ tempfile/; use Sys::Hostname; use Net::Ping; use Carp; use Cwd; use Socket; use Env qw(NLS_LANG); use Env qw(SRVM_TRACE SRVM_NATIVE_TRACE SRVM_JNI_TRACE); use Env qw(CRS_ADR_DISABLE); use Env qw(ORA_CRS_HOME); use POSIX qw(tzset); use Term::ANSIColor; use Scalar::Util 'blessed'; no if $] >= 5.017011, warnings => 'experimental::smartmatch'; use constant ERROR => "-1"; use constant FAILED => "0"; use constant SUCCESS => "1"; use constant TRUE => "1"; use constant FALSE => "0"; use constant WARNING => "2"; use constant REBOOT => "3"; use constant CKPTSUC => 'SUCCESS'; use constant CKPTFAIL => 'FAIL'; use constant CKPTSTART => 'START'; use constant MAX_NUM_OF_LOG_FILES => 10; # How much of the stack do we want to start? use constant START_STACK_NONE => 0; use constant START_STACK_EVMD => START_STACK_NONE + 1; # Start EVMD use constant START_STACK_MDNSD => START_STACK_EVMD + 1; # Start MDNSD use constant START_STACK_GPNPD => START_STACK_MDNSD + 1; # Start GPNPD use constant START_STACK_GIPCD => START_STACK_GPNPD + 1; # Start GIPCD use constant START_STACK_CSSD => START_STACK_GIPCD + 1; # Start CSSD & GIPCD use constant START_STACK_CTSSD => START_STACK_CSSD + 1; # Start CTSSD use constant START_STACK_CHM => START_STACK_CTSSD + 1; # Start CHM use constant START_STACK_HAIP => START_STACK_CHM + 1; # Start HAIP use constant START_STACK_ASM => START_STACK_HAIP + 1; # Start ASM use constant START_STACK_CRSD => START_STACK_ASM + 1; # Start CRSD use constant START_STACK_ALL => START_STACK_CRSD + 1; # Stuff concerning big cluster use constant NODE_ROLE_HUB => 'hub'; use constant NODE_ROLE_RIM => 'leaf'; use constant NODE_ROLE_AUTO => 'auto'; # standard and flex/remote asm modes use constant ASM_MODE_LEGACY => 'legacy'; use constant ASM_MODE_FLEX => 'remote'; use constant ASM_MODE_CLIENT => 'client'; # standard and flex cluster modes use constant CLUSTER_MODE_STD => 'standard'; use constant CLUSTER_MODE_FLEX => 'flex'; use constant GI_STACK_DOWN => '0'; use constant GI_STACK_UP => '1'; use constant GI_STACK_PARTIAL => '2'; # use mnemonic string instead a numeric value use constant USECHKPOINTS => "1"; use constant NOTUSECHKPOINTS => "0"; use constant FIRST_NODE_TO_CONFIG => "FirstNodeToConfig"; use constant NONFIRST_NODE_TO_CONFIG => "NonFirstNodeToConfig"; use constant FIRST_NODE_TO_UPGRADE => "FirstNodeToUpgrade"; use constant MIDDLE_NODE_TO_UPGRADE => "MiddleNodeToUpgrade"; use constant LAST_NODE_TO_UPGRADE => "LastNodeToUpgrade"; use constant SIHA_NODE_TO_CONFIG => "SIHANodeToConfig"; use constant SIHA_NODE_TO_UPGRADE => "SIHANodeToUpgrade"; use constant NONLAST_NODE_TO_DOWNGRADE => "NonLastNodeToDowngrade"; use constant LAST_NODE_TO_DOWNGRADE => "LastNodeToDowngrade"; use constant NONLAST_NODE_TO_DECONFIGURE => "NonLastNodeToDeconfigure"; use constant LAST_NODE_TO_DECONFIGURE => "LastNodeToDeconfigure"; # constants for extended cluster use constant CLUSTER_EXTENDED => 'true'; use constant CLUSTER_NOTEXTENDED => 'false'; # constants for cluster class use constant CLUSTER_CLASS_STANDALONE => "Standalone"; use constant CLUSTER_CLASS_DOMAINSERVICES => "DomainServices"; use constant CLUSTER_CLASS_MEMBER => "Member"; use constant CLUSTER_CLASS_DOMAIN_STR => "Domain"; # Because oracss.pm uses the constants defined here, export in a BEGIN # block so that they will not cause a compilation failure use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); our $CFG; BEGIN { @ISA = qw(Exporter); my @exp_const = qw(ERROR FAILED SUCCESS WARNING TRUE FALSE REBOOT CKPTSUC CKPTFAIL CKPTSTART START_STACK_NONE START_STACK_EVMD START_STACK_MDNSD START_STACK_GPNPD START_STACK_GIPCD START_STACK_CHM START_STACK_CTSSD START_STACK_CSSD START_STACK_ASM START_STACK_HAIP START_STACK_CRSD START_STACK_ALL GI_STACK_DOWN GI_STACK_PARTIAL GI_STACK_UP NODE_ROLE_HUB NODE_ROLE_RIM ASM_MODE_LEGACY ASM_MODE_FLEX ASM_MODE_CLIENT CLUSTER_MODE_STD CLUSTER_MODE_FLEX USECHKPOINTS NOTUSECHKPOINTS FIRST_NODE_TO_CONFIG NONFIRST_NODE_TO_CONFIG FIRST_NODE_TO_UPGRADE MIDDLE_NODE_TO_UPGRADE LAST_NODE_TO_UPGRADE SIHA_NODE_TO_CONFIG SIHA_NODE_TO_UPGRADE NONLAST_NODE_TO_DOWNGRADE LAST_NODE_TO_DOWNGRADE NONLAST_NODE_TO_DECONFIGURE LAST_NODE_TO_DECONFIGURE CLUSTER_EXTENDED CLUSTER_NOTEXTENDED CLUSTER_CLASS_MEMBER CLUSTER_CLASS_DOMAINSERVICES ); my @exp_func = qw(trace trim backtrace error dietrap dieformat is_dev_env tolower_host run_as_user run_as_user2 check_SuperUser is_Exadata copy_file create_dir create_dirs crs_exec_path run_crs_cmd system system_cmd system_cmd_capture read_file print_error start_resource srvctl extractVotedisks start_service check_service oifcfgiflst_to_instlststr isCVUConfigured setperm_vdisks isResRunning srvctl_tty instantiate_scripts copy_wrapper_scripts set_file_perms isCkptexist isCkptSuccess getCkptStatus isCkptPropertyExists writeCkpt writeCkptPropertyFile writeCkptProperty getVerInfo getCkptPropertyValue check_dir check_file checkServiceDown quoteDiskGroup get_crs_version setNetworkInterface stop_resource set_perms_ocr_vdisk add_ons isReusedg start_HAIP start_CVU enable_HAIP isCHMSupported isPathonASM isHAIPsupported isHAIPNonFatal isOCRonASM isAddNode isVersion112 isVersion111 isVersion10 isFirstNodeToStart isRAC_appropriate copydir isPrereqIgnored stop_diskmon ipv4_atol set_logging oifcfg_intf_parse add_CVU createDiskgroupRes remove_checkpoints remove_checkpoint_index stopClusterware isOwnerGroupValid isRolling start_clusterware validate_SICSS isONSexist setNodeRole getcursoftversion system_cmd_capture_noprint isOLRConfiguredCkpt isBigCluster setCurrentNodeRole setHubSize isHubNode isRimNode isNearASM createASMCredentials importASMCredentials isValidVersion versionComparison rscPreChecks isASMExists isLegacyASM isFarASM copyASMCredentials get_ons_port remove_CVU set_bold reset_bold isOldVersionLT112 add_ASM createCredDomain setConfiguredCRSHome getConfiguredCRSHome oifcfg_intf_to_instlststr add_GNS add_scan add_scan_listener start_GNS start_scan start_scan_listener getActiveNodeRoles startFullStack stopFullStack bounce_ohasd isHomeShared startOhasdOnly createMgmtdbDir startExclCRS stopOracleRestart add_mgmt_db_listener isAutoNode logsDirectory get_olsnodes_info getNodeNumList getLocalNodeName isASMConfigured getCurrentNodenameList extractDiskgroups setClusterType removeVotingDisk addVotingDisks removeVotingfiles addVotingFiles start_crsd_and_check start_excl_crs stop_crsd_and_check startExclNoCRS add_localOlr_OlrConfig_OcrConfig lockAcfsFiles waitForVipRes isVersionMatch getNodeStatus add_rim_listener checkGIStack unlockHome modifyparamfile check_NewCrsStack isFirstNodeToConfig setSrvctlTrace resetSrvctlTrace print_lines trace_lines print_trace_lines isVersionGTE1201 isVersionGTE122 getcrsrelver disableRemoteASM get_CVU_checkInterval disable_CVU enable_CVU compact_instlststr isODA isODADomu get_qosctl_path createNetLsnrWithUsername startNetLsnr getLsnrUsername removeNetLsnr stopNetLsnr getCurrentNetLsnrs upgradeNetLsnrWithUsername isOldVersionLT111 startListeners isSIHA bounce_ohasd_SIHA stopFullStack_SIHA startOhasd_SIHA print_info isInstallNode add_cha start_cha isOldVersionLT121 isOldVersion121010 deleteMGMTDB isCHASupported isCHAConfigured status_cha getActiveNodes DEBUG crscfg_trace crscfg_trace_file isMgmtdbConfigured isMgmtdbRunningOnLocalNode setup_env add_RCALLDIR_to_dirs add_ITDIR_to_dirs discoverVotingFiles startOhasdOnlyInSIHA isOldVersionLT11202 checkFirstNodeStatus getInstallNode isForcedFirstNode pingWithICMP isNodeAlive getNodeRoleStatus removeAllVotingDisks kfodListDiskgroups getClusterConfigMode getNodeConfigRole copy_file_to_nodes queryEnabledAndOnlineRes disable_enable_filesystem isOldVersionLT11203 osd_setup setCRSResourceUseAttr getMgmtdbSPfile createMgmtdbPfile getcrsrelver1 setOraHomeSID getASMMode getLocalNodeRole getCrsHome isPathShared findActiveGIProcs termActiveGIProcs isCkptFileExists cluutil removeFileOnDG check_resource_state run_cmd_as_usr_with_backticks modify102Resources startCrsWithoutResources skipSharednessCheck initial_cluster_validation isAppCluster copy_asm_credentials disableRes enable_start_acfs import_asm_credentials create_ohasd_resources copy_file_from_node perform_start_ohasd perform_register_ohasd crsctlResource writeGlobalCkpt pullGlobalCkptFileNIndex isRemoteGIMR get_has_version isMgmtDatabaseConfigured getSubnets get_oifcfg_info tz_localtime modify_networks remove_global_checkpoints remove_global_checkpoint_index TraceOptions isOldVersionLT12102 isOPCDom0 isOPCDomu compareFiles isASMProxyConfigured add_ASM_listener saveOraHome resetOraHome isIpv6 expandIpv6 is_emulated_nodes crsdResource srvctl_capture isOldVersion121 isOldVersionLT122 checkLeafNodes isOracleHomeShared isOracleBaseShared getLocalNodeNumber startSihaStack initialize_ADR_dirs gen_site_GUIDs isMgmtDbEnabled isOldVersionLT11204 copy_file_from_livenode remove_config_params_file isDSCSupported isDSCConfigured getNetworkType getClusterProperties getClusterClass queryRes getClusterClass_crsctl isODALite isODASIP isODAIaaS isODABMIaaS isYODA checkClusterwareOnNode isClusterExtended isSiteConfigured isNodeMappedToSite getASMInstanceCount isListenerConfigured isOldVersion12102 ); @EXPORT = qw($CFG); push @EXPORT, @exp_const, @exp_func; } # root script modules use crsgpnp; use oracss; use oraacfs; use s_crsutils; use crstfa; use oraafd; use oraohasd; use orachm; use XML::Parser; # FIXME: These should be moved to crsdelete.pm, which is the place # they are referenced my %elements = ('params' => 'HASH', 'srvctl_trc_suff' => 'COUNTER', 'pp_srvctl_trc_suff' => 'COUNTER', 'srvctl_trc_suff_pp' => 'COUNTER', 'cluutil_trc_suff_pp' => 'COUNTER', 'OLD_CRS_HOME' => 'INDIRECT', # see %indirect 'oldcrshome' => 'INDIRECT', # see %indirect 'oldcrsver' => 'INDIRECT', # see %indirect 'oldconfig' => 'HASH', 'CLSCFG_EXTRA_PARMS' => 'ARRAY', 'old_nodevips' => 'ARRAY', 'DESTCRSHOME' => 'SCALAR', 'isRerun' => 'SCALAR', 'wipCkptName' => 'SCALAR', 'isNodeCrashed' => 'SCALAR', 'restart_css' => 'SCALAR' ); # The %indirect hash allows the proper methods to be called from 'new', # when the initializing hash that is passed to 'new' is not properly # done. # The correct way of specifying, for example, 'oldcrsver' and # 'oldcrshome' in the initializing hash is not: # oldcrsver => $oldcrsver # oldcrshome => $oldcrshome # but is: # my @oldcrsversion = split('\.', $oldcrsver); # oldconfig => # {ORA_CRS_VERSION => \@oldcrsversion, # ORA_CRS_HOME => $oldcrshome # } my %indirect = ( 'oldcrsver' => \&oldcrsver, 'oldcrshome' => \&oldcrshome, 'OLD_CRS_HOME' => \&OLD_CRS_HOME ); =head2 new This is the class constructor method for this class =head3 Parameters A hash containing values for any key that is listed in the accessor methods section =head3 Returns A blessed class =head3 Usage my $cfg = crsutils->new( paramfile => $PARAM_FILE, OSDFILE => $defsfile, crscfg_trace => TRUE, HOST => $HOST ) This creates an object with parameters built from $PARAM_FILE and $defsfile, for HOST $HOST with tracing turned on. The values specified may be retrieved via the standard access methods, e.g. my $host = $cfg->HOST; will set $host to the $HOST value set in the hash passed to 'new'. While it is possible to pass any key/value pair to 'new', even ones for which there are no access methods, the values cannot be easily used without an access method. For the list of access methods, see the 'Access Methods Section' below =cut # Class constructor and methods sub new { my ($class, %init) = @_; $CFG = {}; for my $element (keys %init) { if (defined($init{$element})) { my $type = $elements{$element}; if (($type eq 'ARRAY' || $type eq 'HASH') && ref($init{$element}) ne $type) { croak "Initializer for $element must be $type reference"; } if ($type ne 'INDIRECT') { $CFG->{$element} = $init{$element}; } } } # Initialize stuff not in the initializer for my $element (keys %elements) { if (!defined($CFG->{$element})) { my $type = $elements{$element}; if ($type eq 'ARRAY') { $CFG->{$element} = []; } elsif ($type eq 'HASH') { $CFG->{$element} = {}; } elsif ($type eq 'COUNTER') { $CFG->{$element} = 0; } } } bless $CFG, $class; for my $element (keys %init) { if (defined($init{$element})) { my $type = $elements{$element}; if ($type eq 'INDIRECT') { $indirect{$element}($CFG, $init{$element}); } } } if (!$CFG->HOST) { $CFG->HOST(tolower_host()); } # The default is we start the entire stack if (!$CFG->stackStartLevel) { $CFG->stackStartLevel(START_STACK_ALL); } if (! $CFG->paramfile) { die("No configuration parameter file was specified"); } if (! -e $CFG->paramfile) { die("Configuration parameter file ", $CFG->paramfile, " cannot be found"); } print("Using configuration parameter file: ", $CFG->paramfile, "\n"); # Set up the parameters setup_param_vars($CFG->paramfile); # Now set various defaults/values based on various input my $OH = $CFG->params('ORACLE_HOME'); # for convenience if (!$CFG->ORA_CRS_HOME) { $CFG->ORA_CRS_HOME($OH); } if ($OH) { trace("Using Oracle CRS home $OH"); } else { die("The Oracle CRS home path not found in the configuration parameters"); } if (!(-d $OH)) { if (($CFG->PREPATCH) && ($CFG->DESTCRSHOME)) { trace("The Oracle CRS home in crsconfig_params may not exist"); } else { die("The Oracle CRS home path \"$OH\" does not exist"); } } my $default_trc_dir = logsDirectory(); my $default_olr_dir = catfile($OH, 'cdata'); #Don't use a time stamp in a dev environment my $timestamp; if(is_dev_env()) { $timestamp = ""; } else { $timestamp = "_" . genTimeStamp(); } if ($CFG->SIHA) { # Define stuff for SIHA # $CFG->parameters_valid($CFG->validateSIHAVarList); # list of params that MUST be specified in the param file my @required = ("ORACLE_HOME", "ORACLE_OWNER", "ASM_UPGRADE"); my $myplatformfamily = s_get_platform_family(); $myplatformfamily =~ tr/A-Z/a-z/; if ($myplatformfamily eq "unix") { push @required, "ORA_DBA_GROUP"; } # trace file if (!$CFG->crscfg_trace_file) { my $file = "roothas$timestamp.log"; if ($CFG->DECONFIG) { $file = "hadeconfig.log"; } if (($CFG->HAPatch) || ($CFG->PREPATCH) || ($CFG->POSTPATCH)) { $file = "hapatch$timestamp.log"; } if ($CFG->LOCK) { $file = "halock_$timestamp.log"; } if ($CFG->UNLOCK) { $file = "haunlock_$timestamp.log"; } $CFG->crscfg_trace_file(catfile($default_trc_dir, $file)); } $CFG->parameters_valid(validateVarList(@required)); if (!$CFG->OLR_DIRECTORY) { $CFG->OLR_DIRECTORY(catfile($default_olr_dir, 'localhost')); } } else { # Define stuff for clustered mode if (!$CFG->crscfg_trace_file) { my $host = $CFG->HOST; my $file = "rootcrs_$host$timestamp.log"; if ($CFG->DECONFIG) { $file = "crsdeconfig_$host$timestamp.log"; } if (($CFG->PREPATCH) || ($CFG->POSTPATCH)) { $file = "crspatch_$host$timestamp.log"; } if ($CFG->DOWNGRADE) { $file = "crsdowngrade_$host$timestamp.log"; } if ($CFG->GIMOVE) { $file = "crsmove_$host$timestamp.log"; } if ($CFG->LOCK) { $file = "crslock_$host$timestamp.log"; } if ($CFG->UNLOCK) { $file = "crsunlock_$host$timestamp.log"; } $CFG->crscfg_trace_file(catfile($default_trc_dir, $file)); } $CFG->parameters_valid(validateVarList()); if (!$CFG->OLR_DIRECTORY) { $CFG->OLR_DIRECTORY($default_olr_dir); } } # We really should destroy $CFG here; this will be impelmented later if ($CFG->parameters_valid) { trace("The configuration parameter file", $CFG->paramfile, " is valid"); } else { die("The configuration parameter file", $CFG->paramfile, " is not valid"); } my $crscfgTraceFile = $CFG->crscfg_trace_file; print("The log of current session can be found at:\n"); print(" $crscfgTraceFile\n"); if ($CFG->osdfile && -e $CFG->osdfile) { setup_param_vars($CFG->osdfile); } # pull all parameters defined in crsconfig_addparams if (-e $CFG->addfile) { setup_param_vars($CFG->addfile); if ($CFG->defined_param('CRS_ADDNODE') && $CFG->params('CRS_ADDNODE') eq "true") { $CFG->addnode(TRUE); } else { $CFG->addnode(FALSE); } } my $SUPERUSER = check_SuperUser(); my $platform = s_get_platform_family(); if ($platform ne 'windows') { $CFG->HAS_USER($SUPERUSER); } $CFG->SUPERUSER($SUPERUSER); if ($SUPERUSER) { $CFG->user_is_superuser(TRUE); } else { # If we are not Superuser and nor is it Dev Environment, fail!! if (! is_dev_env ()) { print "Insufficient privileges to execute this script."; if ($platform ne 'windows') { print "Cause: The root script was not run by root user with its " . "name being 'root' or UID being 0." . "\nAction: Make sure the root username is 'root' with UID " . "0 and rerun the script as the root user."; } else { print "Cause: The root script was not run by an administrator user.". "\nAction: Rerun the script with the administrator user."; } exit 2; } # If we are not SUPERUSER, indicate this and set SUPERUSER to # ORACLE_OWNER $CFG->user_is_superuser(FALSE); $CFG->SUPERUSER($CFG->params('ORACLE_OWNER')); } # Set some default values, if necessary $CFG->OLR_LOCATION(catfile($CFG->OLR_DIRECTORY, $CFG->HOST . '.olr')); if ($CFG->SUPERUSER && $CFG->defined_param('OLASTGASPDIR') && ! -e $CFG->params('OLASTGASPDIR')) { mkpath($CFG->params('OLASTGASPDIR'), 0, 0755); } if ((!$CFG->HAS_USER) || ($CFG->HAS_USER && $CFG->SIHA)) { $CFG->HAS_USER($CFG->params('ORACLE_OWNER')); } if (!$CFG->HAS_GROUP) { $CFG->HAS_GROUP($CFG->params('ORA_DBA_GROUP')); } if (!$CFG->s_run_as_user2p) { $CFG->s_run_as_user2p(\&crsutils::s_run_as_user2); } if (!$CFG->s_run_as_userp) { $CFG->s_run_as_userp(\&crsutils::s_run_as_user); } # If the versions of the 'run_as user' commands with parm order # reversed exist, as inidicated in the sybol table, set them now if (!$CFG->s_run_as_user2_v2p && defined($s_crsutils::{'s_run_as_user2_v2'})) { $CFG->s_run_as_user2p_v2(\&crsutils::s_run_as_user2_v2); } if (!$CFG->s_run_as_user_v2p && defined($s_crsutils::{'s_run_as_user_v2'})) { $CFG->s_run_as_userp_v2(\&crsutils::s_run_as_user_v2); } if (!$CFG->s_get_qosctl_path_p) { $CFG->s_get_qosctl_path_p(\&crsutils::s_get_qosctl_path); } $CFG->platform_family(lc(s_get_platform_family())); $CFG->SUCC_REBOOT(0); $CFG->print_config; # set owner & permission of trace file s_set_ownergroup ($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $CFG->crscfg_trace_file); s_set_perms ("0664", $CFG->crscfg_trace_file); # The GI should not use the ORA_CRS_HOME from the environment, bug #14755799 if (! is_dev_env()) { undef $ORA_CRS_HOME; } else { $ENV{'ORA_CRS_HOME'} = $OH; } # set environment variables $ENV{'ORACLE_HOME'} = $OH; $ENV{'ORACLE_BASE'} = $CFG->params('ORACLE_BASE'); # Set Language related environment variables, when passed # through automatic execution code. if ($CFG->LANG) { $ENV{'LC_ALL'} = $CFG->LANG; $ENV{'LC_MESSAGES'} = $CFG->LANG; $ENV{'LANG'} = $CFG->LANG; } setConfiguredCRSHome(); # Clusterware on NAS from user point of view # User selected NAS storage. GI will configure disk group on NAS. # # This option is indicated by 'CDATA_SIZE' parameter having a non zero value. # # Use 'CDATA_DISK_GROUP' and 'CDATA_BACKUP_DISK_GROUP' values if populated. # # The 'ASM_DISCOVERY_STRING' parameter will contain 1 or 2 mount points # separated by a comma. # e.g. User selects only 1 location to configure # ASM_DISCOVERY_STRING=/oradbnas/ocrdisks # User selects OCR and BACKUP location to configure # ASM_DISCOVERY_STRING=/oradbnas/ocrdisks,/oradbnas/gimrdisks if ($CFG->defined_param('CDATA_SIZE') && ($CFG->params('CDATA_SIZE') != 0)) { $CFG->ASM_STORAGE_USED(TRUE); if ($CFG->params('CDATA_DISK_GROUP') eq '') { $CFG->params('CDATA_DISK_GROUP', 'ocrvfdg'); } my $ocrvfdgfile = $CFG->params('CDATA_DISK_GROUP') . "disk0"; # Only one disk group to be configured if ($CFG->params('ASM_DISCOVERY_STRING') !~ /,/) { $ocrvfdgfile = catfile($CFG->params('ASM_DISCOVERY_STRING'), $ocrvfdgfile); $CFG->params('CDATA_DISKS', $ocrvfdgfile); } # OCR and Backup disk group to be configured else { # Clusterware on NAS from user point of view if ($CFG->defined_param('CDATA_BACKUP_SIZE') && ($CFG->params('CDATA_BACKUP_SIZE') == 0)) { die("Invalid configuration. User selected NAS shared storage for " . "the OCR location. User must select NAS shared storage for " . "backup location."); } my @mounts = split (/,/, $CFG->params('ASM_DISCOVERY_STRING')); $ocrvfdgfile = catfile($mounts[0], $ocrvfdgfile); $CFG->params('CDATA_DISKS', $ocrvfdgfile); if ($CFG->params('CDATA_BACKUP_DISK_GROUP') eq '') { $CFG->params('CDATA_BACKUP_DISK_GROUP', 'backupdg'); } my $backupdgfile = $CFG->params('CDATA_BACKUP_DISK_GROUP') . "disk0"; $backupdgfile = catfile($mounts[1], $backupdgfile); $CFG->params('CDATA_BACKUP_DISKS', $backupdgfile); } } my $diskgroup; if ($CFG->defined_param('CDATA_DISK_GROUP')) { $diskgroup = $CFG->params('CDATA_DISK_GROUP'); } if (($CFG->ASM_STORAGE_USED) && $diskgroup) { $diskgroup =~ s/\$/\\\$/g; trace("Save the ASM password file location: +$diskgroup/orapwASM"); $CFG->ASM_PWD_FILE("+$diskgroup/orapwASM"); } # print system env variables printEnvVars(); return $CFG; } # Access Methods Section # This section contains the accessor method used in this class # # Adding a new accessor method: # Unless the accessor method must do something special, use the ## standard access methods: # access_array - get/set a value from/in an array # access_hash - get/set a value from/in an hash # access_scalar - get/set a scalar value # # Examples: # (scalar FOOS) # (method definition) sub FOOS {return access_scalar(@_);} # (value set) $CFG->FOOS('BARS'); (sets $CFG->FOOS to 'BARS') # (value get) my $foos = $CFG->FOOS; # # (array FOOA) # (method definition) sub FOOA {return access_array(@_);} # (value set) $CFG->FOOA(0, 'BARA'); (sets FOOA[0]) # (value set) $CFG->FOOA(\@BARA); (sets FOOA array to @BARA) # (value get) my $fooa0 = $CFG->FOOA(0); (gets FOOA[0]) # (value get) my @fooa = @{$CFG->FOOA}; (gets @FOOA) # # (hash FOOH) # (method definition) sub FOOH {return access_hash(@_);} # (value set) $CFG->FOOH('BARk', 'BARv'); (sets FOOH key BARk to BARv) # (value set) $CFG->FOOH(\%BARH); (sets $CFG->FOOH to %BARH) # (value get) my $barv = $CFG->FOOH('BARk'); (gets FOOH{'BARk'}) # (value get) my %fooh = %{$CFG->FOOH}; (gets %FOOH) # # (counter FOOC) initial value preset to 0 # (method definitions) # sub FOOC {return access_counter(@_);} # sub pp_FOOC {return access_counter(@_);} # sub FOOC_pp {return access_counter(@_);} # (value get - assuming a current value of 3) # (no value change) # my $cval = $CFG->FOOC; (returns 3, current value still 3) # (increment before returning ++FOOC) # my $cval = $CFG->pp_FOOC; (returns 4, current value now 4) # (increment after returning FOOC++) # my $cval = $CFG->FOOC_pp; (returns 3, current value now 4) # Accessor methods for class elements sub addnode { return access_scalar(@_); } sub old_nodevips { return access_array(@_); } sub CLSCFG_EXTRA_PARMS { return access_array(@_); } sub CLSCFG_POST_CMD { return access_array(@_); } sub CRSCFG_POST_CMD { return access_array(@_); } sub DEBUG { return access_scalar(@_); } sub HAS_GROUP { return access_scalar(@_); } sub HAS_USER { return access_scalar(@_); } sub HOST { return access_scalar(@_); } sub SIHA { return access_scalar(@_); } sub OCR_ID { return access_scalar(@_); } sub ORA_CRS_HOME { return access_scalar(@_); } sub SUPERUSER { return access_scalar(@_); } sub VF_DISCOVERY_STRING { return access_scalar(@_); } sub OLR_DIRECTORY { return access_scalar(@_); } sub OLR_LOCATION { return access_scalar(@_); } sub UPGRADE { return access_scalar(@_); } sub DOWNGRADE { return access_scalar(@_); } sub DECONFIG { return access_scalar(@_); } sub NETWORKS { return access_scalar(@_); } sub gpnp_setup_type { return access_scalar(@_); } sub addfile { return access_scalar(@_); } sub hosts { return access_array(@_); } sub osdfile { return access_scalar(@_); } sub paramfile { return access_scalar(@_); } sub parameters_valid { return access_scalar(@_); } sub platform_family { return access_scalar(@_); } sub user_is_superuser { return access_scalar(@_); } sub oldconfig { return access_hash(@_); } sub dwn_voting_file { return access_hash(@_); } sub node2site { return access_hash(@_); } sub site2guid { return access_hash(@_); } sub exadata_site_found { return access_scalar(@_); } sub exadata_sites { return access_scalar(@_); } sub exadata_guid_found { return access_scalar(@_); } sub exadata_guids { return access_scalar(@_); } sub zero_site_guid { return access_scalar(@_); } sub clscfg_guid_arg_z { return access_scalar(@_); } # e.g., "-z site1:7b7b3bef4c1f5ff9ff9643bceb45433a" sub clscfg_site2guid_arg_y { return access_scalar(@_); } # e.g., "-y site1:7b7b3bef4c1f5ff9ff9643bceb45433a, site2:3d9f7asf4g7q0as7gg6124asdf98764b" sub clscfg_node2site_arg_h { return access_scalar(@_); } # e.g., "-h hostA:site1, hostB:site1, hostC:site2, hostD:site2" sub clscfg_clsprop_ocr_arg_p { return access_scalar(@_); } sub clscfg_clsprop_olr_arg_p { return access_scalar(@_); } # e.g. -p property1:value1,property2:value2 sub s_run_as_user2p { return access_scalar(@_); } sub s_run_as_user2_v2p { return access_scalar(@_); } sub s_run_as_userp { return access_scalar(@_); } sub s_run_as_user_v2p { return access_scalar(@_); } sub GIMOVE { return access_scalar(@_); } sub HAPatch { return access_scalar(@_); } sub FORCE { return access_scalar(@_); } sub LASTNODE { return access_scalar(@_); } sub REMOTENODE { return access_scalar(@_); } sub UNLOCK { return access_scalar(@_); } sub LOCK { return access_scalar(@_); } sub isRerun { return access_scalar(@_); } sub wipCkptName { return access_scalar(@_); } sub isNodeCrashed { return access_scalar(@_); } sub restart_css { return access_scalar(@_); } sub stackStartLevel { return access_scalar(@_); } sub SUCC_REBOOT { return access_scalar(@_); } sub DEINSTALL { return access_scalar(@_); } sub KEEPDG { return access_scalar(@_); } sub ASM_PWD_FILE { return access_scalar(@_); } sub configuredCRSHome { return access_scalar(@_); } sub LOCK_ACFS { return access_scalar(@_); } sub PREPATCH { return access_scalar(@_); } sub POSTPATCH { return access_scalar(@_); } sub NONROLLING { return access_scalar(@_); } sub CLEANPIDS { return access_scalar(@_); } sub ROLLBACK { return access_scalar(@_); } sub DESTCRSHOME { return access_scalar(@_); } sub NORESTART { return access_scalar(@_); } sub AUTO { return access_scalar(@_); } sub JOIN { return access_scalar(@_); } sub EXISTINGNODE { return access_scalar(@_); } sub OLD_CSSD_LOG_LEVEL { return access_scalar(@_); } sub ACFS_ADVM_RESOURCES_LIST { return access_scalar(@_); } sub init { return access_scalar(@_); } sub LANG { return access_scalar(@_); } sub s_get_qosctl_path_p { return access_scalar(@_); } sub localNodeRole { return access_scalar(@_); } sub ASM_MODE { return access_scalar(@_); } sub OCR_BACKUP_FILE_NAME { return access_scalar(@_); } sub OCRBACKUPFILE { return access_scalar(@_); } sub FIRST { return access_scalar(@_); } sub OLD_HAIP_ENABLED { return access_scalar(@_); } sub CLEANSOCKETS { return access_scalar(@_); } sub ONLINE { return access_scalar(@_); } sub DECONFIG_WARNING { return access_scalar(@_); } sub DECONFIG_ERROR { return access_scalar(@_); } sub ASMCADOWNGRADE { return access_scalar(@_); } sub olrNotUpgraded { return access_scalar(@_); } sub skipUpgCheck { return access_scalar(@_); } sub nodeAttributeConfig { return access_scalar(@_); } sub nodeAttributeUpgrade { return access_scalar(@_); } sub nodeAttributeDowngrade { return access_scalar(@_); } sub nodeAttributeDeconfigure { return access_scalar(@_); } sub compOLR { return access_scalar(@_); } sub compOCR { return access_scalar(@_); } sub compASM { return access_scalar(@_); } sub compCHM { return access_scalar(@_); } sub compSRVM { return access_scalar(@_); } sub compOHASD { return access_scalar(@_); } sub compACFS { return access_scalar(@_); } sub compIOS { return access_scalar(@_); } sub ACFSSupported { return access_scalar(@_); } sub AFDSupported { return access_scalar(@_); } sub OKASupported { return access_scalar(@_); } sub isLastNodeToPatch { return access_scalar(@_); } sub isOracleBaseShared_cache { return access_scalar(@_); } sub isOracleHomeShared_cache { return access_scalar(@_); } sub isFirstNodeToUpgrade { return access_scalar(@_); } sub isLastNodeToUpgrade { # If the current node is the last node to upgrade, the root script calls # $CFG->isLastNodeToUpgrade(TRUE) in sub isLastNodeToUpgrade() when it # is executed for the first time. So if the second argument (i.e., $_[1]) # passed to isLastNodeToUpgrade() is "TRUE", the root script should update # the node attribute timely. if ((scalar(@_) > 1) && ($_[1] == TRUE)) { trace("Setting the node attribute to LAST_NODE_TO_UPGRADE..."); $CFG->nodeAttributeUpgrade(LAST_NODE_TO_UPGRADE); } return access_scalar(@_); } # Counters # pp_ for increment before, eg pp_foo same as ++foo # _pp for increment after, eg foo_pp same as foo++ # both take an argument for increment amount (default increment is 1) sub srvctl_trc_suff { return access_counter(@_); } sub pp_srvctl_trc_suff { return access_counter(@_); } # ++srvctl_trc_suff sub srvctl_trc_suff_pp { return access_counter(@_); } # srvctl_trc_suff++ sub cluutil_trc_suff_pp { return access_counter(@_); } sub cluutil_trc_suff_pp_reset { return reset_counter(@_); } sub config_value { return $_[0]->{$_[1]}; } # The following methods are required to translate values associated # with the old CRS installation that were passed as scalars into # key/value pairs in the oldconfig hash # These should not be required, but because code was built around them # after an incorrect implementation, it was less destabilizing to # provide translation methods than to correct the code sub oldcrshome { my $class = shift; return oldconfig($class, 'ORA_CRS_HOME', @_); } sub OLD_CRS_HOME { return oldcrshome(@_); } sub oldcrsver { my $class = shift; my $rv; my @ver; if (scalar(@_) == 0) { @ver = @{oldconfig($class, 'ORA_CRS_VERSION')}; $rv = join('.', @ver); } else { @ver =(split('\.', $_[0])); $rv = oldconfig($class, 'ORA_CRS_VERSION', \@ver); } return $rv; } sub params { my $cfg = shift; # If the parameter has not been defined, error. This prevents # typos from going unnoticed if (scalar(@_) == 1 && !defined($cfg->{'params'}->{$_[0]})) { die(dieformat(300, $_[0])); } else { return access_hash($cfg, @_); } } sub defined_param { my $cfg = shift; my $defd = FALSE; if (scalar(@_) > 1) { croak "Only 1 parameter allowed: @_"; } else { $defd = defined($cfg->{'params'}->{$_[0]}); } return $defd; } sub defined_value { my $cfg = shift; my $defd = FALSE; if (scalar(@_) > 1) { croak "Only 1 parameter allowed: @_"; } else { $defd = defined($cfg->{$_[0]}); } return $defd; } sub ASM_STORAGE_USED { my $cfg = shift; my $val; my $ret; if (@_) { # Setting the value, so keep params in sync $ret = shift; if ($ret) { $cfg->params('CRS_STORAGE_OPTION', 1); } else { $cfg->params('CRS_STORAGE_OPTION', 2); } } elsif ($cfg->params('CRS_STORAGE_OPTION') == 1) { $ret = TRUE; } else { $ret = FALSE; } return $ret; } # If tracing is turned on after initialization, make sure directory is # created sub crscfg_trace { my $ret = access_scalar(@_); # if the call was to turn tracing on, make sure trace dir created if ($ret && scalar(@_) > 1) { setup_trace_dir(); } return $ret; } sub crscfg_trace_file { my $ret = access_scalar(@_); # if the call was set the file name, make sure trace dir created if (scalar(@_) > 1) { setup_trace_dir(); } return $ret; } # Low level access methods sub access_scalar { my $class = shift; # find where we were called from so that we know what element # Get callers name my @caller = caller(1); my $name = $caller[3]; # strip class name to get element name my $class_name = ref($class); $name =~ s/$class_name\:\://; my $ret; if (@_ > 1) { croak "Too many args to $name"; } if (@_) {$ret = shift; $class->{$name} = $ret; } else { $ret = $class->{$name}; } return $ret; } sub access_array { my $class = shift; # find where we were called from so that we know what element # Get callers name my @caller = caller(1); my $name = $caller[3]; # strip class name to get element name my $class_name = ref($class); $name =~ s/$class_name\:\://; my $init; my $ret; if (! @_) { $ret = $class->{$name}; } else { $init = shift; if (ref($init) eq 'ARRAY' && !@_) { $class->{$name} = $init; $ret = $class->{$name}; } elsif (@_ > 1) { croak "Too many args to $name"; } elsif (@_) { $class->{$name}->[$init] = $ret = shift; } else { $ret = $class->{$name}->[$init]; } } return $ret; } sub access_hash { my $class = shift; # find where we were called from so that we know what element # Get callers name my @caller = caller(1); my $name = $caller[3]; # strip class name to get element name my $class_name = ref($class); $name =~ s/$class_name\:\://; my $init; my $ret; if (! @_) { $ret = $class->{$name}; } else { $init = shift; if (ref($init) eq 'HASH' && !@_) { $class->{$name} = $init; $ret = $class; } elsif (@_ > 1) { croak "Too many args to $name"; } elsif (@_) { $class->{$name}->{$init} = $ret = shift; } else { $ret = $class->{$name}->{$init}; } } return $ret; } sub access_counter { my $class = shift; # find where we were called from so that we know what element # Get callers name my @caller = caller(1); my $name = $caller[3]; # strip class name to get element name my $class_name = ref($class); $name =~ s/$class_name\:\://; my $elt_name = $name; my $pre = $elt_name =~ /^pp_/; my $post = $elt_name =~ /_pp$/; my $PreVal = $class->{$elt_name}; my $ret = $PreVal; if ((! $pre) && (! $post)) { return $ret; } my $incr = 1; if (@_ > 1) { croak "Too many args to $name: @_"; } if (@_) { $incr = shift; } $class->{$elt_name} = $PreVal + $incr; if ($pre) { $ret = $class->{$elt_name}; } return $ret; } sub reset_counter { my $class = shift; # find where we were called from so that we know what element # Get callers name my @caller = caller(1); my $counter_name = $caller[3]; # strip class name to get element name my $class_name = ref($class); $counter_name =~ s/$class_name\:\://; $counter_name =~ s/_reset//; trace("Resetting $counter_name to 0"); $class->{$counter_name} = 0; } ###### End Access Methods Section ####### # Set up the trace directory sub setup_trace_dir { if ($CFG && $CFG->crscfg_trace && $CFG->crscfg_trace_file && ! -e $CFG->crscfg_trace_file) { my $trace_dir = dirname($CFG->crscfg_trace_file); if (! -e $trace_dir) { my $tracing = $CFG->crscfg_trace; # temporarily turn off tracing to avoid recursing in create_dir $CFG->crscfg_trace(0); create_dir($trace_dir); s_set_ownergroup ($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $trace_dir); s_set_perms ("0775", $trace_dir); $CFG->crscfg_trace($tracing); } } } ###--------------------------------------------------------- #### Function for tracing logging messages for root scripts # ARGS : 0 sub trace { my ($sec, $min, $hour, $day, $month, $year) = (tz_localtime()) [0, 1, 2, 3, 4, 5]; $month = $month + 1; $year = $year + 1900; if ($CFG && $CFG->crscfg_trace) { my $CRSCFG_TRACE_FILE = $CFG->crscfg_trace_file; if ($CRSCFG_TRACE_FILE) { open (TRCFILE, ">>$CRSCFG_TRACE_FILE") or die("Cannot open $CRSCFG_TRACE_FILE for appending : $!\n"); } printf TRCFILE "%04d-%02d-%02d %02d:%02d:%02d: @_\n", $year, $month, $day, $hour, $min, $sec; close (TRCFILE); } else { printf "%04d-%02d-%02d %02d:%02d:%02d: @_\n", $year, $month, $day, $hour, $min, $sec; } } ####--------------------------------------------------------- #### Function for dumping errors on STDOUT # ARGS : 0 sub error { print "@_\n"; if ($CFG && $CFG->crscfg_trace && $CFG->crscfg_trace_file) { trace (@_); } if ($CFG && $CFG->DEBUG) { trace("###### Begin Error Stack Trace ######"); backtrace(); trace("####### End Error Stack Trace #######\n"); } } ####--------------------------------------------------------- #### Function for getting this host name in lower case with no domain name # ARGS : 0 sub tolower_host { my $host = hostname () or return ""; # If the hostname is an IP address, let hostname remain as IP address # Else, strip off domain name in case /bin/hostname returns FQDN # hostname my $shorthost; if ($host =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) { $shorthost = $host; } else { ($shorthost,) = split (/\./, $host); } # convert to lower case $shorthost =~ tr/A-Z/a-z/; die(dieformat(308)) if ($shorthost eq ""); return $shorthost; } # Thin wrapper of OSD function to run a command as a specified user # Parameters: # 1. user to run command as # remaining arguments are the command to run sub run_as_user { my $user = shift; trace("Running as user $user: @_"); return $CFG->s_run_as_usere($user, @_); } # Execute a command as a user (do not invoke directly, use run_as_user) sub s_run_as_usere { my $cfg = shift; my $user = shift; my $pgm = $cfg->s_run_as_userp; my @args = ("@_", $user); if ($cfg->s_run_as_user_v2p) { $pgm = $cfg->s_run_as_user_v2p; @args = ($user, @_); } return &$pgm(@args); } # Thin wrapper of OSD function to run a command as a specified user # Parameters: # 1. user to run command as # 2. array reference for output capture # remaining arguments are the command to run sub run_as_user2 { my $user = shift; my $aref = shift; trace("Running as user $user: @_"); return $CFG->s_run_as_user2e($user, $aref, @_); } # Execute a command as a user, returning the output # (do not invoke directly, use run_as_user2) sub s_run_as_user2e { my $cfg = shift; my $user = shift; my $aref = shift; my $rc; my @args = (\@_, $user, $aref); if (!$cfg->s_run_as_user2_v2p) { my $pgm = $cfg->s_run_as_user2p; $rc = &$pgm(\@_, $user, $aref); } else { my $pgm = $cfg->s_run_as_user2_v2p; my @out = &$pgm($user, @_); $rc = shift @out; @{$aref} = @out; } return $rc; } sub trim ################################################################################ # Function: Remove leading and trailing blanks. # # Arg : string # # Return : trimmed string ################################################################################ { my $str = shift; $str =~ s/^\s+//; $str =~ s/\s+$//; return $str ; } ####--------------------------------------------------------- #### Function for checking and returning Super User name # ARGS : 0 sub check_SuperUser { my $superuser = s_check_SuperUser () or trace("Not running as authorized user"); return $superuser; } sub is_dev_env { my $isDevEnv = uc($ENV{'_SCLS_DEVELOPMENT'}); if ($isDevEnv eq "TRUE") { return TRUE; } else { return FALSE; } } sub is_Exadata { my $isExadata = s_is_Exadata(); return $isExadata; } sub backtrace { my $levels = $_[0]; my $done = FALSE; trace(sprintf(" %-15s %-20s %-4s %-10s", "Package", "File", "Line", "Calling")); trace(sprintf(" %-15s %-20s %-4s %-10s", "-" x 15, "-" x 20, "-" x 4, "-" x 10)); for (my $bt = 1; ((!$levels && !$done) || $bt <= $levels); $bt++) { my @caller = caller($bt); if (scalar(@caller) == 0) { $done = TRUE; } else { my $pkg = $caller[0]; my $file = basename($caller[1]); my $line = $caller[2]; my $sub = $caller[3]; trace(sprintf("%2d: %-15s %-20s %4d %s", $bt, $pkg, $file, $line, $sub)); } } } sub dietrap { trace("###### Begin DIE Stack Trace ######"); backtrace(0); trace("####### End DIE Stack Trace #######\n"); #wipCkptName is the global variable to store active checkpoint in progress. #Needed to handle ctrl-c cases. if ($CFG) { trace ($CFG->wipCkptName . " checkpoint has failed"); if ($CFG->wipCkptName !~ m/^null/i && $CFG->wipCkptName ne '') { if (isCkptexist($CFG->wipCkptName)) { my $ckptStatus = getCkptStatus($CFG->wipCkptName); trace($CFG->wipCkptName . " state is $ckptStatus"); if (CKPTSUC ne $ckptStatus) { writeCkpt($CFG->wipCkptName, CKPTFAIL); } } writeCkpt("ROOTCRS_STACK", CKPTFAIL); } } die @_; } sub dieformat { print_error(@_); return "Died"; } # Execute a system command and analyze the return codes sub system_cmd { my $rc = 0; trace("Executing @_"); my @output = system_cmd_capture("@_"); $rc = shift @output; print_lines(@output); return $rc; } =head2 system_cmd_capture1 Capture the output from a system command and analyze the return codes =head3 Parameters trace flag to control error output and Command to be executed =head3 Returns Array containing both the return code and the captured output The command output is chomped =head3 Usage To capture the data of command foo: my @out = system_cmd_capture1('foo') my $rc = shift @out; The @out now contains only the output of the command 'foo' =cut sub system_cmd_capture1 { my $trcflag = shift; trace("Executing cmd: @_"); if ($trcflag) { return system_cmd_capture(@_); } else { return system_cmd_capture_noprint(@_); } } #Capture the output from a system command sub system_cmd_capture { #validate the input command whether it is single line or multi-line if (scalar(grep(/\n/, @_)) > 0) { trace("Failure in execution, multi-line command @_ is not supported"); die(dieformat(48)); } trace("Executing cmd: @_"); my @output = system_cmd_capture_noprint(@_); my $rc = shift @output; if (scalar(@output) > 0) { trace(join("\n> ", ("Command output:", @output)), "\n>End Command output"); } return ($rc, @output) } =head2 system_cmd_capture_noprint Capture the output from a system command and analyze the return codes =head3 Parameters Command to be executed =head3 Returns Array containing both the return code and the captured output The command output is chomped =head3 Usage To capture the data of command foo: my @out = system_cmd_capture('foo') my $rc = shift @out; The @out now contains only the output of the command 'foo' =cut sub system_cmd_capture_noprint { my $rc = 0; my $prc = 0; my @output; if (!open(CMD, "@_ 2>&1 |")) { $rc = -1; } else { @output = (); close CMD; # the code return must be after the close $prc = $CHILD_ERROR >> 8; # get program return code right away chomp(@output); } if ($prc != 0) { # program returned error code # error("Command return code of $prc from command: @_"); $rc = $prc; } elsif ($rc < 0 || ($rc = $CHILD_ERROR) < 0) { my $mycmd = join(' ', @_); # DO NOT call print_error here which calls system_cmd_capture_noprint # to run 'clsecho' # It is almost certainly wrong to keep this output from going # directly to the trace file, which will occur if not careful, # because it will be difficult to determine the cause of serious # problems, but this is by request push @output, "Failure in execution (rc=$rc, $CHILD_ERROR, $!) for command @_"; } elsif ($rc & 127) { # program returned error code my $sig = $rc & 127; my $mycmd = join(' ', @_); # It is almost certainly wrong to keep this output from going # directly to the trace file, which will occur if not careful, # because it will be difficult to determine the cause of serious # problems, but this is by request push @output, "Failure with signal $sig from command: @_"; } elsif ($rc) { trace("Failure with return code $rc from command @_"); } return ($rc, @output); } ####--------------------------------------------------------- #### Function for dumping NLS info or warning messages on STDOUT # # ARGS : 1..n # ARG1 : NLS message ID # ARG2..n : Argments put in NLS message # Returns : SUCCESS or FAILED # Example : print_info(1000, "$var", "str"); sub print_info { my $debug; if ($CFG->DEBUG) { $debug = $CFG->DEBUG; } $CFG->DEBUG(0); print_error(@_); if ($debug) { $CFG->DEBUG($debug); } } sub print_error ####--------------------------------------------------------- #### Function for dumping NLS error messages on STDOUT # # ARGS : 1..n # ARG1 : NLS message ID # ARG2..n : Argments put in NLS message # Returns : SUCCESS or FAILED # Example : print_error(1000, "$orahome", "ocrlocation"); { my $errCode = $_[0]; my @msgArgs = @_[1..$#_]; my $rc = -1; my @out = (); my @cmd = (); my $ret = SUCCESS; my $nlsArgs; my ($sec, $min, $hour, $day, $month, $year) = (tz_localtime()) [0, 1, 2, 3, 4, 5]; $month = $month + 1; $year = $year + 1900; my $platform = s_get_platform_family(); my $CLSECHO; if ($platform eq 'windows') { $CLSECHO = crs_exec_path("clsecho.exe"); } else { $CLSECHO = crs_exec_path("clsecho"); } if (! (-x $CLSECHO)) { trace("The CRS executable file 'clsecho' does not exist."); $nlsArgs = join('" "', @msgArgs); print("The 'clsecho' executable is unavailable at this point to print a " . "translated message CLSRSC-$errCode. Arguments to 'clsecho': " . "$nlsArgs\n"); return FAILED; } $ENV{'NLS_LANG'} = $CFG->params('LANGUAGE_ID'); if (scalar(@msgArgs) > 0) { $nlsArgs = join('" "', @msgArgs); @cmd = ($CLSECHO, "-p has", "-f clsrsc", "-m $errCode", "\"$nlsArgs\""); } else { @cmd = ($CLSECHO, "-p has", "-f clsrsc", "-m $errCode"); } @out = system_cmd_capture_noprint(@cmd); $rc = shift @out; if (0 == $rc) { for (my $idx = 0; $idx < scalar(@out); $idx++) { my $str = $out[$idx]; $out[$idx] = "$str\n"; } # Print out the error message to the console printf "$year/%02d/%02d %02d:%02d:%02d @out", $month, $day, $hour, $min, $sec; } else { trace("Failed to execute the command \"@cmd\" (rc=$rc), ". "with the message:\n".join("\n", @out)."\n"); $ret = FAILED; } # Dump errors to the log file if ($CFG && $CFG->crscfg_trace && $CFG->crscfg_trace_file) { undef $NLS_LANG; if (scalar(@msgArgs) > 0) { @cmd = ($CLSECHO, "-p has", "-f clsrsc", "-m $errCode", "\"$nlsArgs\""); } else { @cmd = ($CLSECHO, "-p has", "-f clsrsc", "-m $errCode"); } @out = system_cmd_capture(@cmd); $rc = shift @out; if (0 == $rc) { if(!$CFG->AUTO && !$CFG->params('SILENT')) { print color 'reset'; # When printing to a file, the 'print color' prints an unprintable # character that will show up as the first character of the next # line, making it less readable. Add newline to make next line # more readable print "\n"; } trace(@out); } else { trace("Failed to execute the command \"@cmd\" (rc=$rc), ". "with the message:\n".join("\n", @out)."\n"); } $ENV{'NLS_LANG'} = $CFG->params('LANGUAGE_ID'); } if ($CFG->DEBUG) { trace("###### Begin Error Stack Trace ######"); backtrace(); trace("####### End Error Stack Trace #######\n"); } return $ret; } sub print_config { my $cfg = shift; my @cfgfiles = ($cfg->paramfile); if ($cfg->osdfile && -e $cfg->osdfile) { push @cfgfiles, $cfg->osdfile; } if ($cfg->addfile && -e $cfg->addfile) { push @cfgfiles, $cfg->addfile; } trace ("### Printing the configuration values from files:"); for my $file (@cfgfiles) { trace(" $file"); } # validates if any value is assigned to the script variables for my $key (sort(keys %{$cfg->params})) { my $val = $cfg->params($key); trace("$key=$val"); } trace ("### Printing other configuration values ###"); my %cfgh = %{($cfg)}; for my $key (sort(keys %cfgh)) { my $ref = ref($cfg->$key); my $val = $cfgh{$key}; if (!$ref) { trace("$key=$val"); } # scalar elsif ($ref eq "ARRAY") { trace("$key=" . join(' ', @{($val)})); } elsif ($ref eq "HASH" && $key ne "params" && scalar(keys(%{($val)}))) { trace("Printing values from hash $key"); my %subh = %{($val)}; for my $hkey (sort(keys(%subh))) { trace(" $key key $hkey=$subh{$hkey}"); } } } trace ("### Printing of configuration values complete ###"); return; } # Define our version of system to ensure proper diagnostics are obtained # always sub system { return system_cmd(@_); } sub read_file { my $file = $_[0]; open (FILE, "<$file") or die(dieformat(185, $file, $!)); my @contents = (); close (FILE); return @contents; } sub run_crs_cmd { my $exec = shift; my @cmd = (crs_exec_path($exec), @_); return system_cmd(@cmd); } sub crs_exec_path { my ($cfg, $name, $execpath, $home); $cfg = $name = shift; if (blessed($cfg)) { # called as a method $name = shift; } else { $cfg = $CFG; } $home = shift; if ((defined $CFG) && ($CFG->ORA_CRS_HOME)) { if($home ne "") { $execpath = catfile($home, 'bin', $name); } else { $execpath = catfile($cfg->ORA_CRS_HOME, 'bin', $name); } if ($CFG->platform_family eq 'windows') { if (!(-x $execpath) && !(-x "${execpath}.exe") && !(-x "${execpath}.bat")) { trace (" The CRS executable file $execpath either does not exist or is not executable"); } } else { if (!(-x $execpath)) { trace (" The CRS executable file $execpath either does not exist or is not executable"); } } } return $execpath; } # ARGS # ARG 0: paramfile sub setup_param_vars { my $paramfile = $_[0]; my $platform = s_get_platform_family(); # To support the use of 'strict', it is necessary to create a small # program to be executed via 'eval' # Because 'strict' requires all variables to be declared, the scope # of 'my' variables is the program, and variables that are defined # by the parameter file are used in the definition of subsequent # parameter file entries, e.g. DIRPREFIX, to get the scoping right # it is necessary to create a program that will allow previously # defined values my @epgm; open(PARAMF, $paramfile) or die(dieformat(185, $paramfile, $!)); while () { if ($_ !~ /^#|^\s*$/) { # The magic below takes params of the form KEY=VAL and sets them as # variables in the perl context chomp; $_ = trim ($_); my ($key, $val) = split ('='); # store this in a hash that is returned if ((0 > index($val,'"')) && $key ne 'CDATA_DISK_GROUP') { # escape \ (for NT) $val =~ s!\\!\\\\!g; push @epgm, "my \$$key=\"$val\";"; } else { # won't allow perl var subst # escape "'" $val =~ s!\'!\\'!g; push @epgm, "my \$$key='$val';"; } push @epgm, '$CFG->params(', "'$key',\$$key);"; } } close (PARAMF); eval("@epgm"); # if there was an error log it if ($@) { trace($@); } return; } sub validateVarList ####--------------------------------------------------------- #### Function for validating installer variables list # This validates parameters by checking to see that all have had proper # substituion. # # ARGS: 1 # ARG1: The list of required parameters that should be verified. # If no argument is specified, this funtion validates all variables # in parameter file { my (@required) = @_; my %params = %{($CFG->params)}; my $all_set = SUCCESS; my @keys = (); if (@required) { @keys = @required; } else { @keys = keys(%params); } trace("Checking parameters from paramfile " . $CFG->paramfile . " to validate installer variables"); # validates if any value is assigned to the script variables for my $key (sort(@keys)) { # TODO: remove the following once the installer properly sets # these variables if (($key eq "OPC_CLUSTER_TYPE") || ($key eq "OPC_NAT_ADDRESS") || ($key eq "ODA_CONFIG")) { trace("Skipping validation for $key"); next; } my $val = $CFG->params($key); { if(((@required) && (!$val)) || ($val =~ /^%/)) { # The executable 'clsecho' is not yet available at this point # hence the print_error(...) doesn't work error("No value set for the parameter $key. ", "Use parameter file ", $CFG->paramfile, " to set values"); $all_set = FAILED; } } } return $all_set; } sub isAddNode #------------------------------------------------------------------------------- # Function: Check if hostname is a new node. # Args : [0] Name of host to check # Returns : TRUE if new node # FALSE if not new node #------------------------------------------------------------------------------- { my $hostname = $_[0]; if ($CFG->defined_param('CRS_ADDNODE') && $CFG->params('CRS_ADDNODE') eq "true") { trace("CRS_ADDNODE is defined to 'true'"); my $addNodeList = $CFG->params('CLUSTER_NEW_HOST_NAMES'); if ($addNodeList =~ /\b$hostname\b/i) { trace("'$hostname' is part of add node list: $addNodeList"); trace("It is an add node scenario"); return TRUE; } } return FALSE; } sub isCkptPropertyExists #------------------------------------------------------------------------------- # Function: Check if a property exists for a check point # ARG1 : Check point Name # ARG2 : property name # ARG3 : "-global" or undef # pass "-global" to check for the global checkpoint # Returns : boolean #------------------------------------------------------------------------------- { my $ckptName = $_[0]; my $ckptPropName = $_[1]; my $ckptType = $_[2]; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $run_as_user = TRUE; my @program; if (! defined($ckptType)) { @program = ('-ckpt', '-oraclebase', $ORACLE_BASE, '-chkckpt', '-name', $ckptName, '-pname', $ckptPropName); } elsif ($ckptType eq "-global") { @program = ('-ckpt', '-global', '-oraclebase', $ORACLE_BASE, '-chkckpt', '-name', $ckptName, '-pname', $ckptPropName); } my $args_str = join(' ', @program); my ($rc, @capout) = cluutil($run_as_user, $args_str); if (scalar(grep(/TRUE/, @capout)) > 0) { trace("The property $ckptPropName for checkpoint:$ckptName is there"); return SUCCESS; } if ((0 != $rc) || (scalar(grep(/FALSE/, @capout))) > 0) { trace("The property $ckptPropName for checkpoint $ckptName does not exist"); return FAILED; } } sub getCkptPropertyValue #------------------------------------------------------------------------------- # Function: Get the value for a check point property # ARG1 : Check point Name # ARG2 : property name # ARG3 : "-global" or undef # pass "-global" to check for the global checkpoint # Returns : Failed or property value #------------------------------------------------------------------------------- { my $ckptName = $_[0]; my $ckptPropName = $_[1]; my $ckptType = $_[2]; my ($pname, $pval); my @out = (); my $run_as_user = TRUE; my @program; if (! defined($ckptType)) { @program = ('-ckpt', '-oraclebase', $CFG->params('ORACLE_BASE'), '-listckpt', '-name', $ckptName, '-pname', $ckptPropName); } elsif ($ckptType eq "-global") { @program = ('-ckpt', '-global', '-oraclebase', $CFG->params('ORACLE_BASE'), '-listckpt', '-name', $ckptName, '-pname', $ckptPropName); } my $args_str = join(' ', @program); my ($rc, @capout) = cluutil($run_as_user, $args_str); if (0 != $rc) { trace("Failed to get property value for property:$ckptPropName " . "for checkpoint:$ckptName. Error code is $rc"); print_error(178, $ckptPropName, $ckptName, $rc); return FAILED; } else { trace("Succeeded to get property value:@capout"); @out = grep(/=/, @capout); ($pname, $pval) = split(/=/,$out[0]); } return trim($pval); } sub isOwnerGroupValid #------------------------------------------------------------------------------- # Function: Validate owner and group # Args : none # Returns : TRUE - if owner and group is valid # FALSE- if owner and group is NOT valid #------------------------------------------------------------------------------- { # validate owner my $owner = $CFG->params('ORACLE_OWNER'); if (($CFG->FORCE) && ($owner =~ "%")) { return FALSE; } # validate group my $group = $CFG->params('ORA_DBA_GROUP'); if (($CFG->FORCE) && ($group =~ "%")) { return FALSE; } return TRUE; } sub getIndexforLogRotation { my $module = $_[0]; my ($counter, $reset_counter, $log_idx); if ($module eq 'cluutil') { $counter = \&cluutil_trc_suff_pp; $reset_counter = \&cluutil_trc_suff_pp_reset; } else { trace("No module specified, default the index to 0"); return 0; } $log_idx = &$counter($CFG); if ($log_idx > MAX_NUM_OF_LOG_FILES) { &$reset_counter($CFG); $log_idx = &$counter($CFG); } return $log_idx; } sub srvctl_tty #------------------------------------------------------------------------------- # Function: Run SRVCTL command with the given arguments in some cases where the # SRVCTL command is invoked remotely via SSH and the arguments include # a name that needs to be resolved - See bug 14768261 for details. # Args : [0] - TRUE if run as ORACLE_OWNER # - FALSE if run as root # [1] - srvctl arguments # [2] - CRS home to use. Defaults to $CFG->ORA_CRS_HOME. # Returns : TRUE if successful # FALSE if failed #------------------------------------------------------------------------------- { my $run_as_owner = $_[0]; my $srvctl_args = $_[1]; my $home = $_[2]; my ($srvctlbin, $status); if ($home ne "") { $srvctlbin = crs_exec_path('srvctl', $home); } else { $srvctlbin = crs_exec_path('srvctl'); } # set trace file my $srvctl_trc_file = catfile (logsDirectory(), "srvmcfg" . $CFG->srvctl_trc_suff_pp . ".log"); $ENV{SRVM_TRACE} = "TRUE"; $ENV{SRVM_NATIVE_TRACE} = "TRUE"; $ENV{SRVM_JNI_TRACE} = "TRUE"; $ENV{SRVCTL_TRACEFILE} = $srvctl_trc_file; my $cmd = "echo \'${srvctlbin} $srvctl_args\' | bash /dev/stdin"; trace ("Invoking \"${cmd}\""); trace ("trace file=$srvctl_trc_file"); if ($run_as_owner) { $status = run_as_user ($CFG->params('ORACLE_OWNER'), ${cmd}); } else { $status = system_cmd(${cmd}); } # set owner & permission of trace file s_set_ownergroup ($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $srvctl_trc_file); s_set_perms ("0664", $srvctl_trc_file); #Bug 9718507 . srvctl return code 2 is now only for warnings that can #be ignored if ((0 == $status) || (2 == $status)) { return TRUE; } else { trace (" \"${cmd}\" failed with status ${status}."); return FALSE; } } sub srvctl #------------------------------------------------------------------------------- # Function: Run srvctl with the given arguments. # Args : [0] - TRUE if run as ORACLE_OWNER # - FALSE if run as root # [1] - srvctl arguments # [2] - CRS home to use. Defaults to $CFG->ORA_CRS_HOME. # Returns : TRUE if successful # FALSE if failed #------------------------------------------------------------------------------- { my $run_as_owner = $_[0]; my $srvctl_args = $_[1]; my $home = $_[2]; my ($result, @output); $result = srvctl_capture($run_as_owner, \@output, $srvctl_args, $home); if ((0 == $result) || (2 == $result)) { return TRUE; } else { print_lines(@output); my ($cmd, $srvctlbin); trace (" \"${srvctl_args}\" failed with status ${result}."); # Pass Failed srvctl command($cmd) to 180 meesage if ($home ne "") { $srvctlbin = crs_exec_path('srvctl', $home); } else { $srvctlbin = crs_exec_path('srvctl'); } $cmd = "${srvctlbin} $srvctl_args"; print_error(180, $cmd); return FALSE; } } sub srvctl_capture #------------------------------------------------------------------------------- # Function: Run srvctl with the given arguments. # Args : [0] - TRUE if run as ORACLE_OWNER # - FALSE if run as root # [1] - Reference of the array to place the output # if is not present the output will be printed # [2] - srvctl arguments # [3] - CRS home to use. Defaults to $CFG->ORA_CRS_HOME. # Returns : Zero if successful # Non-zero if failed #------------------------------------------------------------------------------- { my $run_as_owner = $_[0]; my $output_ref = $_[1]; my $srvctl_args = $_[2]; my $home = $_[3]; my ($srvctlbin, $status); if ($home ne "") { $srvctlbin = crs_exec_path('srvctl', $home); } else { $srvctlbin = crs_exec_path('srvctl'); } # set trace file my $srvctl_trc_file = catfile (logsDirectory(), "srvmcfg" . $CFG->srvctl_trc_suff_pp . ".log"); my $srvctl_trc_lck_file = "$srvctl_trc_file".".lck"; if (-e $srvctl_trc_lck_file) { # srvctl command was previously stopped before cleanning up the # trace lock file s_remove_file($srvctl_trc_lck_file); } if (($CFG->platform_family eq 'windows') && ($srvctl_trc_file =~ /\s+/)) { # For Windows, double quotes would be needed if the tracefile has one or # more spaces in its path. $srvctl_trc_file = "\"$srvctl_trc_file\""; } $ENV{SRVM_TRACE} = "TRUE"; $ENV{SRVCTL_TRACEFILE} = $srvctl_trc_file; $ENV{SRVM_JNI_TRACE} = "TRUE"; if ($CFG->UPGRADE && ($home eq $CFG->OLD_CRS_HOME) && isOldVersionLT122()) { trace("Disable the SRVM_NATIVE_TRACE for srvctl command on pre-12.2."); undef $SRVM_NATIVE_TRACE; } else { $ENV{SRVM_NATIVE_TRACE} = "TRUE"; } my $cmd = "${srvctlbin} $srvctl_args"; chomp($cmd); trace ("Invoking \"${cmd}\""); trace ("trace file=$srvctl_trc_file"); if ($run_as_owner) { if (-e $srvctl_trc_file) { s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $srvctl_trc_file); s_set_perms("0664", $srvctl_trc_file); } $status = run_as_user2 ($CFG->params('ORACLE_OWNER'), $output_ref, ${cmd}); # system_cmd_capture already prints the output to the trace file # but run_as_user2 doesn't so we print to the trace file here trace(@$output_ref); } else { ($status, @$output_ref) = system_cmd_capture(${cmd}); } # set owner & permission of trace file s_set_ownergroup ($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $srvctl_trc_file); s_set_perms ("0664", $srvctl_trc_file); undef $SRVM_JNI_TRACE; undef $SRVM_NATIVE_TRACE; undef $SRVM_TRACE; return $status; } =head1 EXPORTED FUNCTIONS =head2 setSrvctlTrace Enable trace and set the trace file for SRVCTL command that is to run as a given user =head3 Parameters [0] User to run SRVCTL command =head3 Returns An array consisting of two elements, which saves trace on/off switch and trace file name. The sub resetSrvctlTrace, which restores the trace settings, takes this as the input argument. =cut sub setSrvctlTrace { my $usrname = $_[0]; my $tempTrcEnv = $ENV{SRVM_TRACE}; my $tempTrcfileEnv = $ENV{SRVCTL_TRACEFILE}; my $userLogDir = catdir(logsDirectory(), $usrname); my $userLog = catfile($userLogDir, "srvmcfg" . $CFG->srvctl_trc_suff_pp . ".log"); if (-e $userLog) { # set ownership & permissions of trace file s_set_ownergroup ($usrname, $CFG->params('ORA_DBA_GROUP'), $userLog); s_set_perms("0664", $userLog); } $ENV{SRVM_TRACE} = "TRUE"; $ENV{SRVCTL_TRACEFILE} = $userLog; return ($tempTrcEnv, $tempTrcfileEnv); } =head1 EXPORTED FUNCTIONS =head2 resetSrvctlTrace Restore the trace settings for SRVCTL command =head3 Parameters [0] User to run SRVCTL command [1] An array that records trace on/off switch and trace file name, which is the return value of sub setSrvctlTrace. =head3 Returns None =cut sub resetSrvctlTrace { my $usrname = $_[0]; my $trcEnv = $_[1]; my $userLogDir = catdir(logsDirectory(), $usrname); my $userLog = catfile($userLogDir, "srvmcfg" . $CFG->srvctl_trc_suff_pp . ".log"); if (-e $userLog) { # set ownership & permissions of trace file s_set_ownergroup ($usrname, $CFG->params('ORA_DBA_GROUP'), $userLog); s_set_perms("0664", $userLog); } my @srvmTrc = @{$trcEnv}; $ENV{SRVM_TRACE} = $srvmTrc[0]; $ENV{SRVCTL_TRACEFILE} = $srvmTrc[1]; } #------------------------------------------------------------------------------- # Function: Run cluutil with the given arguments. # Args : [0] - TRUE if run as ORACLE_OWNER # - FALSE if run as root # [1] - cluutil arguments # [2] - CRS home to use. Defaults to $CFG->ORA_CRS_HOME. # Returns : $rc : return code # $capout : command output #------------------------------------------------------------------------------- sub cluutil { my ($run_as_owner, $cluutil_args, $crshome) = @_; my $cluutil_bin = ($crshome) ? crs_exec_path('cluutil', $crshome) : crs_exec_path('cluutil'); # Set trace file my $trc_loc = logsDirectory(); my $log_idx = getIndexforLogRotation("cluutil"); my $cluutil_trc_file = catfile($trc_loc, "cluutil" . $log_idx . ".log"); my $cluutil_trc_lck_file = "$cluutil_trc_file".".lck"; if (-e $cluutil_trc_lck_file) { # cluutil command was previously stopped before cleanning up the # trace lock file s_remove_file($cluutil_trc_lck_file); } if (($CFG->platform_family eq 'windows') && ($cluutil_trc_file =~ /\s+/)) { # For Windows, double quotes would be needed if the tracefile has one or # more spaces in its path. $cluutil_trc_file = "\"$cluutil_trc_file\""; } $ENV{'SRVM_TRACE'} = "TRUE"; $ENV{'SRVM_NATIVE_TRACE'} = "TRUE"; $ENV{'SRVM_JNI_TRACE'} = "TRUE"; $ENV{ORA_CLUUTIL_TRACE_FILE} = $cluutil_trc_file; my $cmd = "${cluutil_bin} ${cluutil_args}"; trace("Invoking \"${cmd}\""); trace ("trace file=$cluutil_trc_file"); my ($rc, @capout); if ($run_as_owner) { if (-e $cluutil_trc_file) { s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $cluutil_trc_file); s_set_perms("0644", $cluutil_trc_file); } $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@capout, $cmd); } else { @capout = system_cmd_capture($cmd); $rc = shift @capout; } if (-e $cluutil_trc_file) { s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $cluutil_trc_file); s_set_perms("0644", $cluutil_trc_file); } undef $SRVM_JNI_TRACE; undef $SRVM_NATIVE_TRACE; undef $SRVM_TRACE; return ($rc, @capout); } # Module for script instantiation # # Instantiate all files in $CH/crs/sbs/ directory -- replace %FOO% # with value for FOO (obtained from crsconfig_params) -- and place # this in $CH/crs/utl/ directory # # ARGS # none sub instantiate_scripts { my $home = $_[0]; my $myHome = ($home) ? $home : ($CFG->ORA_CRS_HOME); my $hostname = $CFG->HOST; trace("Instantiating scripts in GI home: $myHome"); my $sbsdir = catfile ($myHome, "crs", "sbs"); my @sbsfiles = glob (catfile ($sbsdir, "*.sbs")); my $wrapdir_crs = catfile ($myHome, "crs", "utl"); my $wrapdir_nodespec = catfile($myHome, "crs", "utl", $hostname); # create $wrapdir_crs if it doesn't exist already create_dir ($wrapdir_crs); create_dir($wrapdir_nodespec); foreach my $srcfile (@sbsfiles) { my @sbsfile = read_file ($srcfile); my $dstpath; # strip off .sbs suffix (my $dstfile = basename ($srcfile)) =~ s/\.sbs//g; if (($dstfile eq "crsconfig_dirs") || ($dstfile eq "crsconfig_fileperms")) { $dstpath = catfile($wrapdir_nodespec, $dstfile); trace("SRC FILE: $srcfile; DST PATH: $dstpath"); } else { $dstpath = catfile ($wrapdir_crs, $dstfile); } if ($CFG->DEBUG) { trace ("SRC FILE: $srcfile; DST PATH: $dstpath"); } open (DSTPATH, ">${dstpath}") or die(dieformat(207, $dstpath, $!)); foreach my $line (@sbsfile) { # skip blanks and comments if ($line !~ /^#|^\s*$/) { instantiate_config_params ($line); } print DSTPATH "$line"; } close (DSTPATH); s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $dstpath) or die(dieformat(152, $dstpath)); } trace("Resetting ACFS wrapper scripts"); ($CFG->compACFS)->reset_ACFS_wrapper_scripts(); } #### Function for creating a directory # ARGS: # arg 0 -- directory path to be created # # Returns: list of directories -- including intermediate directories -- created sub create_dir { my $dir_path = $_[0]; # convert '\' to '/' (for NT) $dir_path =~ s!\\!/!g; # If dir_path doesn't already exist, create it. # # If dir_path exists as a symlink, then if the target of the symlink # doesn't exist, create the target path. This is applicable especially # to ADE environments where we might already have a symlink pointing to # some directory in the has_work/ tree. my $link_path; if ($link_path = s_isLink ($dir_path)) { if ($CFG->DEBUG) { trace (" $dir_path is a SYMLINK to $link_path; changing cwd to " . dirname ($dir_path) . " and resetting DIR_PATH"); } # In ADE env, the target could contain a 'virtual' directory # marked by the two dots chdir(dirname($dir_path)); $dir_path = $link_path; } my @dirs; unless (-e $dir_path) { if ($CFG->DEBUG) { trace (" mkpath ($dir_path)");} @dirs = mkpath($dir_path, {error => \my $err}); if ($err && @$err) { my ($dir, $message); for my $diag (@$err) { ($dir, $message) = %$diag; if ($dir eq '') { trace("general error: $message"); } else { trace("problem creating $dir: $message"); } } die(dieformat(254, $dir_path, $message)); } } return @dirs; } sub instantiate_config_params { # If it contains a pattern of the form '%foo%' AND a mapping exists # for 'foo', replace '%foo%' with the corresponding value. my $rexp="[a-zA-Z_]+"; foreach (@_) { my @matchlist = $_ =~ /%(${rexp})%/g; foreach my $match (@matchlist) { if (defined($CFG->config_value($match))) { my $sub = $CFG->config_value($match); $_ =~ s/%(${match})%/$sub/g; } elsif ($CFG->defined_param($match)) { my $sub = $CFG->params($match); $_ =~ s/%(${match})%/$sub/g; } } # We strict the $* instantiation only for the dev env. next if (! is_dev_env()); @matchlist = $_ =~ /\$(${rexp})/g; foreach my $match (@matchlist) { if ($CFG->config_value($match)) { my $sub = $CFG->config_value($match); $_ =~ s/\$(${match})/$sub/g; } elsif ($CFG->defined_param($match)) { my $sub = $CFG->params($match); $_ =~ s/\$(${match})/$sub/g; } } } } sub setup_env { my $disableADR = $_[0]; # Before Directories Creation module is invoked, we need to add entries # for RCALLDIR locations to crsconfig_dirs # Note: this is done only on platforms where RCALLDIR is defined. if ($CFG->defined_param('RCALLDIR')) { add_RCALLDIR_to_dirs (); } # add ITDI _to crsconfig_dirs add_ITDIR_to_dirs(); # create dirs & set permissions my @crsconfig_dirs = read_file(catfile($CFG->ORA_CRS_HOME, 'crs', 'utl', $CFG->HOST, 'crsconfig_dirs')); # Set CRS_ADR_DISABLE=true before crsconfig_dirs processing, which will stop # CLSB initializing ADR, and also prevents the check that leads to 'clsecho' # throwing a warning in case 'clsecho' is used during crsconfig_dirs # processing before the ADR home is created. # Doing this here as a workaround is part of fix for bug#18606913 if ((defined $disableADR) && ($disableADR)) { trace("Set CRS_ADR_DISABLE to stop CLSB initializing ADR"); $ENV{'CRS_ADR_DISABLE'} = "true"; } create_dirs (\@crsconfig_dirs); if ((defined $disableADR) && ($disableADR)) { trace("Enable CLSB to initialize ADR"); undef $CRS_ADR_DISABLE; } # This is a temporary fix until the bug#22927564 gets fixed. fixADRDirsPerms(); copy_wrapper_scripts (); my @crsconfig_fileperms = read_file(catfile($CFG->ORA_CRS_HOME, 'crs', 'utl', $CFG->HOST, 'crsconfig_fileperms')); set_file_perms (\@crsconfig_fileperms); # Set owner/group of ORA_CRS_HOME and its parent dir to root/dba if (! is_dev_env() && (! $CFG->SIHA) && ($CFG->platform_family eq "unix")) { s_setParentDirOwner ($CFG->SUPERUSER, $CFG->ORA_CRS_HOME); } # Assure correct permissions on ADR directories initialize_ADR_dirs(); # create s_crsconfig_$HOST_env.txt file s_createConfigEnvFile (); } # Modify perms on the directories created by ADR under the ADR base.ADR does # not give group write permissions, but group write is needed when the GI and # DB users are different. # This is a temporary fix until the bug 22927564 gets fixed. This bugfix will # set group write perms on the ADR base when it is created by ADR. This will # result in the sub directories under ADR inheriting the permissions of the ADR # base. # # ADR Base: /diag/crs//crs sub fixADRDirsPerms { if ($CFG->platform_family eq "windows") { return; } # List the directories under /diag/crs//crs/ and # set 775 permissions my @dirList; my $adrPath = catdir($CFG->params('ORACLE_BASE'), 'diag', 'crs', $CFG->HOST, 'crs'); trace("Listing the sub-dirs under [$adrPath] ..."); find(sub { push @dirList, $File::Find::name }, $adrPath); foreach my $subdir (@dirList) { if (-d $subdir) { trace("Set permission 0775 on sub directory '$subdir'"); s_set_perms("0775", $subdir) or die (dieformat(153, $subdir)); } } } sub add_RCALLDIR_to_dirs { my $SUPERUSER = $CFG->SUPERUSER; my $dirsfile = catfile ($CFG->ORA_CRS_HOME, 'crs', 'utl', $CFG->HOST, 'crsconfig_dirs'); open (DIRSFILE, ">>$dirsfile") || die(dieformat(208, $dirsfile, $!)); my $myplatformfamily = s_get_platform_family (); $myplatformfamily =~ tr/A-Z/a-z/; # add RCALLDIR locations to crsconfig_dirs my @RCALLDIRLIST = split (/ /, $CFG->params('RCALLDIR')); foreach my $rc (@RCALLDIRLIST) { print DIRSFILE "$myplatformfamily $rc $SUPERUSER $SUPERUSER 0755\n"; } close (DIRSFILE); } sub add_ITDIR_to_dirs #--------------------------------------------------------------------- # Function: add IT_DIR directory to crsconfig_dirs # Args : none #--------------------------------------------------------------------- { if ($CFG->defined_param('IT_DIR')) { if (is_dev_env()) { my $itdir = $CFG->params('IT_DIR'); my $SUPERUSER = $CFG->SUPERUSER; my $dirsfile = catfile($CFG->ORA_CRS_HOME, 'crs', 'utl', $CFG->HOST, 'crsconfig_dirs'); my $platform = s_get_platform_family(); open (DIRSFILE, ">>$dirsfile") or die(dieformat(208, $dirsfile, $!)); print DIRSFILE "$platform $itdir $SUPERUSER $SUPERUSER 0755\n"; close (DIRSFILE); } } } sub create_dirs { # Directories Creation module # # Create directories with ownership/permissions as specified in # crs/utl//crsconfig_dirs my $dirs = shift; foreach my $line (@$dirs) { chomp ($line); next if ($line =~ /^#|\$|^\s*$/); # skip blanks, comments, and lines that # unable to instantiate if (($CFG->SIHA) && (($line =~ /network\/admin/) || ($line =~ /diag\/tnslsnr/))) { trace("LINE is $line"); trace("Skip listener related dirs for SIHA env"); next; } # replace variables in input line my @matches = $line =~ /(\$\w+)/g; for my $match (@matches) { if (defined($CFG->config_value($match))) { my $sub = $CFG->config_value($match); $line =~ s/${match}/$sub/g; } elsif ($CFG->defined_param($match)) { my $sub = $CFG->params($match); $line =~ s/${match}/$sub/g; } } if ($CFG->DEBUG) { trace ("crsconfig_dirs: LINE is $line"); } my ($platform, $dir_path, $owner, $grp, $perms) = split (/ /, $line); my $myplatformfamily = s_get_platform_family (); $myplatformfamily =~ tr/A-Z/a-z/; if (($platform eq "all") || ($platform =~ m/$myplatformfamily/)) { my @dirs_created = create_dir ($dir_path); # if no dir was created, add dir_path to list to set ownership/perms # below if (!@dirs_created) { if ($CFG->DEBUG) { trace (" no dir created; adding $dir_path to list"); } push (@dirs_created, $dir_path); } # Setting same ownership/permissions for all intermediate dirs # as well if (@dirs_created) { foreach my $dir (@dirs_created) { # Skip setting ownership and permissions of the /etc/init.d directory on AIX # because this directory doesn't exist on AIX # On NT, s_set_ownergroup and s_set_perms have no op if ($CFG->platform_family ne "windows") { next if (($OSNAME eq 'aix') && (trim($dir) eq trim($CFG->params('ID')))); # Change permissions on /etc/init.d and /etc/inittab only for the test environment next if ((! is_dev_env()) && (trim($dir) eq trim($CFG->params('ID')))); # Change permissions on RCALLDIR dir's only for the # test environment my $set_perms; $set_perms = check_dir_in_RCALLDIR($dir) ; next if ((! is_dev_env()) && ($set_perms == TRUE)) ; s_set_ownergroup ($owner, $grp, $dir) or die(dieformat(152, $dir)); if ($perms) { s_set_perms ($perms, $dir) or die(dieformat(153, $dir)); } } } } } } } # # Check whether given directory ( from crsconfig_dirs ) present # in RCALLDIR dir's. # # ARGS # arg 0 -- dir sub check_dir_in_RCALLDIR { my $dir = $_[0]; my @RCALLDIRLIST = split ( /\s+/, $CFG->params('RCALLDIR')); foreach my $rc (@RCALLDIRLIST) { if ( trim($rc) eq trim($dir) ) { return TRUE ; } } return FALSE ; } #--------------------------------------------------------------------------------- # Function: Copy files from SOURCE to DEST as specified in crs/utl/crsconfig_files # Args: # Returns: #--------------------------------------------------------------------------------- sub copy_wrapper_scripts { my @wcfile = read_file(catfile($CFG->ORA_CRS_HOME, 'crs', 'utl', 'crsconfig_files')); trace ("Processing crsconfig_files.sbs and copying wrapper scripts"); foreach my $line (@wcfile) { chomp ($line); next if ($line =~ /^#|^\s*$/); # skip blanks and comments my ($platform, $src, $dst) = split (/ /, $line); my $myplatformfamily = s_get_platform_family (); $myplatformfamily =~ tr/A-Z/a-z/; if (($platform eq "all") || ($platform =~ m/$myplatformfamily/)) { # If the dest file already exists, first remove it if (-e $dst) { if ($CFG->DEBUG) { trace ("unlink ($dst)"); } unlink ($dst) or print_error(168, $dst, $!); } if ($CFG->DEBUG) { trace ("copy ($src, $dst)"); } copy($src, $dst) or print_error(105, $src, $dst, $!); } } } # # File permissions module # # Set ownership/permissions as specified in # crs/utl//crsconfig_fileperms (after touching the file, if # required) # # ARGS # arg 0 -- param hash sub set_file_perms { my $fpfile = shift; my $myplatform = $CFG->platform_family; my ($file_name, $bin_file); foreach my $line (@$fpfile) { chomp ($line); next if ($line =~ /^#|^\s*$/); # skip blanks and comments if (($CFG->SIHA) && ($line =~ /network\/admin/)) { trace("LINE is $line"); trace("Skip listener related files for SIHA env"); next; } # replace variables in input line $line =~ s/(\$\w+)/$1/eeg; if ($CFG->DEBUG) { trace ("crsconfig_fileperms: LINE is $line"); } my ($platform, $file_path, $owner, $grp, $perms) = split (/\s+/, $line); if ((! is_dev_env()) && ($myplatform ne "windows") && (trim($file_path) eq trim($CFG->params('IT')))) { trace("Skipping setting ownership & permissions on file $file_path"); next; } if (($platform eq "all") || ($platform =~ m/$myplatform/)) { if ( !(-e $file_path) && ( $file_path !~ m/.*acfs.*/ ) && ( $file_path !~ m/.*oka.*/ ) && ( $file_path !~ m/.*afd.*/ ) ) { if ($CFG->DEBUG) { trace ("Creating empty file $file_path");} # create an empty file open (FILEHDL, ">$file_path") or die(dieformat(255, $file_path, $!)); close (FILEHDL); } elsif (!(-e $file_path) && ( $file_path =~ m/.*acfs.*/ )) { trace ("skipping set file perm for acfs file $file_path. File does not exist"); next; } elsif (!(-e $file_path) && ( $file_path =~ m/.*oka.*/ )) { trace ("skipping set file perm for oka file $file_path. File does not exist"); next; } elsif (!(-e $file_path) && ( $file_path =~ m/.*afd.*/ )) { trace ("skipping set file perm for afd file $file_path. File does not exist"); next; } # Set ownership/group if ($CFG->DEBUG) { trace ("s_set_ownergroup ($owner, $grp, $file_path)"); } $file_name = basename($file_path); # in dev env, perms should not be set on symlinks my $link_path; if ($link_path = s_isLink ($file_path)) { $bin_file = TRUE; } elsif (($file_name =~ m/\.bin/) && ($owner eq $CFG->HAS_USER)) { $bin_file = TRUE; } else { $bin_file = FALSE; } if ($bin_file && is_dev_env()) { trace("Development env... Not setting permissions on $file_name"); } else { # The bug fix for BUG 12752359 - GRID INSTALL IN SC ZONE FAILED, # ROOT.SH CAN'T CHANGE PERMISSION OF LIBSKGXN2.SO if (($file_name =~ /skgxn2/) && ($link_path)) { trace("Skip changing the ownership and ". "permissions of $file_name"); next; } # Set ownership/group s_set_ownergroup ($owner, $grp, $file_path) or die(dieformat(152, $file_path)); # Set permissions, if specified if ($perms) { if ($CFG->DEBUG) { trace ("s_set_perms ($perms, $file_path)");} s_set_perms ($perms, $file_path) or die(dieformat(153, $file_path)); } } } } if (! is_dev_env() && (! $CFG->SIHA)) { if ($myplatform eq "unix") { if ((!$CFG->ASM_STORAGE_USED) && (! isOCRonASM())) { # in an upgrade, OCR_LOCATIONS would be empty. if ($CFG->UPGRADE) { mod_perms_ocr_loc(); } else { my @ocr_locs = split (/\s*,\s*/, $CFG->params('OCR_LOCATIONS')); foreach my $loc (@ocr_locs) { # Set owner/group of OCR path to root/dba trace ("set owner/group of OCR path"); if (-e $loc) { s_setParentDirOwner ($CFG->SUPERUSER, $loc); } } } } } } } sub mod_perms_ocr_loc { my $ocrconfig_loc = s_get_config_key('ocr', 'ocrconfig_loc'); my $ocrmirror_loc = s_get_config_key('ocr', 'ocrmirrorconfig_loc'); trace ("ocrconfig_loc=$ocrconfig_loc"); trace ("ocrmirror_loc=$ocrmirror_loc"); if ($ocrconfig_loc) { # check if it's a symbolic link if (-l $ocrconfig_loc) { my $abs_ocr = s_getAbsLink($ocrconfig_loc); s_setParentDirOwner ($CFG->SUPERUSER, $abs_ocr); } else { if (-e $ocrconfig_loc) { s_setParentDirOwner ($CFG->SUPERUSER, $ocrconfig_loc); } } } if ($ocrmirror_loc) { if (-l $ocrmirror_loc) { my $abs_ocrmirror = s_getAbsLink($ocrmirror_loc); s_setParentDirOwner ($CFG->SUPERUSER, $abs_ocrmirror); } else { if (-e $ocrmirror_loc) { s_setParentDirOwner ($CFG->SUPERUSER, $ocrmirror_loc); } } } } ####--------------------------------------------------------- #### Function to check if OCR is on ASM sub isPathonASM { trace ("Checking if given path is on ASM"); my $diskpath = $_[0]; if (!$diskpath) { trace ("Device path is not specified"); return FALSE; } if ($diskpath =~ /\+/) { return TRUE; } else { return FALSE; } } sub isRAC_appropriate #------------------------------------------------------------------------------- # Function: Check if rac_on/rac_off on Unix # Args : none # Returns : TRUE if rac_on/rac_off needs to be set # FALSE if rac_on/rac_off not needs to be set #------------------------------------------------------------------------------- { my $myplatformfamily = s_get_platform_family (); $myplatformfamily =~ tr/A-Z/a-z/; if ($myplatformfamily eq "unix") { return s_isRAC_appropriate (); } else { return TRUE } } sub set_perms_ocr_vdisk { my $platform = $CFG->platform_family; my ($line, @dirs, @files); # add OCR_LOCATION and OCR_MIRROR_LOCATION if (! $CFG->SIHA) { # OCR permissions need to change to 0600 when CSSD dependency on OCR # goes away. Bypass if ASM is used. if (!$CFG->ASM_STORAGE_USED) { my @ocr_locs = split (/\s*,\s*/, $CFG->params('OCR_LOCATIONS')); my $loc; foreach $loc (@ocr_locs) { $line = join (' ', $platform, $loc, $CFG->SUPERUSER, $CFG->params('ORA_DBA_GROUP'), '0640'); push @files, $line; # set owner and permission of OCR directory my @loc_dirs; if ($platform eq "windows") { @loc_dirs = split (/\\/, $loc); } else { @loc_dirs = split (/\//, $loc); } my $nbr_of_levels = scalar (@loc_dirs); # $nbr_of_levels = 2 means it's at the root directory (exp: R:\ocr). # Therefore, no need to add to crsconfig_dirs if ($nbr_of_levels > 2 ) { my ($dir) = split ($loc_dirs[$nbr_of_levels-1], $loc); $line = join (' ', $platform, $dir, $CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), '0755'); push @dirs, $line; } } } } # add all voting disks. Bypass if ASM is used. # XXX: is this step required? Existing shell scripts don't seem to be # using validate_VDisks() function if (!$CFG->ASM_STORAGE_USED) { my @votingdisks = split (/\s*,\s*/, $CFG->params('VOTING_DISKS')); my $vdisk; foreach $vdisk (@votingdisks) { # voting disk should not be precreated since the startup script may # be run as a different user than crs user. Precreating/touching # a voting disk prematurely will cause later I/Os to fail, such as # voting file upgrade/create # # set owner and permission of votind disks directory by adding to # crsconfig_dirs my @vdisk_dirs; if ($platform eq "windows") { @vdisk_dirs = split (/\\/, $vdisk); } else { # other platforms @vdisk_dirs = split (/\//, $vdisk); } my $nbr_of_levels = scalar (@vdisk_dirs); # $nbr_of_levels = 2 means it's at the root directory (exp: R:\vdisk). # Therefore, no need to add to crsconfig_dirs if ($nbr_of_levels > 2 ) { my ($dir) = split ($vdisk_dirs[$nbr_of_levels-1], $vdisk); $line = join (' ', $platform, $dir, $CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), '0755'); push @dirs, $line; } } } # add OCRCONFIGDIR and OLRCONFIGDIR to crsconfig_dirs my $owner; if (is_dev_env()) { $owner = $CFG->params('ORACLE_OWNER'); } else { $owner = $CFG->SUPERUSER; } if ($CFG->defined_param('OCRCONFIGDIR')) { $line = join (' ', $platform, $CFG->params('OCRCONFIGDIR'), $owner, $CFG->params('ORA_DBA_GROUP'), '0755'); push @dirs, $line; } if ($CFG->defined_param('OLRCONFIGDIR') && $CFG->defined_param('OCRCONFIGDIR')) { if ($CFG->params('OLRCONFIGDIR') ne $CFG->params('OCRCONFIGDIR')) { $line = join (' ', $platform, $CFG->params('OLRCONFIGDIR'), $owner, $CFG->params('ORA_DBA_GROUP'), '0755'); push @dirs, $line; } } create_dirs (\@dirs); set_file_perms (\@files); } sub setperm_vdisks #------------------------------------------------------------------------------- # Function: Set voting disks permission #------------------------------------------------------------------------------- { if (($CFG->platform_family eq 'windows') || ($CFG->ASM_STORAGE_USED) || (isOCRonASM())) { return SUCCESS; } my $owner = $CFG->params('ORACLE_OWNER'); my $group = $CFG->params('ORA_DBA_GROUP'); my $rc = SUCCESS; my @vdisks = extractVotedisks(); foreach (@vdisks) { if ($CFG->DEBUG) { trace ("set permission on $_"); } if (! s_set_ownergroup ($owner, $group, $_)) { $rc = FAILED; print_error(152, $_); last; } if (! s_set_perms("0640", $_)) { $rc = FAILED; print_error(153, $_); last; } } return $rc; } sub copydir { my $sourcedir = $_[0]; my $destdir = $_[1]; my ($filepath, $filename, $destfilepath, $dirpath); my @filelist; trace("source dir=$sourcedir"); trace("dest dir=$destdir"); find(sub { push @filelist, $File::Find::name }, $sourcedir); if ($CFG->platform_family eq "windows") { # escape char '\' on Windows $destdir =~ s!\\!\\\\!g; $sourcedir =~ s!\\!\\\\\\\\!g; } foreach $filepath (@filelist) { if ( -f $filepath) { $filename = basename($filepath); # Do not copy OCR backup files # Skip copying shrept.lst due to bug 18346917 if ($filename =~ /\.ocr$/ || "shrept.lst" eq lc($filename)) { trace("Skipping the file '$filename'"); next; } $dirpath = dirname($filepath); if ($CFG->platform_family eq "windows") { # escape char '\' on Windows $dirpath =~ s!\\!\\\\!g; } $dirpath =~ s/$sourcedir/$destdir/g; if (! -e $dirpath) { trace("creating directory $dirpath"); mkpath( $dirpath); s_set_ownergroup ($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $dirpath); } $destfilepath = catfile($dirpath, $filename); trace ("copying file=$filename"); trace ("source file path=$filepath"); trace ("dest file path=$destfilepath"); copy_file ($filepath, $destfilepath, $CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP')); } } } ####--------------------------------------------------------- #### Copy file from one local location to another # # Copies file from one local location to another # if user/group given, will chown copied file as it # ARGS: 4 # ARG1 : Source file path # ARG2 : Destination file path # ARG3 : User owner to set (or undef) # ARG4 : Group owner to set (or undef) # @returns SUCCESS or $FAILURE # sub copy_file { my $src = $_[0]; my $dst = $_[1]; my $usr = $_[2]; my $grp = $_[3]; if (! (-f $src)) { trace(" $src ? -f failed" ); return FAILED; } trace(" copy \"$src\" => \"$dst\"" ); if (! copy( $src, $dst )) { print_error(105, $src, $dst, $!); return FAILED; } # chown to specific user if requested if (defined( $usr ) && defined( $grp )) { trace(" set ownership on \"$dst\" => ($usr,$grp)" ); if (FAILED == s_set_ownergroup ($usr, $grp, $dst)) { print_error(152, $dst); return FAILED; } } return SUCCESS; } sub getCkptStatus #------------------------------------------------------------------------------- # Function: Get the checkpoint status # Arg1 : Check point Name # Arg2 : "-global" or undef # pass "-global" to check for the global checkpoint # Returns : CKPTSTART, CKPTSUC or CKPTFAIL #------------------------------------------------------------------------------- { my $ckptName = $_[0]; my $ckptType = $_[1]; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $run_as_user = TRUE; my @program; if (! defined($ckptType)) { @program = ('-ckpt', '-oraclebase', $ORACLE_BASE, '-chkckpt', '-name', $ckptName, '-status'); } elsif ($ckptType eq "-global") { @program = ('-ckpt', '-global', '-oraclebase', $ORACLE_BASE, '-chkckpt', '-name', $ckptName, '-status'); } my $args_str = join(' ', @program); my ($rc, @capout) = cluutil($run_as_user, $args_str); # cluutil return 0 err code and errors, if any, on stdout if (scalar(grep(/START/, @capout)) > 0) { trace("The '$ckptName' status is START"); return CKPTSTART; } elsif (scalar(grep(/SUCCESS/, @capout)) > 0) { trace("The '$ckptName' status is SUCCESS"); return CKPTSUC; } elsif (scalar(grep(/FAIL/, @capout)) > 0) { trace("The '$ckptName' status is FAILED"); return CKPTFAIL; } } sub isCkptSuccess #------------------------------------------------------------------------------- # Function: Check if the checkpoint status is SUCCESS # Arg1 : Check point Name # Arg2 : "-global" or undef # pass "-global" to check for the global checkpoint # Returns : boolean #------------------------------------------------------------------------------- { my $ckptName = $_[0]; my $ckptType = $_[1]; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $run_as_user = TRUE; my @program; if (! defined($ckptType)) { @program = ('-ckpt', '-oraclebase', $ORACLE_BASE, '-chkckpt', '-name', $ckptName, '-status'); } elsif ($ckptType eq "-global") { @program = ('-ckpt', '-global', '-oraclebase', $ORACLE_BASE, '-chkckpt', '-name', $ckptName, '-status'); } my $args_str = join(' ', @program); my ($rc, @capout) = cluutil($run_as_user, $args_str); # cluutil return 0 err code and errors, if any, on stdout if (scalar(grep(/SUCCESS/, @capout)) > 0) { trace("The '$ckptName' status is SUCCESS"); return TRUE; } else { trace("The '$ckptName' is either in START/FAILED state"); return FALSE; } } sub isCkptexist #------------------------------------------------------------------------------- # Function: Verify if checkpoint exist # Arg1 : Check point Name # Arg2 : "-global" or undef # pass "-global" to check for the global checkpoint # Returns : boolean #------------------------------------------------------------------------------- { my $ckptName = $_[0]; my $ckptType = $_[1]; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $run_as_user = TRUE; my @program; if (! defined($ckptType)) { @program = ('-ckpt', '-oraclebase', $ORACLE_BASE, '-chkckpt', '-name', $ckptName); } elsif ($ckptType eq "-global") { @program = ('-ckpt', '-global', '-oraclebase', $ORACLE_BASE, '-chkckpt', '-name', $ckptName); } my $args_str = join(' ', @program); trace( ' ckpt: '.join(' ', @program) ); my ($rc, @capout) = cluutil($run_as_user, $args_str); # cluutil return 0 err code and errors, if any, on stdout if (scalar(grep(/TRUE/, @capout)) > 0) { return SUCCESS; } if ((0 != $rc) || (scalar(grep(/FALSE/, @capout))) > 0) { trace("checkpoint $ckptName does not exist"); return FAILED; } } ####-------------------------------------------------------------------------- ##### Check a remote host for reachability using ICMP ping ## ARGS: 2 ## ARG1: hostname or IP address ## ARG2: timeout value in seconds ## @returns ## $ret: 1 - reachable; 0 - unreachable; undef - invalid hostname/IP address ## ## Note: The caller must run as root or be setuid to root ###--------------------------------------------------------------------------- sub pingWithICMP { my ($host, $timeout) = @_; my $pObj; $pObj = Net::Ping->new("icmp", $timeout, 64); my $ret = $pObj->ping($host); $pObj->close(); return $ret; } =head1 EXPORTED FUNCTIONS =head2 isNodeAlive Check whether a given node is still alive. =head3 Parameters [0] hostname =head3 Returns TRUE / FALSE =cut sub isNodeAlive { my $node = $_[0]; my $run_as_user = TRUE; my @program = ('-exec', '-nodealive', '-node', $node); my $args_str = join(' ', @program); trace("Checking a remote host $node for reachability..."); my ($rc, @capout) = cluutil($run_as_user, $args_str); if ( $rc == 0 ) { my @args = split(':', $capout[0]); my $status = trim($args[1]); trace("$node is $status"); return ($status eq 'ALIVE') ? TRUE : FALSE; } else { print_lines(@capout); trace("cluutil $args_str failed with status $rc"); die(dieformat(180, "cluutil $args_str")); } } =head1 EXPORTED FUNCTIONS =head2 getInstallNode Get the node name of the node on which the installer is running =head3 Parameters None =head3 Returns The local hostname with the domain name stripped off =cut sub getInstallNode { my $installNode = lc((split(/\./, ($CFG->params('INSTALL_NODE'))))[0]); trace("Install node: $installNode"); return $installNode; } =head1 EXPORTED FUNCTIONS =head2 isForcedFirstNode If user specifies '-first -force' option while running the root script on current node, this node will be taken as the first node to run root scripts. =head3 Parameters None =head3 Returns TRUE/FALSE =cut sub isForcedFirstNode { my $localNode = tolower_host(); my $installNode = getInstallNode(); if (($CFG->FIRST) && ($localNode ne $installNode)) { # the local node is made as the first node when -first -force is specified. trace("Local node: $localNode is being forced as the first node."); return TRUE; } elsif ($localNode eq $installNode) { # This elsif condition is entered either when the root script is run on the # install node first, or after forcing a non-install node as the first node. my $ckptExist = isCkptexist("ROOTCRS_FIRSTNODE", "-global"); my $ckptState = trim(getCkptStatus("ROOTCRS_FIRSTNODE", "-global")); trace("Global ckpt 'ROOTCRS_FIRSTNODE' state: $ckptState"); if ($ckptExist && ($ckptState eq CKPTSUC)) { # Root script is run on the install node after forcing a non-install node # as the first node. trace("First node operations have been done by forcing first node on a " . "non-installer node."); trace("Local node: " . $localNode . " is not the first node."); return FALSE; } else { # root script is run on the install node first trace("First node operations have not been done, and local node is " . "installer node."); trace("Local node: " . $localNode . " is the first node."); return TRUE; } } else { trace("Local node is neither installer node, nor a forced first node."); trace("Local node: " . $localNode . " is not the first node."); return FALSE; } } =head1 EXPORTED FUNCTIONS =head2 isInstallNode Check if the current node is the install node, i.e., the node where the OUI is running =head3 Parameters None =head3 Returns TRUE or FALSE =cut sub isInstallNode { my $ret = FALSE; my $localNode = tolower_host(); my $installNode = getInstallNode(); if ($localNode eq $installNode) { trace("Current node is the install node"); $ret = TRUE; } else { trace("Current node is not the install node"); $ret = FALSE; } return $ret; } =head1 EXPORTED FUNCTIONS =head2 checkFirstNodeStatus Check if the first node has been successfully configured or upgraded based on the global checkpoints in OCR =head3 Parameters None =head3 Returns None =cut sub checkFirstNodeStatus { my $localNode = tolower_host(); trace("Check if the first node has been successfully configured or upgraded"); if (isForcedFirstNode()) { trace("Local node is the first node, hence nothing to check"); return; } my $installNode = getInstallNode(); trace("Check the existence of global ckpt 'ROOTCRS_FIRSTNODE'"); if (!isCkptexist("ROOTCRS_FIRSTNODE", "-global")) { if (isNodeAlive($installNode)) { trace("Run root config or upgrade scripts on $installNode first"); die(dieformat(507, $localNode, $installNode)); } else { trace("First node is not alive, must choose any one of the remaining ". "nodes with '-force' option to resume first node operations"); die(dieformat(506, $localNode, $installNode)); } } my $ckptState = getCkptStatus("ROOTCRS_FIRSTNODE", "-global"); $ckptState = trim($ckptState); trace("Global ckpt 'ROOTCRS_FIRSTNODE' state: $ckptState"); if (CKPTSUC ne $ckptState) { if (isNodeAlive($installNode)) { if (CKPTSTART eq $ckptState) { trace("The first node operations are still in progress"); die(dieformat(504, $localNode, $installNode)); } if (CKPTFAIL eq $ckptState) { trace("The first node operations have not yet successfully completed"); die(dieformat(505, $localNode, $installNode)); } } else { # Fist node is not alive trace("First node is not alive, must choose any one of the remaining ". "nodes with '-force' option to resume first node operations"); die(dieformat(506, $localNode, $installNode)); } } trace("The first node has been successfully configured or upgraded"); } sub writeCkpt { my $ckptName = $_[0]; my $ckptState = $_[1]; my $crshome = $CFG->ORA_CRS_HOME; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $localNode = tolower_host(); my $run_as_user = TRUE; my @program = ('-ckpt', '-oraclebase', $ORACLE_BASE, '-writeckpt', '-name', $ckptName, '-state', $ckptState); my $args_str = join(' ', @program); my ($rc, @capout) = cluutil($run_as_user, $args_str, $crshome); if (0 != $rc) { print_lines(@capout); trace("Failed to write the checkpoint:'$ckptName' with status:$ckptState.Error code is $rc"); print_error(175, $ckptName, $ckptState, $rc); die(dieformat(175, $ckptName, $ckptState, $rc)) if (CKPTSUC eq $ckptState); return FAILED; } else { trace("Succeeded in writing the checkpoint:'$ckptName' with status:$ckptState"); # If in SIHA environment, no need to write global checkpoint. return SUCCESS if (($CFG->SIHA) || ($CFG->PREPATCH) || ($CFG->POSTPATCH) || ($CFG->HAPatch) || ($CFG->GIMOVE)); if (($ckptName ne "ROOTCRS_STACK") && (CKPTSUC eq $ckptState)) { $ckptState = CKPTSTART; } if (isForcedFirstNode()) { if ($CFG->UPGRADE) { my $firstnodeState = getCkptStatus("ROOTCRS_FIRSTNODE", "-global"); $firstnodeState = trim($firstnodeState); # if firstnode CKPT is SUCCESS, first-node operations have been done. # Avoid changing firstnode CKPT while current node is forced to do # the last-node operations. if ($firstnodeState ne CKPTSUC ) { writeGlobalCkpt("ROOTCRS_FIRSTNODE", $ckptState, "-transferfile"); } } else { writeGlobalCkpt("ROOTCRS_FIRSTNODE", $ckptState, "-transferfile"); } } if (($CFG->UPGRADE) && ($CFG->isLastNodeToUpgrade)) { my $lastnodeState = getCkptStatus("ROOTCRS_LASTNODE", "-global"); $lastnodeState = trim($lastnodeState); if ($lastnodeState ne CKPTSUC) { if ($ckptState eq CKPTSUC) { my $ocrkey = 'LASTNODE'; my $oraocr = $CFG->compOCR; my $succ = $oraocr->removeOcrKeyPair($ocrkey); if (! $succ) { die(dieformat(663, "SYSTEM.$ocrkey")); } } writeGlobalCkpt("ROOTCRS_LASTNODE", $ckptState, "-transferfile"); } } return SUCCESS; } } sub writeCkptPropertyFile { my $ckptName = $_[0]; my $ckptPropFile = $_[1]; my $crshome = $CFG->ORA_CRS_HOME; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $run_as_user = TRUE; my @program = ('-ckpt', '-oraclebase', $ORACLE_BASE, '-writeckpt', '-name', $ckptName, '-pfile', $ckptPropFile); my $args_str = join(' ', @program); my ($rc, $capout) = cluutil($run_as_user, $args_str, $crshome); if (0 != $rc) { trace("Failed to write contents of pfile :$ckptPropFile for checkpoint:$ckptName. Error code is $rc"); print_error(176, $ckptPropFile, $ckptName, $rc); return FAILED; } else { trace("write contents of pfile :$ckptPropFile for checkpoint:$ckptName succeeded"); return SUCCESS; } } sub writeCkptProperty #------------------------------------------------------------------------------- # Function: Add property/value for a check point # ARG1 : Check point Name # ARG2 : property name # ARG3 : property value # ARG4 : "-global" or undef # ARG5 : "-transferfile" or undef # pass "-global" to check for the global checkpoint # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- { my $ckptName = $_[0]; my $ckptPropName = $_[1]; my $ckptPropValue = $_[2]; my $ckptType = $_[3]; my $transfer = $_[4]; my $crshome = $CFG->ORA_CRS_HOME; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $run_as_user = TRUE; my @program; if (! defined($ckptType)) { @program = ('-ckpt', '-oraclebase', $ORACLE_BASE, '-writeckpt', '-name', $ckptName, '-pname', $ckptPropName, '-pvalue', $ckptPropValue); } elsif ($ckptType eq "-global") { my $nodelist = $CFG->params('NODE_NAME_LIST'); @program = ('-ckpt', '-global', '-oraclebase', $ORACLE_BASE, '-writeckpt', '-name', $ckptName, '-pname', $ckptPropName, '-pvalue', $ckptPropValue, '-nodelist', $nodelist); if (($transfer eq "-transferfile") && !isOracleBaseShared()) { push(@program, $transfer); } } my $args_str = join(' ', @program); my ($rc, $capout) = cluutil($run_as_user, $args_str, $crshome); if (0 != $rc) { trace("Failed to add (property/value):('$ckptPropName/'$ckptPropValue') for checkpoint:$ckptName.Error code is $rc"); print_error(177, $ckptPropName, $ckptPropValue, $ckptName, $rc); return FAILED; } else { trace("Succeeded to add (property/value):('$ckptPropName/'$ckptPropValue') for checkpoint:$ckptName"); return SUCCESS; } } ####--------------------------------------------------------- #### Check if this is a SI CSS configuration sub validate_SICSS { trace ("Validating for SI-CSS configuration"); my $oraocr = $CFG->compOCR; my $ocrfile = $oraocr->get_ocrdisk(); if (!$ocrfile) { trace ("Unable to retrieve ocr disk info"); return SUCCESS; } if ($ocrfile =~ /\+/) { # return if ocrfile is ASM disk group return SUCCESS; } else { if (! (-e $ocrfile)) { print_error(13, $ocrfile); return SUCCESS; } } # OCR location already specified. Check if it is used for # single instance CSS/ASM my $local_flag = s_get_config_key("ocr", "local_only"); if (!$local_flag) { return FAILED; } # convert to upper-case $local_flag =~ tr/a-z/A-Z/; # Previous installation of 10g single instance trace ("LOCAL_FLAG = " . $local_flag); if ($local_flag eq "TRUE") { print_error(14); return FAILED; } return SUCCESS; } =head2 versionComparison Make a comparison between two version strings =head3 Parameters Args: 2 [0]: version1 [1]: version2 =head3 Returns 1: version1 > version2 0: version1 = version2 -1: version1 < version2 =head3 Usage my $ret = versionComparison("12.1.0.0.0", "11.1.0.0.0"); =cut sub versionComparison { my $leftVersion = $_[0]; my $rightVersion = $_[1]; my $retVal = 0; trace("leftVersion=$leftVersion; rightVersion=$rightVersion"); if ((!$leftVersion) || (!isValidVersion($leftVersion))) { die("Missing or invalid version number [$leftVersion]"); } if ((!$rightVersion) || (!isValidVersion($rightVersion))) { die("Missing or invalid version number [$rightVersion]"); } if ($leftVersion eq $rightVersion) { trace("[$leftVersion] is same as [$rightVersion]"); return $retVal; } my @ver1 = split(/\./, $leftVersion); my @ver2 = split(/\./, $rightVersion); my $i = 0; for (; $i < scalar(@ver1); $i++) { my $j = 0; while (($j < $i) && ($ver1[$j] == $ver2[$j])) { $j++; } if (($j == $i) && ($ver1[$j] > $ver2[$j])) { last; } } if ($i < scalar(@ver1)) { $retVal = 1; } else { $retVal = -1; } my $cmp = ((1 == $retVal) ? 'higher' : 'lower'); trace("[$leftVersion] is $cmp than [$rightVersion]"); return $retVal; } =head2 isValidVersion Check validity of a version string, which must be in the format A.B.C.D.E =head3 Parameters Args: 1 [0]: Version to be checked =head3 Returns TRUE or FALSE =head3 Usage =cut sub isValidVersion { my $verString = shift; my @ver = split(/\./, $verString); if (scalar(@ver) != 5) { trace("Not 5-digit version numbers"); return FALSE; } foreach my $v (@ver) { if (($v !~ /^[0-9]+$/) || ($v =~ /^0.+$/)) { trace("Not all version numbers are numeric"); return FALSE; } } return TRUE; } sub isVersion10 { my $is10ver; my @oldcrs_ver = @{$CFG->oldconfig('ORA_CRS_VERSION')}; if ($oldcrs_ver[0] eq '10') { $is10ver = TRUE; } else { $is10ver = FALSE; } return $is10ver; } sub isVersion111 { my $is111ver; my @oldcrs_ver = @{$CFG->oldconfig('ORA_CRS_VERSION')}; if ($oldcrs_ver[0] eq '11' && $oldcrs_ver[1] eq '1') { $is111ver = TRUE; } else { $is111ver = FALSE; } return $is111ver; } sub isVersion112 { my $is112ver; my @oldcrs_ver = @{$CFG->oldconfig('ORA_CRS_VERSION')}; if ($oldcrs_ver[0] eq '11' && $oldcrs_ver[1] eq '2') { $is112ver = TRUE; } else { $is112ver = FALSE; } return $is112ver; } # Function to check if old CRS version is 12.1.0.1.0 sub isOldVersion121010 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (0 == versionComparison($verinfo, "12.1.0.1.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is lower than 12.1 sub isOldVersionLT121 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (-1 == versionComparison($verinfo, "12.1.0.0.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is lower than 12.1.0.2 sub isOldVersionLT12102 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (-1 == versionComparison($verinfo, "12.1.0.2.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is 12.1.0.2 sub isOldVersion12102 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (0 == versionComparison($verinfo, "12.1.0.2.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is lower than 12.2 sub isOldVersionLT122 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (-1 == versionComparison($verinfo, "12.2.0.0.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is lower than 11.2 sub isOldVersionLT112 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (-1 == versionComparison($verinfo, "11.2.0.0.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is lower than 11.2.0.2.0 sub isOldVersionLT11202 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (-1 == versionComparison($verinfo, "11.2.0.2.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is lower than 11.2.0.3.0 sub isOldVersionLT11203 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (-1 == versionComparison($verinfo, "11.2.0.3.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is lower than 11.2.0.4.0 sub isOldVersionLT11204 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (-1 == versionComparison($verinfo, "11.2.0.4.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is lower than 11.1 sub isOldVersionLT111 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (-1 == versionComparison($verinfo, "11.1.0.0.0")) { return TRUE; } else { return FALSE; } } # Function to check if old CRS version is 12.1.0.1 or 12.1.0.2 sub isOldVersion121 { my @oldCrsVer = @{$CFG->oldconfig('ORA_CRS_VERSION')}; my $verinfo = join('.', @oldCrsVer); if (0 == versionComparison($verinfo, "12.1.0.1.0") || 0 == versionComparison($verinfo, "12.1.0.2.0")) { return TRUE; } else { return FALSE; } } ####--------------------------------------------------------- #### Function to check if OCR is on ASM sub isOCRonASM { trace ("Checking if OCR is on ASM"); my $oraocr = $CFG->compOCR; my $ocrfile = $oraocr->get_ocrdisk(); my $ocrmirror = $oraocr->get_ocrmirrordisk(); my $ocrloc3 = $oraocr->get_ocrloc3disk(); my $ocrloc4 = $oraocr->get_ocrloc4disk(); my $ocrloc5 = $oraocr->get_ocrloc5disk(); if (!$ocrfile) { trace ("OCR config does not exist"); return FALSE; } if (($ocrfile =~ /\+/) || ($ocrmirror =~ /\+/) || ($ocrloc3 =~ /\+/) || ($ocrloc4 =~ /\+/) || ($ocrloc5 =~ /\+/)) { trace("OCR is on ASM"); return TRUE; } else { trace("OCR is NOT on ASM"); return FALSE; } } sub isONSexist #------------------------------------------------------------------------------- # Function: Find if ONS is already configured # Args : none # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- { my $status = SUCCESS; my $run_as_owner = FALSE; trace("Invoking srvctl config ons"); my $rc = srvctl($run_as_owner, "config ons"); if ($rc == FALSE) { trace("ONS resource does not exist."); $status = FAILED; } else { trace("ONS resource exist"); } return $status; } sub get_srvdisk #------------------------------------------------------------------------------- # Function: Retrieving SRV location # Args : none #------------------------------------------------------------------------------- { trace ("Retrieving SRV location"); return s_get_config_key("srv", "srvconfig_loc"); } #------------------------------------------------------------------------------ # Ref: # Table 1-1 List of Processes and Services Associated with Oracle Clusterware # Components # #------------------------------------------------------------------------------ =head2 getCrsHome This function is only used for partial downgrade (downgrading the lastnode which has never been upgraded). It returns the old crs home in case of partial downgrade, otherwise, remain using the $CFG->ORA_CRS_HOME. =head3 Parameters Args: None =head3 Returns $oldCrsHome - if partial downgrade $CFG->ORA_CRS_HOME - if not partial downgrade =cut sub getCrsHome { my $crsHome = $CFG->ORA_CRS_HOME; if (($CFG->DOWNGRADE) && ($CFG->LASTNODE)) { my $oldCrsHome = s_get_olr_file("crs_home"); my $oracleHome = $CFG->ORA_CRS_HOME; if ($oldCrsHome ne $oracleHome) { # partial downgrade $crsHome = $oldCrsHome; } } trace("Configured CRS Home: $crsHome"); return $crsHome; } ####--------------------------------------------------------- #### Function for checking daemon/service # ARGS: 2 # ARG1: daemon to be checked # ARG2: num retries sub check_service { my $srv = $_[0]; my $retries = $_[1]; my $srv_running = FALSE; my $currentHome = getCrsHome(); #it's configured crs home in case of partial downgrade my $CRSCTL = crs_exec_path("crsctl", $currentHome); my $cmd = "$CRSCTL check $srv"; my $grep_val; my @chk; my @cmdout; # for OHASD, we need to grep for CRS-4638 # cannot use grep on Windows, customers are unlikely to have grep # on their systems # for CRS, we need to grep for CRS-4537 if ($srv eq "ohasd") { $grep_val = "4638"; $cmd = "$CRSCTL check has"; } elsif ($srv eq "cluster") { my $node = $CFG->HOST; $cmd = "$CRSCTL check $srv -n $node"; $grep_val = "4537"; trace("Running $cmd"); } elsif ($srv eq "css") { $grep_val = "4529"; } elsif ($srv eq "crs") { $grep_val = "4537"; trace("Running $cmd"); } else { # Add a check before the stat $cmd = "$CRSCTL check resource $srv -init"; @chk = system_cmd_capture($cmd); # Return code of command is set on close, so capture now my $rc0 = shift @chk; if ($rc0 != 0) { trace("Check of service \"$_[0]\" failed\n".join("\n", @chk)); } $cmd = "$CRSCTL status resource $srv -init"; $grep_val = "STATE=ONLINE"; } # Wait for srv to start up while ($retries && ! $srv_running) { @chk = system_cmd_capture($cmd); # Return code of command is set on close, so capture now my $rc = shift @chk; if ($grep_val) { @cmdout = grep(/$grep_val/, @chk); } # for OHASD # if scalar(@cmdout) > 0, we found the msg we were looking for if (($grep_val && scalar(@cmdout) > 0) || (!$grep_val && $rc == 0)) { $srv_running = TRUE; } else { trace ("Checking the status of $srv"); sleep (5); $retries--; } } # perform OSD actions s_check_service ($srv, $srv_running); return $srv_running; } ####--------------------------------------------------------- #### Verify directory exists # ARGS: 1 # ARG1 : Path to check # @returns SUCCESS or $FAILURE # static sub check_dir { my $chkdirnm = $_[0]; if (!(defined($chkdirnm ))) { trace("No value passed for the directory name"); return FAILED; } if (!(-d $chkdirnm)) { trace("The directory \"$chkdirnm\" does not exist"); return FAILED; } # not checking perms, since they may not be valid for root return SUCCESS; } ####--------------------------------------------------------- #### Verify file exists # ARGS: 1 # ARG1 : Path to check # @returns SUCCESS or $FAILURE # static sub check_file { my $chkfilenm = $_[0]; if (!(defined($chkfilenm))) { trace("No value passed for the file name"); return FAILED; } if (!(-f $chkfilenm)) { trace ("The setup file \"$chkfilenm\" does not exist"); return FAILED; } # not checking perms, since they may not be valid for root return SUCCESS; } ####--------------------------------------------------------- #### Function for starting daemon/service # ARGS: 2 # ARG1: daemon to be started # ARG2: user as whom daemon/service needs to be started sub start_service { my $srv = $_[0]; my $user = $_[1]; my $status; # call OSD API $status = s_start_service ($srv); if ($status == SUCCESS) { trace("Started service '$srv'"); } else { trace("Failed to start service '$srv'"); } return $status; } sub isODA { my @OAKLIB = glob(catfile("/opt", "oracle", "extapi", "64", "oak", "liboak.*.so")); if (@OAKLIB) { return TRUE; } else { return FALSE; } } sub isHAIPsupported { # upgrade case only: the OLD_HAIP_ENABLED was set to ckpt and # also read from ckpt in the upgrade case # HAIP is disabled on ODA BMIaas if (isODABMIaaS()) { return FALSE; } if (isOPCDomu()) { return FALSE; } if (s_is_Exadata()) { trace("Exadata detected"); if ((! $CFG->UPGRADE) || ($CFG->UPGRADE && (! isOldVersion12102()))) { # HAIP is not supported on Exadata starting 12.2 # Enable HAIP when upgrading from 12.1.0.2 since ACFS requires it # ACFS is there on Exadata since 12.1.0.2 trace("HAIP is not supported"); return FALSE; } } if ($CFG->UPGRADE && ($CFG->OLD_HAIP_ENABLED == 0)) { return FALSE; } if((! $CFG->SIHA) && s_is_HAIP_supported() && (!is_dev_env() || ($ENV{'INSTALL_HAIP'} eq "true")) ) { return TRUE; } else { return FALSE; } } sub isHAIPNonFatal { if( ($ENV{'HAIP_NON_FATAL'} eq "true") || s_is_HAIP_NonFatal() ) { return TRUE; } else { return FALSE; } } sub isCHASupported #------------------------------------------------------------------------------ # Function: Find if CHA is supported # Args : # Returns : TRUE or FALSE # Notes : In 12.2, CHA is not added/enabled in the root script. #------------------------------------------------------------------------------ { return FALSE; } sub isCHMSupported #------------------------------------------------------------------------------ # Function: Find if CHM is supported # Args : # Returns : TRUE or FALSE #------------------------------------------------------------------------------ { # Initialize orachm module if (isOPCDomu() || isOPCDom0()) { trace("CHM resource is not supported in OPC environment"); return FALSE; } if (isODALite()) { trace("CHM resource is not supported in an ODA Lite env"); return FALSE; } $CFG->compCHM(oraClusterwareComp::orachm->new("CHM")); my $orachm = $CFG->compCHM; return $orachm->isSupported(); } sub isCHAConfigured #------------------------------------------------------------------------------ # Function: Find if CHA resource is already configured # Args : None # Returns : TRUE or FALSE # Notes : Used in case of add node #------------------------------------------------------------------------------ { my @output; my $run_as_owner = TRUE; my $ret; my $status; trace("Invoking srvctl config cha"); $status = srvctl($run_as_owner, "config cha"); if ($status == TRUE) { trace ("'srvctl config cha' ... success"); $ret = TRUE; } else { trace ("'srvctl config cha' error. Not configured."); $ret = FALSE; } return $ret; } sub isDSCSupported #------------------------------------------------------------------------------ # Function: Find if Domain Service Cluster (DSC) is supported # Args : # Returns : TRUE or FALSE # Notes : In 12.2, DSC is not supported on Windows #------------------------------------------------------------------------------ { my $ret = TRUE; if ($CFG->platform_family eq 'windows') { return FALSE; } return $ret; } sub isDSCConfigured #------------------------------------------------------------------------------ # Function: Find if this cluster is a Domain Services Cluster # Args : None # Returns : TRUE or FALSE # Notes : Used to decide to configure some components #------------------------------------------------------------------------------ { my $cluster_class; $cluster_class = getClusterClass(); if ($cluster_class eq '') { # Cluster class is not set. i.e. upgrade from 12.2 to later. Retrieve using # crsctl command $cluster_class = getClusterClass_crsctl(); } if ($cluster_class eq CLUSTER_CLASS_DOMAINSERVICES) { return TRUE; } else { return FALSE; } } sub checkServiceDown #--------------------------------------------------------------------- # Function: Check if service is down # Args : 1 - service # Returns : TRUE if service is down # FALSE if service is up #--------------------------------------------------------------------- { my $srv = $_[0]; my $crsHome = getCrsHome(); my $crsctl = crs_exec_path('crsctl', $crsHome); my $srv_down = FALSE; my @cmdout = (); my ($grep_val, $cmd, $node); # for OHASD, we need to grep for CRS-4639 if ($srv eq "ohasd") { $grep_val = "4639"; $cmd = "$crsctl check has"; } elsif ($srv eq "cluster") { $grep_val = "4639"; $cmd = "$crsctl check cluster -n " . $CFG->HOST; } elsif ($srv eq "css") { $grep_val = "4639|4530"; $cmd = "$crsctl check css"; } elsif ($srv eq "crs") { $grep_val = "4639|4535"; $cmd = "$crsctl check crs"; } my @chk = system_cmd_capture($cmd); my $rc = shift @chk; if ($grep_val) { @cmdout = grep(/$grep_val/, @chk); } # if scalar(@cmdout) > 0, we found the msg we were looking for if (($grep_val && scalar(@cmdout) > 0) || (!$grep_val && $rc == 0)) { $srv_down = TRUE; } return $srv_down; } sub checkClusterwareOnNode #--------------------------------------------------------------------- # Function: Check if clusterware services are up on node passed # Args : 1 - node to check clusterware on # Returns : TRUE if clusterware is up on the node # FALSE if clusterware is down on the node #--------------------------------------------------------------------- { my $node = $_[0]; my $crsHome = getCrsHome(); my $crsctl = crs_exec_path('crsctl', $crsHome); my $ret = FALSE; my $crs_up = FALSE; my $css_up = FALSE; my $evm_up = FALSE; my @cmdout = (); my $grep_crs = "^CRS-4537:"; my $grep_css = "^CRS-4529:"; my $grep_evm = "^CRS-4533:"; my $cmd = "$crsctl check cluster -n " . $node; my @chk = system_cmd_capture($cmd); my $rc = shift @chk; if ($rc != 0) { return $ret; } # if scalar(@cmdout) > 0, we found the msg we were looking for @cmdout = grep(/$grep_crs/, @chk); if (scalar(@cmdout) > 0) { $crs_up = TRUE; } # if scalar(@cmdout) > 0, we found the msg we were looking for @cmdout = grep(/$grep_css/, @chk); if (scalar(@cmdout) > 0) { $css_up = TRUE; } # if scalar(@cmdout) > 0, we found the msg we were looking for @cmdout = grep(/$grep_evm/, @chk); if (scalar(@cmdout) > 0) { $evm_up = TRUE; } if ($crs_up && $css_up && $evm_up) { $ret = TRUE; trace("Clusterware is up on node $node"); } return $ret; } sub isClusterExtended #--------------------------------------------------------------------- # Function: Check if the Cluster is Extended # Args : # Returns : TRUE if cluster is extended # FALSE otherwise #--------------------------------------------------------------------- { my @output; my $crsctl = crs_exec_path('crsctl'); my @cmd = ($crsctl, 'get', 'cluster', 'extended'); my $grep_ext = "^CRS-6579:"; my $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @cmd); if ($rc != 0) { trace("Failed to run command @cmd, output: @output"); return FALSE; } if (scalar(grep(/$grep_ext/, @output)) > 0) { return FALSE; } trace("The cluster is Extended"); return TRUE; } sub isSiteConfigured #--------------------------------------------------------------------- # Function: Checks if the site name passed is configured in OCR/OLR # Args : 1 - site name to query # Returns : TRUE if the site is configured # FALSE Otherwise #--------------------------------------------------------------------- { my @output; my $site = $_[0]; my $crsctl = crs_exec_path('crsctl'); my @cmd = ($crsctl, 'query', 'cluster', 'site', $site); my $grep_site = "^CRS-10508:"; my $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @cmd); if ($rc != 0) { trace("Failed to run command @cmd, output: @output"); return FALSE; } if (scalar(grep(/$grep_site/, @output)) > 0) { trace("The site $site is not configured"); return FALSE; } trace("The site is configured"); return TRUE; } sub isNodeMappedToSite #--------------------------------------------------------------------- # Function: Checks if the site name passed is mapped to local node # Args : 1 - site name to query # Returns : TRUE if the site is mapped to local node # FALSE Otherwise #--------------------------------------------------------------------- { my @output; my $site = $_[0]; my $node = $CFG->HOST; my $crsctl = crs_exec_path('crsctl'); my @cmd = ($crsctl, 'query', 'cluster', 'site', $site); my $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @cmd); if ($rc != 0) { trace("Failed to run command @cmd, output: @output"); return FALSE; } if (scalar(grep(/$node/, @output)) > 0) { trace("The site node $node is mapped to site $site"); return TRUE; } trace("The node $node is not mapped to site $site"); return FALSE; } sub getASMInstanceCount #--------------------------------------------------------------------- # Function: Queries ASM instance count in the cluster # Args : # Returns : current ASM instance count # Notes : Dies in case of error while querying instance count #--------------------------------------------------------------------- { my $run_as_owner = TRUE; my @output = (); my $srvctl_args = "config asm -inner 1"; my $cardinality; my $found; my @tokens; my $status = srvctl_capture($run_as_owner, \@output, $srvctl_args, $CFG->ORA_CRS_HOME); if (($status == 0) || ($status == 2)) { $found = FALSE; @tokens = split(/[=\s{}]+/, $output[0]); foreach my $token (@tokens) { if ($found) { $cardinality = $token; last; # break } if ($token eq "count") { $found = TRUE; } } if (!$found) { trace("Could not find ASM instance count in output. Output: @output"); die(dieformat(5110, "4")); } trace("ASM cardinality is: $cardinality"); } else { print_lines(@output); print_error(180, "srvctl config asm"); die(dieformat(5101)); } return $cardinality; } sub isListenerConfigured #--------------------------------------------------------------------- # Function: Checks if listener passed is configured # Args : listener name, fatal (set to true to die in case of error) # Returns : TRUE if specified listener is configured, FALSE otherwise # Notes : #--------------------------------------------------------------------- { my ($listener) = $_[0]; my ($fatal) = $_[1]; my $run_as_owner = TRUE; my $cmd = "config listener -listener $listener"; my @out = (); my $status = srvctl_capture($run_as_owner, \@out, $cmd, $CFG->ORA_CRS_HOME); if (($status != 0) && ($status != 2)) { # If listener is not configured $status is set to 1, look at the output # PRCR-1001 : Resource does not exist if (grep(/PRCR-1001/, @out)) { trace("Listener $listener is not configured"); return FALSE; } print_lines(@out); trace("Failed to run command: srvctl $cmd"); if ($fatal) { die(dieformat(180, "srvctl $cmd")); } return FALSE; } trace("Listener $listener is configured"); return TRUE; } sub stop_resource { my $CRSCTL = crs_exec_path('crsctl'); my @cmd = ($CRSCTL, 'stop', 'resource', (@_)); my $success = TRUE; my @out = system_cmd_capture_noprint(@cmd); my $status = shift @out; if ($status == 0 || # stop successful scalar(grep(/CRS-0*2500/i, @out)) != 0) { # resource not up trace("Resource '@_' was not online but was successfully stopped"); } elsif (check_service($_[0], 1)) { # check if service is still running trace("Stop of resource \"@_\" failed\n".join("\n", @out)); print_error(116, $_[0]); $success = FALSE; } return $success; } sub stop_diskmon { my $CRSCTL = crs_exec_path('crsctl'); my $success = TRUE; # no diskmon in windows if ($CFG->platform_family eq "windows") { return TRUE; } if (check_service("ora.diskmon", 2)) { my @output = system_cmd_capture($CRSCTL, "stop", "resource", "ora.diskmon", "-init"); my $status = shift @output; if ($status != 0 && !scalar(grep(/CRS\-2500/, @output))) { trace("Stop of resource \"ora.diskmon\" failed\n".join("\n", @output)); print_error(116, "ora.diskmon"); $success = FALSE; } } return $success; } sub start_resource { my $crsHome = getCrsHome(); #it's configured crs home in case of partial downgrade my $CRSCTL = catfile ($crsHome, "bin", "crsctl"); my @cmd = ($CRSCTL, 'start', 'resource', (@_)); my $success = TRUE; my @out = system_cmd_capture_noprint(@cmd); my $status = shift @out; if ($status == 0) { if (scalar(@out) >0 ) { trace(join("\n> ", ("Command output:", @out)), "\n>End Command output"); } trace("Start of resource \"$_[0]\" Succeeded"); } elsif (scalar(grep(/CRS-0*5702/i, @out)) != 0) { # resource already up trace("The resource \"$_[0]\" was already running"); } elsif ($status != 0) { if (scalar(@out) >0 ) { trace(join("\n> ", ("Command output:", @out)), "\n>End Command output"); } if (!check_service($_[0], 10)) { trace("Start of resource \"$_[0]\" failed\n".join("\n", @out)); print_error(115, $_[0]); $success = FALSE; } else { trace("The resource \"$_[0]\" is already running"); } } return $success; } sub start_HAIP { if ( ! start_resource("ora.cluster_interconnect.haip", "-init") ) { trace("Failed to startup HAIP"); # if HAIP is not considered fatal on the platform then disable # further startup attempts my $crsctl = catfile($CFG->ORA_CRS_HOME, 'bin', 'crsctl'); if ( isHAIPNonFatal() ) { my @cmd = ($crsctl, 'modify', 'res', 'ora.cluster_interconnect.haip', '-attr', "\"ENABLED=0\"", '-init'); my $status = system_cmd(@cmd); if ( 0 != $status ) { trace("Failed to disable HAIP startup: $status"); die(dieformat(246)); } else { trace("HAIP startup not considered fatal, continuing"); } } else { die(dieformat(246)); } } } sub enable_HAIP { trace("enable HAIP if supported"); if(isHAIPsupported()) { my $crsctl = catfile($CFG->ORA_CRS_HOME, 'bin', 'crsctl'); my @cmd = ($crsctl, 'modify', 'res', 'ora.cluster_interconnect.haip', '-attr', "\"ENABLED=1\"", '-init'); my $status; $status = system_cmd(@cmd); if ( 0 != $status ) { trace("Failed to enable HAIP startup: $status"); } if ( ! start_resource("ora.cluster_interconnect.haip", "-init") ) { trace("Failed to startup HAIP"); # if HAIP is not considered fatal on the platform then disable # further startup attempts if( isHAIPNonFatal() ) { my @cmd = ($crsctl, 'modify', 'res', 'ora.cluster_interconnect.haip', '-attr', "\"ENABLED=0\"", '-init'); $status = system_cmd(@cmd); if( 0 != $status ) { trace("Failed to disable HAIP startup: $status"); } } else { die(dieformat(246)); } } } } # TODO: We don't need this when upgrade stops using this sub start_clusterware { my $level = $_[0]; my $status = SUCCESS; my $orachm = $CFG->compCHM; my %stack_start_levels = (START_STACK_MDNSD => 'Oracle clusterware daemons up to MDNSD', START_STACK_GPNPD => 'Oracle clusterware daemons up to GPNPD', START_STACK_GIPCD => 'Oracle clusterware daemons up to GIPCD', START_STACK_CHM => 'Oracle clusterware daemons up to CHM/OS', START_STACK_CTSSD => 'Oracle clusterware daemons up to CTSSD', START_STACK_CSSD => 'Oracle clusterware daemons up to CSSD', START_STACK_ASM => 'Oracle clusterware daemons up to ASM', START_STACK_ALL => 'the Oracle clusterware stack' ); trace ("Starting", $stack_start_levels{$level}); if (($level < START_STACK_EVMD || start_resource("ora.evmd", "-init")) && ($level < START_STACK_MDNSD || start_resource("ora.mdnsd", "-init")) && ($level < START_STACK_GPNPD || (start_resource("ora.gpnpd", "-init") && wait_for_gpnpd_start())) && ($level < START_STACK_GIPCD || start_resource("ora.gipcd", "-init")) && ($level < START_STACK_CHM || !$orachm->isSupported() || start_resource("ora.crf", "-init")) && ($level < START_STACK_CSSD || CSS_start_clustered()) && ($level < START_STACK_CTSSD || start_resource("ora.ctssd", "-init", "-env", "USR_ORA_ENV=CTSS_REBOOT=TRUE")) && ($level < START_STACK_ASM || !$CFG->ASM_STORAGE_USED || # if ASM used, start it start_resource("ora.asm", "-init")) && ($level < START_STACK_CRSD || $level < START_STACK_ALL || start_resource("ora.crsd", "-init"))) { trace ("Successfully started requested Oracle stack daemons"); } else { print_error(117); $status = FAILED; } return $status; } =head2 get_crs_version Gets parsed version numbers of active CRS version. Version is a result of "crsctl query crs activeversion" command. Stack (CRS) must be up for this to succeed. =head3 Parameters string with crsctl home location. If undef, then current home is used. =head3 Returns =head4 returns an array of version numbers major to minor. If error occurred, all numbers will be 0. Error will be printed. =cut sub get_crs_version { my $home = $_[0]; my @ver = (0, 0, 0, 0, 0); my ($crsctl); my $verstring; if (! $home) { $crsctl = crs_exec_path('crsctl'); } else { $crsctl = catfile($home, 'bin', 'crsctl' ); } # run "crsctl query crs activeversion" -- stack must be up # Example output: # Oracle Clusterware active version on the cluster is [11.2.0.0.2] my @cmd = ($crsctl, 'query', 'crs', 'activeversion'); my @out = system_cmd_capture(@cmd); my $rc = shift @out; # if succeeded, parse to ver numbers, output must be a single line, # version is 5 numbers, major to minor (see above) if ($rc == 0) { $verstring = getVerInfo($out[0]); trace( "Got CRS active version: ".join('.', $verstring) ); } @ver = split(/\./, $verstring); trace( "Got CRS active version: ".join('.', @ver) ); return @ver; } sub isCVUConfigured #------------------------------------------------------------------------------- # Function: Find if CVU resource is already configured # Args : oldCrsHome # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- { my $status = SUCCESS; my $rc; my $oldCrsHome = shift; my $run_as_owner = FALSE; my @output = (); my $srvctl_args = "config cvu"; trace("Invoking srvctl config cvu"); $rc = srvctl_capture($run_as_owner, \@output, $srvctl_args, $oldCrsHome); if (($rc != 0) && ($rc != 2)) { if(scalar(grep(/PRKO\-2700/, @output)) > 0) { trace("CVU resource does not exist"); } else { print_lines(@output); trace ("srvctl $srvctl_args failed with status $rc"); die(dieformat(180, "srvctl $srvctl_args")); } $status = FAILED; } else { trace("CVU resource exists"); } return $status; } sub get_CVU_checkInterval #------------------------------------------------------------------------------- # Function: get CVU resources check_interval value # Args : oldCrsHome # Returns : CVU check_interval value if successful. 0 in case of failure #------------------------------------------------------------------------------- { my $checkIntervalStr = "0"; my $oldCrsHome = shift; my $outline; my @out; my $rc; my $run_as_owner = FALSE; trace("Invoking srvctl config cvu"); $rc = srvctl_capture($run_as_owner, \@out, "config cvu -S 1", $oldCrsHome); if (($rc != 0) && ($rc != 2)) { print_lines(@out); trace("CVU resource config could not be obtained"); } else { trace("CVU resource exist"); # Example output: # #@=result[0]: res_name={ora.cvu} check_interval={360} foreach my $line (@out) { $line = trim($line); if ($line =~ m/^#@=result/) { trace ("assigining $line to outline"); $outline = $line } } $outline =~ m/\{(\d+)\}*$/; if ($1 && $1 ne "") { $checkIntervalStr = $1; } } trace("check interval is : [$checkIntervalStr]"); return $checkIntervalStr; } sub isRolling { my $isrolling = TRUE; if ($CFG->params('ISROLLING') =~ m/false/i) { $isrolling = FALSE; } trace("Rolling upgrade is set to $isrolling"); return $isrolling; } =head1 EXPORTED FUNCTIONS =head2 getcrsrelver1 Use 'sqlplus -V' to get the release version of the software given Oracle home; =head3 Parameters [1] Oracle home from which 'sqlplus' is invoked; If undef, the current GI home is used. =head3 Returns A version sting in the format A.B.C.D.E =cut sub getcrsrelver1 { my $home = $_[0]; my ($sqlplus); my $verstring; if (! defined $home) { $sqlplus = crs_exec_path('sqlplus'); } else { $sqlplus = catfile($home, 'bin', 'sqlplus' ); } my @cmd = ($sqlplus, '-V'); my @out = system_cmd_capture(@cmd); my $rc = shift @out; # if succeeded, parse to ver numbers, output must be a single line, # version is 5 numbers, major to minor (see above) if ($rc == 0) { my ($s1, $s2, $s3, $s4) = split(/ /, $out[1]); chomp $s3; $verstring = $s3; trace( "Got CRS release version: $verstring "); } else { error ("@cmd ... failed rc=$rc with message:\n @out \n"); } return $verstring; } sub getVerInfo #------------------------------------------------------------------------------- # Function: Get the the Version from the String Passed # Args : VerString # Returns : VerInfo #------------------------------------------------------------------------------- { my $verstring = $_[0]; my @verarray = (0, 0, 0, 0, 0); my $verinfo; trace("Version String passed is: [$verstring]"); if ($verstring) { $verstring =~ m/\[?(\d*)\.(\d*)\.(\d*)\.(\d*)\.(\d*)\]?.*$/; @verarray = ($1, $2, $3, $4, $5); $verinfo = join('.',@verarray); trace("Version Info returned is : [$verinfo]"); } else { trace("Null Version String is Passed to getVerInfo"); } return $verinfo; } sub waitForVipRes #------------------------------------------------------------------------------- # Function: Wait for VIP resource to exist # Args : none # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- { my $host = $CFG->HOST; my $retries = 12; my $vip_exists = FALSE; my $run_as_owner = FALSE; my ($rc, @output); while ($retries) { $rc = srvctl_capture($run_as_owner, \@output, "status vip -n $host"); if (($rc != 0) && ($rc != 2)) { print_lines(@output); } my @cmdout = grep(/(^PRKO-2165)/, @output); if ($rc == 0 && scalar(@cmdout) == 0) { $vip_exists = TRUE; last; } trace ("Waiting for VIP resource"); sleep (5); $retries--; } if ($vip_exists) { trace ("VIP resource exists"); } else { trace ("Timed out waiting for VIP resource"); } return $vip_exists; } sub isFirstNodeToStart ###################################################################### # Returns: # FALSE if node is not first to start # TRUE if node is first to start ###################################################################### { my $isFirst = FALSE; # A rim node can never be a first node. The first node is always a hub node. if (isRimNode()) { return FALSE; } return isFirstNodeToConfig($CFG->HOST); } sub getSubnets #--------------------------------------------------------------------- # Function: Get subnets from $NETWORKS of the network type $net_type # Args : [0] $NETWORKS # [1] $net_type # Returns : Array of subnets. #--------------------------------------------------------------------- { my $networks = $_[0]; my $net_type = $_[1]; my ($eth, $txt, $subnet, @subnetList); if ($networks =~ /\b$net_type\b/) { my @network_ifs = split (/,/, $networks); foreach my $network_if (@network_ifs) { if ($network_if =~ /\b$net_type\b/) { # strip out "eth*" and ":$net_type" ($eth, $txt) = split (/\//, $network_if); ($subnet, $txt) = split (/:$net_type/, $txt); push @subnetList, $subnet; } } } return @subnetList; } sub modify_networks #--------------------------------------------------------------------- # Function: Get subnets and interfaces of the network type # cluster_interconnect and set that n/w with :cluster_interconnect,asm # Args : [0] $NETWORKS, which is the output of 'oifcfg getif' like: # eth0 10.244.32.0 global public # eth2 10.244.120.0 global cluster_interconnect # Returns : Array of subnets with network type cluster_interconnect,asm. #--------------------------------------------------------------------- { my (@txt, @subnetList, $subnet); my $net_type = 'cluster_interconnect'; foreach my $network_if (@_) { if ($network_if =~ /\b$net_type\b/) { # The parenthesis () mean we keep the spaces we match @txt = split(/(\s+)/, $network_if); pop @txt; # remove the last element 'cluster_interconnect' pop @txt; # remove spaces pop @txt; # remove 'global' pop @txt; # remove spaces my $net = pop @txt; # return the subnet IP address pop @txt; # remove spaces # The remaining is the interface name my $netif = join("", @txt); # Enclose the interface name with double quotes in case # it contains spaces $subnet = "\"$netif\"\/$net:cluster_interconnect,asm"; trace("The network config after conversion: [$subnet]"); push @subnetList, $subnet; } } return @subnetList; } sub get_ons_port #--------------------------------------------------------------------- # Function: Get the ONS port used by the old version crs #--------------------------------------------------------------------- { my $node = $_[0]; my ($home, $ONSCONFFILE, $Name, $portnum, $useocr, $localport, $remoteport, $locport, $remport, $cmd, $ocrkey, $line); my $idx = 0; my @buf2; $home = $CFG->OLD_CRS_HOME; $ONSCONFFILE = catfile( $home, 'opmn' , 'conf', 'ons.config'); trace("The ons conf file location is: $ONSCONFFILE"); open(FONS, $ONSCONFFILE) or trace("Could not open \"$ONSCONFFILE\": $!"); while() { if(/^useocr\=on\b/i) { $useocr=$_; } if(/^remoteport\b/i) { $remoteport=$_; } if(/^localport\b/i) { $localport=$_; } } close (FONS); #get the remote port if (defined($useocr)) { trace("useocr is on. get the remote port from OCR"); $cmd = catfile ($CFG->ORA_CRS_HOME, 'bin', 'ocrdump' ); $ocrkey = "DATABASE.ONS_HOSTS"; my @args = ($cmd, '-stdout', '-keyname', $ocrkey); my @out = system_cmd_capture(@args); if ($CFG->DEBUG) { trace("ocrdump output: @out"); } my $rc = shift @out; foreach $line (@out) { if ($line =~ m/DATABASE\.ONS_HOSTS\.$node.*\.PORT\]/i) { @buf2 = $out[$idx+1]; last; } $idx++; } if ($CFG->DEBUG) { trace("ocrdump output for port information is: @buf2"); } if (scalar(@buf2) != 0) { ($Name, $portnum) = split(/:/, $buf2[0]); } $remport = trim($portnum); } else { if (defined($remoteport)) { ($Name, $portnum) = split(/=/, $remoteport); $remport = trim($portnum); } } #always get the localport from ons.config if present if (defined($localport)) { ($Name, $portnum) = split(/=/, $localport); $locport = trim($portnum); } #set 11.2 default values for ons port if (! $locport) { trace("setting default port for ons localport"); $locport = "6100"; } if (! $remport) { trace("setting default port for ons remoteport"); $remport = "6200"; } trace("Local port=$locport"); trace("Remote port=$remport"); if($locport == $remport){ die(dieformat(485, $locport, $remport)); } else { return ($locport, $remport); } } sub setNetworkInterface { my $networks = $_[0]; my $success = TRUE; my $oifcfg = catfile($CFG->params('ORACLE_HOME'), 'bin', 'oifcfg'); if (! $networks) { $networks = oifcfgNetworks(); } my @out = system_cmd_capture($oifcfg, "setif -global", $networks); my $rc = shift @out; if ($rc == 0) { trace("$oifcfg setif -global $networks successful"); } else { trace("$oifcfg setif -global $networks failed rc = $rc"); print_lines(@out); print_error(180, "$oifcfg setif -global $networks"); $success = FALSE; } return $success; } =head2 oifcfgNetworks Converts NETWROKS to a list that can be accepted by oifcfg E.g., from NETWORKS="eth0"/1.2.3.4:public,"eth1"/2.3.4.5:cluster_interconnect,"eth1"/2.3.4.5:asm to "eth0"/1.2.3.4:public "eth1"/2.3.4.5:cluster_interconnect,asm =head3 Parameters None =head3 Returns A space-separated list passed to oifcfg =cut sub oifcfgNetworks { my @networks = split(/,/, $CFG->params('NETWORKS')); my %nets; my $netSpecs; foreach my $elem (@networks) { # In a test environment oifcfg setting same interface to pvt and public # throws an error. For this reason a check is placed to skip setting # public interface in dev environment only. # oifcfg setif -global eth2/10.232.192.0:public,cluster_interconnect,asm # PRIF-53: Invalid type combination specified for interface [eth2] # Remove this check after Bug #13806475 is fixed. #next if (( $elem !~ /cluster_interconnect/i) && ( is_dev_env() ) && ( $CFG->params('NETWORKS') =~ /asm/ )); # Look for the last colon to separate / and # my @net = split(/:(public|cluster_interconnect|asm)/, $elem); my $type = $nets{$net[0]}; if ($type) { # Commbine multile interface types for the same / # using commas as separator $type = $type.",".$net[1]; $nets{$net[0]} = $type; } else { $nets{$net[0]} = $net[1]; } } my @array; while (my($key, $value) = each % nets) { my $spec = $key.":".$value; push(@array, $spec); } # Create a space-separated list that is passed to oifcfg if (scalar(@array) > 0) { $netSpecs = join(" ", sort(@array)); } return $netSpecs; } =head2 compact_instlststr Converts a installer-prepared list containing duplicates to a compact version, which is required by sub instlststr_to_gpnptoolargs E.g., from "eth0"/1.2.3.4:public,"eth1"/2.3.4.5:cluster_interconnect,"eth1"/2.3.4.5:asm to "eth0"/1.2.3.4:public,"eth1"/2.3.4.5:cluster_interconnect|asm =head3 Parameters A installer-prepared list string =head3 Returns A compact version of install-style network info list that is passed to instlststr_to_gpnptoolargs() =cut sub compact_instlststr { my $pNets = $_[0]; $pNets = ($pNets) ? ($pNets) : ($CFG->params('NETWORKS')); trace("Ready to parse: $pNets"); my @networks = split(/,/, $pNets); my %nets; my $netSpecs; foreach my $elem (@networks) { # Look for the last colon to separate / and # # Some srgs/lrgs populate NETWORKS with compact format, e.g., in srgrsc: # NETWORKS=eth0/10.232.168.0:cluster_interconnect|public # Do nothing and just return the original one in this case if ($elem =~ m/(^.+)[:]((public|cluster_interconnect|asm)([|](public|cluster_interconnect|asm))+)$/) { trace("The original one is already a compact version"); return $pNets; } my @net = split(/:(public|cluster_interconnect|asm)/, $elem); my $type = $nets{$net[0]}; if ($type) { # Commbine multile interface types for the same / # using '|' as separator $type = $type."|".$net[1]; $nets{$net[0]} = $type; } else { $nets{$net[0]} = $net[1]; } } my @array; while (my($key, $value) = each % nets) { my $spec = $key.":".$value; push(@array, $spec); } # Create a comma-separated list that is passed to instlststr_to_gpnptoolargs() if (scalar(@array) > 0) { $netSpecs = join(",", sort(@array)); } return $netSpecs; } sub start_CVU #------------------------------------------------------------------------------ # Function: Start the cvu resource # Args : none #------------------------------------------------------------------------------- { my $run_as_owner = TRUE; my $status = srvctl($run_as_owner, "start cvu"); if (${status}) { trace ("start cvu ... success"); } else { print_error(112); return FALSE; } return TRUE; } sub remove_checkpoints #------------------------------------------------------------------------------- # Function: remove the local checkpoint file # Args : None # Returns : None #------------------------------------------------------------------------------- { my $ckpt_file; my $host = tolower_host(); my $ckpt_filename = "ckptGridHA_".$host.".xml"; $ckpt_file = catfile ($CFG->params('ORACLE_BASE'), "crsdata", $host, "crsconfig", $ckpt_filename); trace ("Removing the local checkpoint file $ckpt_file"); s_remove_file ($ckpt_file); } sub remove_checkpoint_index #------------------------------------------------------------------------------- # Function: remove the local checkpoint index file # Args : None # Returns : None #------------------------------------------------------------------------------- { my $ckpt_index_file; my $ckpt_index_filename = "index.xml"; my $host = tolower_host(); $ckpt_index_file = catfile ($CFG->params('ORACLE_BASE'), $host, "checkpoints", "crsconfig", $ckpt_index_filename); trace ("Removing the local checkpoint index file $ckpt_index_file"); s_remove_file ($ckpt_index_file); } sub remove_global_checkpoints #------------------------------------------------------------------------------- # Function: remove the global checkpoint file # Args : None # Returns : None #------------------------------------------------------------------------------- { my $global_ckpt_filename = "ckptGridHA_global.xml"; my $ckpt_file = catfile ($CFG->params('ORACLE_BASE'), "crsdata", "\@global", "crsconfig", $global_ckpt_filename); my $node_list = $CFG->params('NODE_NAME_LIST'); my @program = ('-rmfile', $ckpt_file, $node_list); my $args_str = join(' ', @program); my $crshome = $CFG->ORA_CRS_HOME; my $run_as_user = TRUE; trace ("Removing the global checkpoint file $ckpt_file"); my ($rc, @output) = cluutil($run_as_user, $args_str, $crshome); if (0 != $rc) { print_error(641, $ckpt_file, $node_list); } } sub remove_global_checkpoint_index #------------------------------------------------------------------------------- # Function: remove the global checkpoint index file # Args : None # Returns : None #------------------------------------------------------------------------------- { my $ckpt_index_filename = "index.xml"; my $ckpt_index_file = catfile ($CFG->params('ORACLE_BASE'), "crsdata", "\@global", "crsconfig", $ckpt_index_filename); my $node_list = $CFG->params('NODE_NAME_LIST'); my @program = ('-rmfile', $ckpt_index_file, $node_list); my $args_str = join(' ', @program); my $crshome = $CFG->ORA_CRS_HOME; my $run_as_user = TRUE; trace ("Removing the global checkpoint index file $ckpt_index_file"); my ($rc, @output) = cluutil($run_as_user, $args_str, $crshome); if (0 != $rc) { print_error(641, $ckpt_index_file, $node_list); } } sub remove_config_params_file #------------------------------------------------------------------------------- # Function: remove the 'crsgenconfig_params' file # Args : None # Returns : None #------------------------------------------------------------------------------- { my $crshome = $CFG->ORA_CRS_HOME; my $paramFile = catfile($crshome, "crs", "install", "crsgenconfig_params"); my $node_list = $CFG->params('NODE_NAME_LIST'); my @program = ('-rmfile', $paramFile, $node_list); my $args_str = join(' ', @program); my $run_as_user = TRUE; trace ("Removing the 'crsgenconfig_params' file $paramFile"); my ($rc, @output) = cluutil($run_as_user, $args_str, $crshome); if (0 != $rc) { print_error(641, $paramFile, $node_list); } } sub isCkptFileExists { my $host = tolower_host(); my $ckpt_filename = "ckptGridHA_".$host.".xml"; my $ckpt_file = catfile ($CFG->params('ORACLE_BASE'), "crsdata", $host, "crsconfig", $ckpt_filename); trace ("Checking the existence of the checkpoint file $ckpt_file"); if (-e $ckpt_file) { trace("The checkpoint file $ckpt_file exists."); return TRUE; } else { trace("The checkpoint file $ckpt_file not found."); return FALSE; } } sub isASMExists #------------------------------------------------------------------------------- # Function: Check if ASM exists # Args : none # Returns : TRUE if exists # FALSE if not exists #------------------------------------------------------------------------------- { my $crs_home = $CFG->ORA_CRS_HOME; my $host = $CFG->HOST; my $crs_stat = catfile ($crs_home, 'bin', 'crs_stat'); open (CRSSTAT, "$crs_stat |"); # temporarely using crs_stat to find pre 11.2 ASM # grep "ora.$host*asm" my @txt = grep /ora.$host.*asm/, ; close (CRSSTAT); if (scalar(@txt) == 0) { trace ("check ASM exists done and ASM does not exist"); return FALSE; } return TRUE; } =head2 getNodeSites Get the number of compute/node sites in an extended cluster =head3 Parameters None =head3 Returns count of unique sites listed in HUB_NODE_LIST parameter =cut sub getNodeSites { my $HUB_NODE_LIST = $CFG->params('HUB_NODE_LIST'); my @HUB_NODES_N_SITES = split (/,/, $HUB_NODE_LIST); my @sites; foreach my $node_n_site (@HUB_NODES_N_SITES) { if ($node_n_site =~ /:/) { my @item = split (/:/, $node_n_site); my $site = trim($item[1]); if (scalar(grep(/$site/, @sites)) == 0) { push(@sites, $site); } } } return scalar(@sites); } sub add_ASM { my $run_as_owner = TRUE; my $asmPwdFile = $CFG->ASM_PWD_FILE; if (($CFG->ASM_STORAGE_USED) && (! $asmPwdFile)) { die(dieformat(379)); } if (isLegacyASM()) { trace("add asm ..."); my $cmd; if ($CFG->ASM_STORAGE_USED) { trace("The path for ASM password file is $asmPwdFile"); $cmd = "add asm -pwfile ${asmPwdFile}"; } else { $cmd = "add asm"; } srvctl($run_as_owner, $cmd) || exit(1); } if (isNearASM()) { if(lc($CFG->params('EXTENDED_CLUSTER')) eq CLUSTER_EXTENDED) { # default ASM cardinality is 2* number of unique sites in # HUB_NODE_LIST parameter my $count = getNodeSites()*2; trace("Setting ASM cardinality $count for extended cluster"); srvctl($run_as_owner, "add asm -flex -count $count -pwfile ${asmPwdFile}") || die(dieformat(371)); } else { srvctl($run_as_owner, "add asm -flex -pwfile ${asmPwdFile}") || die(dieformat(371)); } my @asmsubnets = getSubnets($CFG->params('NETWORKS'), 'asm'); add_ASM_listener(\@asmsubnets); if ($CFG->ACFSSupported) { trace("add asm -proxy ..."); srvctl($run_as_owner, "add asm -proxy") || die(dieformat(622)); trace("disable asm -proxy"); srvctl($run_as_owner, "disable asm -proxy") || die(dieformat(623)); } } # isNearASM } sub add_ASM_listener { my $asmsubnets = $_[0]; trace("add asm listeners"); my $aux = 0; # used for the listeners' names my @subnets = @$asmsubnets; my %hash_subnets; @hash_subnets{@subnets} = (); my @uniq_subnets = sort keys %hash_subnets; my $status; my ($asmlsnr, $srvctlCmd, @output); my $run_as_owner = TRUE; foreach my $subnet (@uniq_subnets) { $asmlsnr = "asmnet" . ++$aux . "lsnr"; if (isOPCDomu()) { $srvctlCmd = "add listener -asmlistener -listener $asmlsnr"; } else { $srvctlCmd = "add listener -asmlistener -listener $asmlsnr -subnet $subnet"; } $status = srvctl_capture($run_as_owner, \@output, $srvctlCmd); # do not fail if PRCN-3081 occurs as there is already a listener for that # subnet. only one listener is required per subnet. if ($status == 0 || $status == 2 || scalar(grep(/PRCN-3081/, @output)) > 0) { trace("ASM Listener $asmlsnr created"); } else { print_lines(@output); die(dieformat(180, "srvctl $srvctlCmd")); } } # for } sub createDiskgroupRes #--------------------------------------------------------------------- # Function: Create ASM diskgroup resource # Args : none # Returns : TRUE if success # FALSE if failed #--------------------------------------------------------------------- { my $cmd; trace ("Adding ASM diskgroup resource"); # convert CDATA_DISK_GROUP to upper-case my $crsctl = crs_exec_path('crsctl'); my $CDATA_DISK_GROUP = uc($CFG->params('CDATA_DISK_GROUP')); if ($CDATA_DISK_GROUP =~ /\$/) { # if diskgroup contains '$', put single-quotes around it quoteDiskGroup($CDATA_DISK_GROUP); $cmd = "$crsctl create diskgroup '$CDATA_DISK_GROUP'"; } else { $cmd = "$crsctl create diskgroup $CDATA_DISK_GROUP"; } my $status = run_as_user ($CFG->params('ORACLE_OWNER'), $cmd); if ($status == 0) { trace ("create diskgroup $CDATA_DISK_GROUP ... success"); } else { print_error(182, $CDATA_DISK_GROUP); return FALSE; } trace ("Successfully created disk group resource"); return TRUE; } sub quoteDiskGroup #------------------------------------------------------------------------------- # Function: Check if asm disk group contains '$' # Args : diskgroup # Returns : diskgroup w/ '\' character #------------------------------------------------------------------------------- { if ($_[0]) { $_[0] =~ s/\$/\\\$/g; } } sub add_GNS #--------------------------------------------------------------------- # Function: Add GNS # Returns : TRUE if success # FALSE if failed #--------------------------------------------------------------------- { if ($CFG->params('GNS_CONF') ne "true") { trace ("GNS is not to be configured - skipping"); return TRUE; } my $srvctl_func = \&srvctl; if ((($^O eq "linux") || ($^O eq "solaris")) && ($CFG->AUTO)) { trace("Call srvctl_tty"); $srvctl_func = \&srvctl_tty; } my $run_as_owner = FALSE; my ($gns_type, $credentials, $address_list, $domain_list) = ($CFG->params('GNS_TYPE'), $CFG->params('GNS_CREDENTIALS'), $CFG->params('GNS_ADDR_LIST'), $CFG->params('GNS_DOMAIN_LIST')); if("shared" eq lc($CFG->params('GNS_TYPE'))) { if ($credentials ne undef) { #Shared GNS is to be added. my $status = srvctl($run_as_owner,"add gns -clientdata $credentials"); if (TRUE == ${status}) { trace ("add gns -clientdata $credentials ... passed"); } else { return FALSE; } } else { trace("GNS credentials file not provided"); print_error(300,'GNS_CREDENTIALS'); return FALSE; } } elsif($domain_list ne undef){ #A domain is provided. my $status = &$srvctl_func($run_as_owner, "add gns -i ${address_list} -d ${domain_list}"); if (TRUE == ${status}) { trace ("add gns -i $address_list -d $domain_list ... passed"); } else { return FALSE; } } else { #No domain is provided. my $status = &$srvctl_func($run_as_owner, "add gns -i $address_list"); if (TRUE == ${status}) { trace ("add gns -i $address_list ... passed"); } else { return FALSE; } } return TRUE; } sub add_scan { my $isDHCP = $_[0]; my $scanName = $CFG->params('SCAN_NAME'); # append the GNS subdomain to the scan name only if dhcp (16532618) if ($isDHCP && ("shared" eq lc($CFG->params('GNS_TYPE')))) { my @output; my $rc = srvctl_capture(FALSE, \@output, "config gns -S 1"); if ((0 != $rc) && ($rc != 2)) { print_lines(@output); die(dieformat(180, "srvctl config gns -S 1")); } else { my @line = grep(/gns_subdomain=/, @output); if (scalar(@line) > 0) { trace(" @line"); my $result = (split(/:\s+/, $line[0]))[1]; trace("result: $result"); $result =~ s/{//g; $result =~ s/}//g; my $domain = (split(/=/, $result))[1]; trace("Client GNS sub-domain: $domain"); $scanName = $scanName."\.".$domain; trace("SCAN name with sub-domain suffix: $scanName"); } } } my $srvctl_func = \&srvctl; if ((($^O eq "linux") || ($^O eq "solaris")) && ($CFG->AUTO)) { trace("Call srvctl_tty"); $srvctl_func = \&srvctl_tty; } my $run_as_owner = FALSE; my $cmd; if (isOPCDomu() || isODALite() || isODASIP()) { my $nodelist = $CFG->params('NODE_NAME_LIST'); $cmd = "add scan -scanname $scanName -nodenames $nodelist"; } else { $cmd = "add scan -n $scanName"; } my $status = &$srvctl_func($run_as_owner, $cmd); if (${status}) { trace ("add scan=" . $scanName . " ... success"); } else { return FALSE; } return TRUE; } sub add_scan_listener { my $run_as_owner = TRUE; my $status = srvctl($run_as_owner, "add scan_listener -p " . $CFG->params('SCAN_PORT') . " -s"); if (${status}) { trace ("add scan listener ... success"); } else { return FALSE; } return TRUE; } sub add_mgmt_db_listener #------------------------------------------------------------------------------- ## Function: Add Management DB listener ## Args : none ##------------------------------------------------------------------------------ { my $run_as_user = TRUE; my $cmd = "add mgmtlsnr"; my $status = srvctl($run_as_user, $cmd); if ( $status ) { trace ("add mgmt db listener ... success"); return TRUE; } else { return FALSE; } } sub add_cha #------------------------------------------------------------------------------ ## Function: Add Cluster Health Analysis Resource ## Args : force flag '-force' | '' ## Notes : This issues : srvctl add cha [-force] ## '-force' option is passed in new install and when upgrading from ## pre 12.1 version when the ora.mgmtdb resource has not been ## configured yet. ##----------------------------------------------------------------------------- { my $force = $_[0]; my $run_as_owner = TRUE; my @output; my $status; if ($force eq "-force") { $status = srvctl($run_as_owner, "add cha -force"); } else { $status = srvctl($run_as_owner, "add cha"); } if ($status == TRUE) { trace ("srvctl add cha ... success"); } else { return FALSE; } return TRUE; } sub start_cha #------------------------------------------------------------------------------ ## Function: Start Cluster Health Analysis Service ## Args : ignore error TRUE|FALSE ## node name [OPTIONAL] ## Notes : This issues : srvctl start cha [-node ] ## In case of new install and when upgrading from ## pre 12.1 version when the ora.mgmtdb resource has not been ## configured yet, the ignore error option is passed. The command ## is used to set the TARGET of the ora.cha resource to ONLINE ##----------------------------------------------------------------------------- { my $ignoreerror = $_[0]; my $nodename = $_[1]; my @output; my $run_as_owner = TRUE; my $ret = TRUE; my $status; my $cmd; # Required to set the TARGET to ONLINE for CHA resource if ($ignoreerror) { $status = srvctl($run_as_owner, "add mgmtdb"); if ($status == FALSE) { trace ("'srvctl add mgmtdb' ... failed"); $ret = FALSE; return $ret; } } $cmd = "start cha"; if ($nodename ne "") { $cmd = "start cha -node " . $nodename; } $status = srvctl($run_as_owner, $cmd); if ($status == TRUE) { trace ("'srvctl $cmd' ... success"); } else { if ($ignoreerror) { trace ("Ignoring 'srvctl $cmd' error"); } else { $ret = FALSE; } } # Remove MGMTDB configuration if ($ignoreerror) { $status = srvctl($run_as_owner, "remove mgmtdb -force"); if ($status == FALSE) { trace ("'srvctl remove mgmtdb -force' ... failed"); $ret = FALSE; } } return $ret; } sub status_cha #------------------------------------------------------------------------------ ## Function: Status of Cluster Health Analysis Service ## Args : node list ## Notes : This issues : crsctl status res ora.chad -n ## Returns TRUE if CHA is running (ONLINE) on at least one node in ## the node list passed. ##----------------------------------------------------------------------------- { my $nodelist = $_[0]; my $chares = 'ora.chad'; my $ret = FALSE; my $status; my @clunodes = split(',', $nodelist); foreach my $node (@clunodes) { $status = isResRunning($chares, $node); if ($status == TRUE) { trace("OCHAD running on node $node"); $ret = TRUE; return $ret; } } return $ret; } sub add_CVU #------------------------------------------------------------------------------ # Function: Create the cvu resource # Args : checkInterval #------------------------------------------------------------------------------- { my $checkInterval = $_[0]; my $run_as_owner = TRUE; my $cmdArgs = "add cvu"; if ($checkInterval && $checkInterval ne "" && $checkInterval ne "0") { $cmdArgs = "add cvu -t $checkInterval"; } trace ("running srvctl $cmdArgs"); my $status = srvctl($run_as_owner, $cmdArgs); if (${status}) { trace ("add cvu ... success"); } else { return FALSE; } return TRUE; } sub enable_CVU #------------------------------------------------------------------------------ # Function: enable the cvu resource # Args : none #------------------------------------------------------------------------------- { my $run_as_owner = TRUE; trace ("running enable cvu"); my $status = srvctl($run_as_owner, "enable cvu"); if ($status) { trace ("enable cvu ... success"); } else { return FALSE; } return TRUE; } sub disable_CVU #------------------------------------------------------------------------------ # Function: disable the cvu resource # Args : GI HOME, defaults to $CFG->ORA_CRS_HOME #------------------------------------------------------------------------------- { my $home = $_[0]; if (! defined $home) { $home = $CFG->ORA_CRS_HOME; } my $run_as_owner = TRUE; trace ("running disable cvu"); my $status = srvctl($run_as_owner, "disable cvu", $home); if ($status) { trace ("disable cvu ... success"); } else { return FALSE; } return TRUE; } sub remove_CVU #------------------------------------------------------------------------------ # Function: remove the cvu resource and type # Args : none #------------------------------------------------------------------------------- { my $run_as_owner = TRUE; my $status = srvctl($run_as_owner, "remove cvu -f"); if (${status}) { trace ("remove cvu -f ... success"); } else { return FALSE; } trace("remove cvu resource type"); my $CRSCTL = crs_exec_path('crsctl'); my @out = system_cmd_capture($CRSCTL, "delete", "type", "ora.cvu.type", "-unsupported"); $status = shift @out; if ($status != 0) { if(scalar(grep(/CRS-2560/, @out)) > 0) { trace("Resource type 'ora.cvu.type' does not exist"); return TRUE; } trace("remove cvu resource type failed @out"); trace("$CRSCTL delete type ora.cvu.type failed with status $status"); print_error(180, "$CRSCTL delete type ora.cvu.type"); return FALSE; } return TRUE; } sub start_GNS #--------------------------------------------------------------------- # Function: Start GNS # Args : none # Returns : TRUE if success # FALSE if failed #--------------------------------------------------------------------- { if ((lc($CFG->params('GNS_CONF')) ne "true") || ("shared" eq lc($CFG->params('GNS_TYPE')))) { trace ("GNS not configured locally, skip starting GNS"); return TRUE; } # start gns my $run_as_owner = FALSE; my $status = srvctl($run_as_owner, "start gns"); if (${status}) { trace ("start gns ... passed"); } else { print_error(107); return FALSE; } return TRUE; } #------------------------------------------------------------------------------- # Function: This function starts the SCAN in the current node. It takes one # optional argument, if such argument is passed then the flag # '-scannumber' is added to the srvctl call using the given argument. # ARG1: The SCAN number that will be used with the flag '-scannumber', this # argument is optional. # Returns: TRUE in success or FALSE in failure #------------------------------------------------------------------------------- sub start_scan { my $run_as_owner = FALSE; my $scannumber = shift; my @args = ('start', 'scan'); my ($status, $args_str); if ($scannumber) { push(@args, '-scannumber', $scannumber); } $args_str = join(' ', @args); $status = srvctl($run_as_owner, $args_str); if (${status}) { trace ("start scan ... success"); } else { print_error(110); return FALSE; } return TRUE; } #------------------------------------------------------------------------------- # Function: This function starts the SCAN listener in the current node. It takes # one optional argument, if such argument is passed then the flag # '-scannumber' is added to the srvctl call using the given argument. # ARG1: The SCAN number that will be used with the flag '-scannumber', this # argument is optional. # Returns: TRUE in success or FALSE in failure #------------------------------------------------------------------------------- sub start_scan_listener { my $run_as_owner = TRUE; my $scannumber = shift; my @args = ('start', 'scan_listener'); my ($status, $args_str); if ($scannumber) { push(@args, '-scannumber', $scannumber); } $args_str = join(' ', @args); $status = srvctl($run_as_owner, $args_str); if (${status}) { trace ("start scan listener ... success"); } else { print_error(111); return FALSE; } return TRUE; } =head2 oifcfgiflst_to_instlststr Create GPnP networks list based on oifcfg info Example of output: "Local Area Connection 3"/140.87.128.0:public,"Local Area Connection 4"/140.87.136.0:cluster_interconnect|public or eth0/10.0.100.0:cluster_interconnect,eth1/140.87.4.0.0:public Adaptor name can be quoted. /\<>|""*? are not legal for in adapter name (note, spaces or commas can appear). List is space-separated. For the sake of "oifcfg iflist" compatibility, UNKNOWN/PRIVATE/LOCAL types recognized (UNKNOWN mapped to public, PRIVATE mapped to cluster_interconnect, and LOCAL skipped. oifcfg types can be combined (comma-separated) - inst list uses | separator, though installer never produces interfaces with multiple types. =head3 Parameters Reference to array of oifcfg-style output, e.g. ("eth0 10.0.0.0 PRIVATE", "eth1 140.87.4.0 UNKNOWN") ("Local Area Connection 3 140.87.128.0 PUBLIC", i "Local Area Connection 4 140.87.136.0 CLUSTER_INTERCONNECT,PUBLIC" ) =head3 Returns returns a string with installer-style net info. =cut sub oifcfgiflst_to_instlststr { my $intfsref = $_[0]; # ref my @intfs = @{$intfsref}; my $s_instiflist = ''; foreach (0..$#intfs) { my $idef = $intfs[$_]; my @intf = oifcfg_intf_parse( $idef ); $s_instiflist = oifcfg_intf_to_instlststr( \@intf, $s_instiflist ); } trace ("inst netlst:\"".$s_instiflist."\""); return $s_instiflist; } =head2 oifcfg_intf_parse Parse a single net interface description produced by oifcfg cmd. For example: a) oifcfg iflist output: "eth0 10.0.0.0 PRIVATE 255.255.252.0", "eth1 140.87.4.0 UNKNOWN 255.255.252.0", "Local Area Connection 3 140.87.128.0 PRIVATE" b) oifcfg getif output: "Local Area Connection 4 140.87.136.0 global cluster_interconnect,public" =head3 Parameters A string containing oifcfg interface definition, see examples above. (Other strings, such as warnings, etc., must be filtered out.) =head3 Returns @returns an array of interface parameters: ($adapter_name, $network, $node_name, $type_list, $mask) Where: adapter_name is the name of net adapter, unquoted; network is a network bits of adapter address; node_name is 'global' for cluster-wide config, or node name if node-specific; type_list is a comma-separated list of network types (valid values are unknown|local|public|private|cluster_interconnect); mask is a mask bits; If any of the parameters was not defined, undef returned in its place. =cut sub oifcfg_intf_parse { my $idef = $_[0]; my $valid_types = "(local|public|private|unknown|cluster_interconnect|asm)([,](local|public|private|unkown|cluster_interconnect|asm))*"; my $an_; my $ada; my $net; my $nod; my $typ; my $msk; my $iaddr; my $n; trace ("intf: $idef"); $idef =~ s/^\s+|\s+$//g; $n = rindex( $idef, ' ' ); $an_ = substr( $idef, 0, $n ); $an_ =~ s/\s+$//; $typ = substr( $idef, $n+1 ); if ($typ !~ m/$valid_types/i) { # if cannot be type, check if mask $msk = $typ; $typ = undef; # validate mask $iaddr = ipv4_atol($msk); # toberevised: +ipv6 - inet_pton if (! defined $iaddr) { $msk = undef; } else { $msk = ipv4_ltoa($iaddr); # toberevised: +ipv6 - inet_ntop } $iaddr = undef; if (defined $msk) { $n = rindex( $an_, ' ' ); $typ = substr( $an_, $n+1 ); $an_ = substr( $an_, 0, $n ); $an_ =~ s/\s+$//; } } if ($typ !~ m/$valid_types/i) { $typ = undef; } if (defined $typ) { $n = rindex( $an_, ' ' ); if (1 <= $n) { $ada = substr( $an_, 0, $n ); $ada =~ s/\s+$//; $net = substr( $an_, ($n+1) ); # validate address, if not addr, must be scope (nodename/global) $iaddr = ipv4_atol($net); # toberevised: +ipv6 - inet_pton if ((! defined $iaddr) || ($iaddr == 0)) { $nod = $net; $net = undef; $n = rindex( $ada, ' ' ); if (1 <= $n) { $net = substr( $ada, ($n+1) ); $ada = substr( $ada, 0, $n ); $ada =~ s/\s+$//; } # validate address $iaddr = ipv4_atol($net); #toberevised: +ipv6 -inet_pton } if ((! defined $iaddr) || ($iaddr == 0)) { $net = undef; } else { $net = ipv4_ltoa($iaddr); # toberevised: +ipv6 - inet_ntop } $iaddr = undef; } } trace ("intf parsed: -$ada-$net-$nod-$typ-$msk-="); return ($ada, $net, $nod, lc($typ), $msk ); } =head2 ipv4_atol Convert a string with decimal dotted ipv4 to network-ordered long Note: this is quite similar to inet_aton(); however, it does not tries to resolve hostnames as inet_aton does. =head3 Parameters String containing decimal-dotted ipv4 address value. =head3 Returns @returns a network-ordered long ipv4 address value. =cut sub ipv4_atol { return unpack('N',pack('C4',split(/\./,shift))); } =head2 ipv4_ltoa Convert a network-ordered long ipv4 address value to a string decimal dotted ipv4 notation. =head3 Parameters Long containing network-ordered ipv4 address value. =head3 Returns @returns a string dotted-decimal ipv4 address value. =cut sub ipv4_ltoa { return inet_ntoa(pack('N',shift)); } sub oifcfg_intf_to_instlststr { my $idefref = $_[0]; # ref # ref to parsed interface definition my $s_instiflist = $_[1]; # list of NETWORKS, installer-style my $ada; my $net; my $nod; my $typ; my $msk; ($ada, $net, $nod, $typ, $msk ) = @{$idefref}; $s_instiflist = '' if (! defined $s_instiflist); if ((! defined $typ) || (! defined $net) || (! defined $ada)) { return; # bad intf definition } # For the sake of "oifcfg iflist" compatibility, UNKNOWN/PRIVATE/LOCAL types # recognized (UNKNOWN mapped to public, # PRIVATE mapped to cluster_interconnect, # and LOCAL skipped. oifcfg types can be combined (comma-separated) - inst # list uses | separator, though installer never produces interfaces with # multiple types. # if ($typ =~ m/LOCAL/i) { return; # skip "do_not_use" itf } $typ =~ s/(UNKNOWN|unknown)/public/g; $typ =~ s/(PRIVATE|private)/cluster_interconnect/g; $typ =~ s/,/|/g; # replace separator # tabs, \/|"'*:<>? are normally illegal in adapter names if ($ada =~ /[ ,:<>\t\^\(\)\\\/\*\?\|\[\]\+]/) { $ada = '"'.$ada.'"'; } if (!($s_instiflist eq '')) { $s_instiflist .= ','; } $s_instiflist .= $ada.'/'.$net.':'.lc($typ); trace ("inst netlst:\"".$s_instiflist."\""); return $s_instiflist; } sub isResRunning #------------------------------------------------------------------------------- # Function: Check if resource is running # Args : 0 - resource # : 1 - node name # Returns : TRUE if running # FALSE if not running #------------------------------------------------------------------------------- { my $res = $_[0]; my $nodename = $_[1]; my $node = ($nodename) ? ($nodename) : ($CFG->HOST); my $crsctl = crs_exec_path("crsctl"); trace("Check if resource '$res' is ONLINE on node '$node'"); my @cmd = ($crsctl, 'status', 'resource', $res, '-n', $node); my @out = system_cmd_capture(@cmd); my $rc = shift @out; if ($rc == 0) { my @reStat = grep(/STATE=ONLINE/, @out); if (scalar(@reStat) > 0) { trace("$res is online"); return TRUE; } else { trace("$res is not online"); return FALSE; } } return FALSE; } sub isPrereqIgnored { my $isprereqignored = FALSE; if ($CFG->params('USER_IGNORED_PREREQ') =~ m/true/i) { $isprereqignored = TRUE; } trace("USER_IGNORED_PREREQ is set to $isprereqignored"); return $isprereqignored; } sub stopOracleRestart #------------------------------------------------------------------------------- # Function: Stop Oracle Restart Stack # Returns : TRUE if success # FALSE if failed #------------------------------------------------------------------------------- { my $res = $_[0]; my $success = TRUE; my $crsctl; if ($CFG->platform_family eq "windows") { $crsctl = crs_exec_path("crsctl.exe"); } else { $crsctl = crs_exec_path("crsctl"); } if (! -e $crsctl) { print_error(13, $crsctl); trace ("$crsctl does not exist to proceed stop clusterware"); return FALSE; } # stop resource my @cmd = ($crsctl, 'stop', 'has', '-f'); trace("Executing @cmd"); my @out = system_cmd_capture(@cmd); my $rc = shift @out; # Allow HA daemons to shutdown in 10sec sleep 10; # check if ohasd is still up if (! checkServiceDown("ohasd")) { print_error(192); $success = FALSE; } return $success; } sub unlockHome #------------------------------------------------------------------------------- # Function: unlocks the GI Home for patching. sets the owner of all the # files/dirs in crsconfig_fileperms and crsconfig_dirs to GI Home # owner. This is sufficient for patching GI Home. # Args : none # Returns : none #------------------------------------------------------------------------------- { my ($owner, $group, $perms, $basedir) = @_; my %dirown; my %fileown; my ($a, $dirname, $own, $grp, $perm, $fname); my $ORA_CRS_HOME; trace("The GI home to unlock: $basedir"); $ORA_CRS_HOME = ($basedir) ? $basedir :($CFG->ORA_CRS_HOME); trace("Unlocking the GI home: $ORA_CRS_HOME"); my $platform = s_get_platform_family(); if ($platform eq 'windows') { $ORA_CRS_HOME =~ s/\\/\\\\/g; } my $SUPERUSER = $CFG->SUPERUSER; my $wrapdir_crs = catdir($ORA_CRS_HOME, "crs", "utl", $CFG->HOST); my @dirs = read_file (catfile($wrapdir_crs, "crsconfig_dirs")); my @fileperms = read_file (catfile($wrapdir_crs, "crsconfig_fileperms")); foreach my $line (@dirs) { chomp ($line); next if ($line =~ /^#|\$|^\s*$/); ($a, $dirname, $own, $grp, $perm) = split(/ /, $line); if ($own =~ $SUPERUSER) { $dirown{$dirname} = $own; } else { next; } } foreach my $line (@fileperms) { chomp ($line); next if ($line =~ /^#|\$|^\s*$/); ($a, $fname, $own, $grp, $perm) = split(/ /, $line); if ($own =~ $SUPERUSER) { $fileown{$fname} = $own; } else { next; } } foreach my $dir (keys%dirown) { next if ($dir !~ m/^$ORA_CRS_HOME.*/); if (! s_set_ownergroup ($owner, $group, $dir)) { if (! is_dev_env()) { die(dieformat(152, $dir)); } } } foreach my $file (keys%fileown) { next if ($file !~ m/^$ORA_CRS_HOME.*/); if (! s_set_ownergroup ($owner, $group, $file)) { if (! is_dev_env()) { die(dieformat(152, $file)); } } } } sub lockAcfsFiles { my %fileown; my ($a, $own, $grp, $perm, $fname); my $ORA_CRS_HOME = $CFG->ORA_CRS_HOME; my $wrapdir_crs = catfile ($ORA_CRS_HOME, "crs", "utl", $CFG->HOST); my @fileperms = read_file (catfile($wrapdir_crs, "crsconfig_fileperms")); foreach my $line (@fileperms) { chomp ($line); next if ($line =~ /^#|\$|^\s*$/ || $line !~ m/^.*acfs.*/ || $line =~ m/^.*install.*/ ); ($a, $fname, $own, $grp, $perm) = split(/ /, $line); if ($own =~ $CFG->HAS_USER) { $fileown{$fname} = $own; } else { next; } } foreach my $file (keys%fileown) { next if ($file !~ m/^$ORA_CRS_HOME.*/); s_set_ownergroup ($CFG->SUPERUSER, $CFG->params('ORA_DBA_GROUP'), $file); } } sub modifyparamfile { my $param = $_[0]; my $paramval = $_[1]; my $paramfile = $_[2]; my $params_file = ($paramfile) ? ($paramfile) : ($CFG->paramfile); trace("modify $param in $params_file"); my @params_table = read_file ($params_file); my $updateParamsFile = FALSE; my $ix = 0; foreach my $rec (@params_table) { chomp($rec); if ($rec =~ m/^$param=/) { my ($key, $value) = split (/=/, $rec); $params_table[$ix] = $param . "=" . "$paramval"; $updateParamsFile = TRUE; last; } $ix++; } if ($updateParamsFile) { # save original params file my $save_file = catfile (dirname($params_file), 'crsconfig_params.saved'); copy_file ($params_file, $save_file); # delete old file and create new file if (s_remove_file($params_file)) { open (SFILE, ">$params_file") || die(dieformat(185, $params_file, $!)); foreach my $rec (@params_table) { chomp($rec); print SFILE "$rec\n"; } close SFILE; } } } sub stopClusterware #------------------------------------------------------------------------------- # Function: Stop crs / cluster # Args : oracle home, Resource (crs/cluster) # Returns : TRUE if success # FALSE if failed #------------------------------------------------------------------------------- { my ($home, $res) = @_; my $success = TRUE; my $crsctl; if (! defined $home) { $crsctl = crs_exec_path('crsctl'); } else { $crsctl = catfile( $home, 'bin', 'crsctl' ); } # stop resource my @cmd; if ($res eq 'crs') { @cmd = ($crsctl, 'stop', 'crs', '-f'); } else { @cmd = ($crsctl, 'stop', 'resource', '-all', '-init'); } my @out = system_cmd_capture (@cmd); my $rc = shift @out; trace ("@cmd"); if (! checkServiceDown("crs")) { print_error(357); return FALSE; } # check if ohasd & crs are still up if ($res eq 'crs') { if (! checkServiceDown("ohasd")) { print_error(191); $success = FALSE; } } return $success; } sub queryVoteDisks { #------------------------------------------------------------------------------- # Function: Query Voting disks # Args : none # Returns : List of voting disks/files #------------------------------------------------------------------------------- my $currentHome = getCrsHome(); #it's configured crs home in case of partial downgrade my $crsctl = crs_exec_path('crsctl', $currentHome); my @css_votedisk; my $css_is_up = FALSE; if (checkServiceDown("css")) { trace("Starting CSS exclusive"); my $css_rc = CSS_start_exclusive(); if ($css_rc != CSS_EXCL_SUCCESS) { trace ("CSS failed to enter exclusive mode to extract votedisk"); } else { $css_is_up = TRUE; } } else { $css_is_up = TRUE; } if ($css_is_up) { trace("Querying CSS vote disks"); open (QUERY_VOTEDISK, "$crsctl query css votedisk|"); @css_votedisk = (); chomp @css_votedisk; close QUERY_VOTEDISK; } return @css_votedisk; } sub queryVoteDisks_112 { #------------------------------------------------------------------------------- # Function: Query Voting disks for downgrade of a cluster whose upgrade from # 112 was not completed # Args : none # Returns : List of voting disks/files # Notice : This subroutine is added to fix bug 23000412 #------------------------------------------------------------------------------- my $currentHome = getCrsHome(); #it's configured crs home in case of partial downgrade my $crsctl = crs_exec_path('crsctl', $currentHome); my @css_votedisk; my $css_is_up = FALSE; # The upgrade was aborted before upgrading OLR, but OCR has been changed. # The configured home is still the old home. # Check if CRS is up my $cluster_is_up = check_service ("cluster", 20); my $crs_is_up = check_service ("ohasd", 10); my $start_exclusive = FALSE; if (!$crs_is_up) { trace("OHASD is not up. So starting CRS exclusive"); if(start_excl_crs($currentHome)) { $crs_is_up = TRUE; } } else { trace("OHASD is already up."); if (!$cluster_is_up) { trace("Starting CSS exclusive"); $start_exclusive = TRUE; my $css_rc = CSS_start_exclusive_112(); if ($css_rc != CSS_EXCL_SUCCESS) { $start_exclusive = FALSE; trace ("CSS failed to enter exclusive mode to extract votedisk"); } } } if (($crs_is_up) || ($start_exclusive)) { trace("Querying CSS vote disks"); open (QUERY_VOTEDISK, "$crsctl query css votedisk|"); @css_votedisk = (); chomp @css_votedisk; close QUERY_VOTEDISK; } return @css_votedisk; } sub extractVotedisks #------------------------------------------------------------------------------- # Function: Extract Voting disks # Args : none #------------------------------------------------------------------------------- { my @votedisk_list; my @css_votedisk = queryVoteDisks(); foreach my $votedisk (@css_votedisk) { trace("Voting disk is : $votedisk"); # get line contains ' (/' if ($votedisk =~ / \(/) { # $votedisk contains '1. 2 282bf2a833f54f02bf4befd002fa90d6 # (/dev/raw/raw1) [OCRDG]' # parse $votedisk to get '/dev/raw/raw1' my $vdisk; my ($dummy, $text) = split (/\(/, $votedisk); ($vdisk, $dummy) = split (/\)/, $text); push (@votedisk_list, $vdisk); } } trace ("Vote disks found: @votedisk_list"); return @votedisk_list; } sub add_ons { my $run_as_owner = TRUE; my $status = srvctl($run_as_owner, 'add ons'); if ($status) { trace ("srvctl add ons ... success"); } else { return FALSE; } return TRUE; } =head1 EXPORTED FUNCTIONS =head2 getNodeRoleStatus Get the active role for current node or a given node =head3 Parameters ARG1: Given node name; refers to the current node if not supplied =head3 Returns Valid node role or undef if unsuccessful =head3 Usage my $role = getNodeRoleStatus(); or my $role = getNodeRoleStatus($node); =cut sub getNodeRoleStatus { my $node = $_[0]; my $role = undef; my @cmd; my $nodeName = (defined $node) ? $node: "local"; trace("Getting the node role status for the $nodeName node"); my $crsctl = crs_exec_path('crsctl'); if (defined $node) { @cmd = ($crsctl, 'get', 'node', 'role', 'status', '-node', $node); } else { @cmd = ($crsctl, 'get', 'node', 'role', 'status'); } my @output = system_cmd_capture(@cmd); my $rc = shift @output; if (0 != $rc) { if (defined $node) { print_error(495, $node); } else { print_error(496); } } else { if (scalar(grep(/hub/, @output)) > 0) { $role = NODE_ROLE_HUB; } elsif (scalar(grep(/leaf/, @output)) > 0) { $role = NODE_ROLE_RIM; } else { trace("Invalid node role: @output"); } } if (defined $role) { trace("The node role status for the $nodeName node is $role"); } return $role; } =head2 getNodeConfigRole Get the configured role for current node or a given node =head3 Parameters ARG1: Given node name; refers to the current node if not supplied ARG2: Given CRS home: Notice: the CRS home will be accepted as ARG2 only if ARG1 (the node name) is given. =head3 Returns Valid node role or undef if unsuccessful =head3 Usage my $role = getNodeConfigRole(); or my $role = getNodeConfigRole($node); or my $role = getNodeConfigRole($node,$crs_home); =cut sub getNodeConfigRole { my $node = $_[0]; my $role = undef; my @cmd; my $nodeName = (defined $node) ? $node: "local"; trace("Getting the configured node role for the $nodeName node"); my $crsctl; $crsctl = crs_exec_path('crsctl'); if (defined $node) { my $crs_home = $_[1]; if (defined $crs_home) { $crsctl = crs_exec_path('crsctl', $crs_home); } if ($node eq $CFG->HOST) { @cmd = ($crsctl, 'get', 'node', 'role', 'config'); } else { @cmd = ($crsctl, 'get', 'node', 'role', 'config', '-node', $node); } } else { @cmd = ($crsctl, 'get', 'node', 'role', 'config'); } my @output = system_cmd_capture(@cmd); my $rc = shift @output; if (0 != $rc) { (defined $node) ? trace("Failed to get the configured node role for the node $node.") : trace("Failed to get the configured node role for the local node"); } else { if (scalar(grep(/hub/, @output)) > 0) { $role = NODE_ROLE_HUB; } elsif (scalar(grep(/leaf/, @output)) > 0) { $role = NODE_ROLE_RIM; } elsif (scalar(grep(/auto/, @output)) > 0) { $role = NODE_ROLE_AUTO; } else { trace("Invalid node role: @output"); } } if (defined $role) { trace("The configured node role for the $nodeName node is $role"); } return $role; } =head2 getLocalNodeRole Get the node role for current node. If upgrade, retrieve node role from old CRS stack. If install, look for the node role from HUB_NODE_LIST/RIM_NODE_LIST =head3 Parameters NONE =head3 Returns Valid node role =head3 Usage my $role = getLocalNodeRole(); =cut sub getLocalNodeRole { my $host = tolower_host(); my $role; trace("Getting the node role for $host"); if ($CFG->UPGRADE) { trace("Getting the configured node role from the old stack"); $role = $CFG->oldconfig('NODE_CONFIG_ROLE'); } else { trace("Getting the configured node role from the parameter file"); my $HUB_NODE_LIST = $CFG->params('HUB_NODE_LIST'); my $RIM_NODE_LIST = $CFG->params('RIM_NODE_LIST'); my @hubNodes = split(',', $HUB_NODE_LIST); foreach my $hubNode (@hubNodes) { # Remove the site from the node name $hubNode = (split(':', $hubNode))[0]; trace("Removing the site from 'HUB_NODE_LIST' and get: " .$hubNode); if (lc($hubNode) eq lc($host)) { $role = NODE_ROLE_HUB; last; } } my @rimNodes = split(',', $RIM_NODE_LIST); foreach my $rimNode (@rimNodes) { # Remove the site from the node name $rimNode = (split(':', $rimNode))[0]; if (lc($rimNode) eq lc($host)) { $role = NODE_ROLE_RIM; last; } } if (!$role) { trace("Not found $host role in the parameter file"); } } trace("$host role is: $role"); return $role; } =head2 isHubNode Check if the current node is a hub node =head3 Parameters None =head3 Returns TRUE or FALSE =head3 Usage =cut sub isHubNode { my $node_role = getNodeConfigRole(); print_error(378) unless (defined $node_role); if (NODE_ROLE_HUB eq $node_role) { trace("the node role is hub"); return TRUE; } return FALSE; } sub isRimNode { my $node_role = getNodeConfigRole(); print_error(378) unless (defined $node_role); if (NODE_ROLE_RIM eq $node_role) { trace("the node role is leaf"); return TRUE; } return FALSE; } =head2 isAutoNode Check if the current node is a auto node =head3 Parameters None =head3 Returns TRUE or FALSE =head3 Usage =cut sub isAutoNode { my $node_role = getNodeConfigRole(); print_error(378) unless (defined $node_role); if (NODE_ROLE_AUTO eq $node_role) { trace("the node role is auto"); return TRUE; } return FALSE; } =head2 setCurrentNodeRole Set the role for the local node =head3 Parameters Args: None =head3 Returns SUCCESS or FAILED =head3 Usage my $ret = setCurrentNodeRole(); =cut sub setCurrentNodeRole { my $role; my $status = SUCCESS; my $localNode = $CFG->HOST; if (($CFG->UPGRADE) && (isOldVersionLT121())) { trace("The lower version GI is a legacy cluster"); return SUCCESS; } if ((! defined ($CFG->localNodeRole)) && (! isBigCluster())) { # Return at once if not big cluster trace("No need to set the role for the local node in a regular cluster"); return SUCCESS; } trace("Setting the role for the local node '$localNode'"); if (isAddNode($localNode)) { trace("New node $localNode to be added"); my @new_hosts = split (/,/, $CFG->params('CLUSTER_NEW_HOST_NAMES')); my @new_roles = split (/,/, $CFG->params('CLUSTER_NEW_NODE_ROLES')); my $nbr_of_hosts = scalar(@new_hosts); my $nbr_of_roles = scalar(@new_roles); my $ix = 0; if ($nbr_of_hosts != $nbr_of_roles) { die(dieformat(438, $CFG->ORA_CRS_HOME)); } foreach my $host (@new_hosts) { chomp $host; if ($localNode eq lc($host)) { last; } $ix++; } $role = $new_roles[$ix]; trace("The new node's role is: $role"); $role = lc($role); } if (! $role) { if (defined ($CFG->localNodeRole)) { $role = $CFG->localNodeRole; trace("Local node role: $role"); } } if (!$role) { my $hubNodesList = $CFG->params('HUB_NODE_LIST'); my @hubNodes = split(',', $hubNodesList); foreach my $hubNode (@hubNodes) { # For extended clusters the contents of HUB_NODE_LIST # come in the format of nodelist:site,nodelist:site... # so we remove the site from the node name $hubNode = (split(':', $hubNode))[0]; if (lc($hubNode) eq lc($localNode)) { $role = NODE_ROLE_HUB; last; } } } if (!$role) { my $rimNodesList = $CFG->params('RIM_NODE_LIST'); my @rimNodes = split(',', $rimNodesList); foreach my $rimNode (@rimNodes) { # For extended clusters the contents of RIM_NODE_LIST # come in the format of nodelist:site,nodelist:site... # so we remove the site from the node name $rimNode = (split(':', $rimNode))[0]; if (lc($rimNode) eq lc($localNode)) { $role = NODE_ROLE_RIM; last; } } } if ($role) { trace("The current node's role is: $role"); if ($role ne NODE_ROLE_HUB) { $status = setNodeRole($role,"force"); } } else { trace("The current node '$localNode' is not classified"); $status = FAILED; } return $status; } sub setNodeRole { my $role = $_[0]; my $force = $_[1]; my $status = SUCCESS; my $crsctl = crs_exec_path('crsctl'); my @cmd; if ($force eq "force") { @cmd = ($crsctl, 'set', 'node', 'role', $role, '-f'); } else { @cmd = ($crsctl, 'set', 'node', 'role', $role); } my @output = system_cmd_capture(@cmd); my $rc = shift @output; if (0 != $rc) { print_error(368, $role); $status = FAILED; } return $status; } =head2 setClusterType Set the cluster type as per the configuration data that comes from user input =head3 Parameters None =head3 Returns SUCCESS or FAILED =head3 Notes The cluster type is only necessarily set on application cluster. =cut sub setClusterType { my $clutype; my $status = SUCCESS; my $crsctl = crs_exec_path('crsctl'); if (isAppCluster()) { $clutype = "application"; my @cmd = ($crsctl, 'set', 'cluster', 'type', $clutype); my @output = system_cmd_capture(@cmd); my $rc = shift @output; if (0 == $rc) { trace("Successfully set the cluster type to '$clutype'"); } elsif (scalar(grep(/CRS-6538/, @output)) > 0) { trace("The cluster type has already been set to '$clutype'"); } else { print_lines(@output); trace(join(' ', @cmd) . "failed with status $rc"); trace("Unable to set the cluster type to '$clutype'"); die(dieformat(585, $clutype)); } } return $status; } =head2 setHubSize Set the maximum size of hub nodes =head3 Parameters None =head3 Returns SUCCESS or FAILED =head3 Usage =cut sub setHubSize { my $hubSize = $CFG->params('HUB_SIZE'); trace("Setting the hub size to $hubSize"); my $crsctl = crs_exec_path('crsctl'); my @cmd = ($crsctl, 'set', 'cluster', 'hubsize', $hubSize); my @output = system_cmd_capture(@cmd); my $status = shift @output; if (0 != $status) { print_error(369, $hubSize); return FAILED; } return SUCCESS; } =head2 isBigCluster Check if it's a big cluster environment =head3 Parameters None =head3 Returns TRUE : A big cluster FALSE: Not a big cluster =head3 Usage my $bc = isBigCluster(); =cut sub isBigCluster { if ("true" eq lc($CFG->params('BIG_CLUSTER'))) { trace("Set BC to 1"); return TRUE; } else { return FALSE; } } =head1 EXPORTED FUNCTIONS =head2 getClusterConfigMode Get the configured mode for the current cluster =head3 Parameters None =head3 Returns Valid cluster mode or undef if unsuccessful =cut sub getClusterConfigMode { my $mode = undef; trace("Getting the cluster configured mode ..."); my $crsctl = crs_exec_path('crsctl'); my @cmd = ($crsctl, 'get', 'cluster', 'mode', 'config'); my @output = system_cmd_capture(@cmd); my $rc = shift @output; if (0 != $rc) { print_lines(@output); die(dieformat(497)); } else { if (scalar(grep(/standard/, @output)) > 0) { $mode = CLUSTER_MODE_STD; } elsif (scalar(grep(/flex/, @output)) > 0) { $mode = CLUSTER_MODE_FLEX; } else { trace("Invalid cluster mode: @output"); } } if (defined $mode) { trace("The cluster configured mode: $mode"); } else { die(dieformat(498)); } return $mode; } =head2 createCredDomain The subroutine for creating credential domain in OCR/OLR =head3 Parameters Args: Domain name =head3 Returns SUCCESS or FAILED =head3 Usage my $ret = createCredDomain('ASM', 'OCR'); =cut sub createCredDomain { my $domain = $_[0]; my $location = $_[1]; my @out; my @program; my $rc; my $crsctl = crs_exec_path('crsctl'); my $local; if ("olr" eq lc($location)) { $local = "-local"; } @program = ($crsctl, 'add', 'credmaint', '-path', $domain, $local); @out = system_cmd_capture(@program); $rc = shift @out; if (0 != $rc && scalar(grep(/10405/, @out)) == 0) { trace("Failed to add $domain credential domain, error: @out"); return FAILED; } @program = ($crsctl, 'setperm', 'credmaint', '-o', $CFG->params('ORACLE_OWNER'), '-path', $domain, $local); @out = system_cmd_capture(@program); $rc = shift @out; if (0 != $rc) { trace("Failed to set perms on $domain credential domain, error: @out"); return FAILED; } return SUCCESS; } #------------------------------------------------------------------------------- # Function: Enables flex asm on non-first nodes by importing the credentials created on the # first node. # Args : none # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- sub importASMCredentials { my @output; my $rc = -1; my $location = $_[0]; my $repos; my $KFOD = catfile($CFG->ORA_CRS_HOME, 'bin', 'kfod'); my $wrap = 'wrap=' . get_asm_cred_file(); if ("olr" eq lc($location)) { $repos = "olr=TRUE"; } elsif ("ocr" eq lc($location)) { $repos = ""; } my @program = ($KFOD, 'op=credimport', $wrap, $repos, 'force=TRUE'); $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @program); trace("kfod op=credimport rc: $rc"); if (0 != $rc) { trace("Failed to enable flex ASM on local node, error: @output"); return FAILED; } else { trace("Enabled flex ASM on local node"); } return SUCCESS; } sub import_asm_credentials #------------------------------------------------------------------------------- # Function: Import ASM credentials into OLR. # This is called on Local, Near and Far ASM clusters. # Args : None. # Returns : None. #------------------------------------------------------------------------------- { if (FAILED == createCredDomain('ASM', 'OLR') ) { die(dieformat(377)); } if (FAILED == importASMCredentials('OLR')) { die(dieformat(366)); } } #------------------------------------------------------------------------------- # Function: # Issues "ALTER DISKGROUP ALL MOUNT" before converting ASM to Flex mode. # Enables Flex ASM by creating credentials on the first node. They are # propagated to the other nodes. # Args : none # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- sub createASMCredentials { my @output; my $rc = -1; my $asm_mode = getASMMode(); my $ASMCMD = catfile($CFG->ORA_CRS_HOME, 'bin', 'asmcmd'); my @program = ($ASMCMD, 'mount -a'); setOraHomeSID($asm_mode, $CFG->ORA_CRS_HOME); $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @program); trace("asmcmd mount -a rc: $rc"); if (0 != $rc) { # @output looks like: # ORA-15032: not all alterations performed # ORA-15017: diskgroup "OCR_VD_FLEX" cannot be mounted # ORA-15013: diskgroup "OCR_VD_FLEX" is already mounted (DBD ERROR: OCIStmtExecute) if (scalar(grep(/15013/, @output)) > 0) { trace("All disk groups already mounted."); } else { trace("Failed to mount all disk groups, error: @output"); return FAILED; } } else { trace("Successfully mounted all disk groups"); } my $KFOD = catfile($CFG->ORA_CRS_HOME, 'bin', 'kfod'); my $wrap = 'wrap=' . get_asm_cred_file(); @program = ($KFOD, 'op=credremote', $wrap); $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @program); trace("kfod op=credremote rc: $rc"); if (0 != $rc) { trace("Failed to create credentials for flex ASM, error: @output"); return FAILED; } else { trace("Created credentials for flex ASM"); } return SUCCESS; } #------------------------------------------------------------------------------- # Function: Removes the remote asm setting in the gpnp profile. This is called during # deconfig. # Args : none # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- sub disableRemoteASM { my @output; my $rc = -1; my $KFOD = catfile($CFG->ORA_CRS_HOME, 'bin', 'kfod'); my @program = ($KFOD, 'op=disableremote'); $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @program); trace("kfod op=disableremote rc: $rc"); if (0 != $rc) { trace("Failed to disable remote ASM, error: @output"); return FAILED; } else { trace("Successfully disabled remote ASM"); } return SUCCESS; } =head2 copyASMCredentials Copy ASM credentials to the gpnp dir from the location specified by ASM_CREDENTIALS =head3 Parameters None =head3 Returns SUCCESS or FAILED =head3 Usage =cut sub copyASMCredentials { my $srcfile = $CFG->params('ASM_CREDENTIALS'); my $dstfile = get_asm_cred_file(); my $ret = copy_file($srcfile, $dstfile, $CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP')); return $ret; } sub copy_asm_credentials #------------------------------------------------------------------------------- # Function: Copy and import ASM credentials. # This is called on the first node of a ASM client cluster. # Args : None. # Returns : None. #------------------------------------------------------------------------------- { if (FAILED == createCredDomain('ASM', 'OLR')) { die(dieformat(377)); } if (FAILED == copyASMCredentials()) { die(dieformat(375)); } import_asm_credentials(); push_seed_dir($CFG->params('NODE_NAME_LIST')); } =head2 isLegacyASM Check if legacy ASM is configured =head3 Parameters None =head3 Returns TRUE : Legacy ASM FALSE: Non-legacy ASM =head3 Usage =cut sub isLegacyASM { if (($CFG->UPGRADE) && (! $CFG->SIHA)) { if ((1 == $CFG->oldconfig('ASM_CONFIGURED')) && ($CFG->oldconfig('ASM_MODE') eq ASM_MODE_LEGACY)) { trace("ASM config is legacy"); return TRUE; } else { return FALSE; } } if (isAddNode($CFG->HOST)) { if ($CFG->ASM_MODE eq ASM_MODE_LEGACY) { trace("ASM config is legacy"); return TRUE; } else { return FALSE; } } my $asmConf = $CFG->params('ASM_CONFIG'); trace("ASM_CONFIG value: $asmConf"); if ((! $asmConf) || ((! isNearASM()) && (! isFarASM()))) { trace("ASM_CONFIG is legacy"); return TRUE; } return FALSE; } =head2 isFarASM Check if far ASM is configured =head3 Parameters None =head3 Returns TRUE : Far ASM configured FALSE: Far ASM not configured =head3 Usage =cut sub isFarASM { if (($CFG->UPGRADE) && (! $CFG->SIHA)) { if ((1 == $CFG->oldconfig('ASM_CONFIGURED')) && ($CFG->oldconfig('ASM_MODE') eq ASM_MODE_CLIENT)) { trace("ASM config is far"); return TRUE; } else { return FALSE; } } if (isAddNode($CFG->HOST)) { if ($CFG->ASM_MODE eq ASM_MODE_CLIENT) { trace("ASM config is far"); return TRUE; } else { return FALSE; } } if ("far" eq lc($CFG->params('ASM_CONFIG'))) { trace("ASM_CONFIG is far"); return TRUE; } else { return FALSE; } } sub isNearASM { if (($CFG->UPGRADE) && (! $CFG->SIHA)) { if ((1 == $CFG->oldconfig('ASM_CONFIGURED')) && ($CFG->oldconfig('ASM_MODE') eq ASM_MODE_FLEX)) { trace("ASM config is near"); return TRUE; } else { return FALSE; } } if (isAddNode($CFG->HOST)) { if ($CFG->ASM_MODE eq ASM_MODE_FLEX) { trace("ASM config is near"); return TRUE; } else { return FALSE; } } # return true if ASM_CONFIG = "near" if ("near" eq lc($CFG->params('ASM_CONFIG'))) { trace("ASM_CONFIG is near"); return TRUE; } else { return FALSE; } } =head2 isRemoteGIMR Check if GIMR is remote =head3 Parameters None =head3 Returns TRUE or FALSE =head3 Usage =cut sub isRemoteGIMR { if ("remote" eq lc($CFG->params('GIMR_CONFIG'))) { trace("Remote GIMR is configured"); return TRUE; } else { return FALSE; } } =head2 isAppCluster Check if an application cluster is configured =head3 Parameters None =head3 Returns TRUE or FALSE =head3 Usage =cut sub isAppCluster { if ("app" eq lc($CFG->params('CLUSTER_TYPE'))) { trace("This is an application cluster"); return TRUE; } else { return FALSE; } } =head2 isMgmtDatabaseConfigured Check if the management database is configured =head3 Parameters None =head3 Returns TRUE or FALSE =head3 Notes This function works only during install. =cut sub isMgmtDatabaseConfigured { if (($CFG->params('MGMT_DB')) && ("true" eq lc($CFG->params('MGMT_DB')))) { trace("The mgmt database is configured"); return TRUE; } else { return FALSE; } } =head2 get_oifcfg_info Gets oifcfg networks interface info for specified command line params. =head3 Parameters =head4 string with oifcfg home location. If undef, then current home is used. =head4 Rest of arguments passed to oifcfg cmdline. =head3 Returns =head4 returns a list of strings-net intf defs; Warning messages, if any, are filtered out. =head4 result code (0 for success) as a first member of array. =cut sub get_oifcfg_info { my @intfs = (); my ($home, @args) = @_; my $cmd; if (! defined $home) { $cmd = crs_exec_path('oifcfg'); } else { $cmd = catfile( $home, 'bin', 'oifcfg' ); } # run oifcfg asking for intf, net, type and mask my @out = system_cmd_capture(($cmd, @args)); my $rc = shift @out; # read-in interface list if (0 == $rc) { trace "---Got oifcfg out ($cmd ".join(' ',@args)."):"; foreach (0..$#out) { my $intf = $out[$_]; trace $intf ; # total failure should return rc, else filter out non-fatal # error messages, if any, e.g. "PRIF-nn: error....." if ($intf !~ /^PR[A-Z]+-[0-9]+: /) { push @intfs, $intf ; } else { trace( $intf ); print_error(173, $intf); } } } else { push @intfs, "$cmd ".join(' ',@args)." failed."; } return ($rc, @intfs); } #------------------------------------------------------------------------------- # Function: Enables bold print if platform is not windows. # Args : none # Returns : none #------------------------------------------------------------------------------- sub set_bold { my $platform = s_get_platform_family(); if ($platform ne 'windows' && !$CFG->AUTO && !$CFG->params('SILENT')) { print color 'bold'; } } #------------------------------------------------------------------------------- # Function: Disabled bold print if platform is not windows. # Args : none # Returns : none #------------------------------------------------------------------------------- sub reset_bold { my $platform = s_get_platform_family(); if ($platform ne 'windows' && !$CFG->AUTO && !$CFG->params('SILENT')) { print color 'reset'; } } sub rscPreChecks #--------------------------------------------------------------------- # Function: Pre-checks for running root script # Args : None # Returns : None #--------------------------------------------------------------------- { trace("Performing few checks before running scripts"); if ($CFG->platform_family eq "unix") { my @capout = (); my $rc; my $user = $CFG->params('ORACLE_OWNER'); my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); trace("Attempt to get current working directory"); $rc = run_as_user2($user, \@capout, 'pwd'); my $pwd = join(' ', @capout); $pwd = trim($pwd); my $safeDir = $CFG->params('ORACLE_HOME'); if ((0 != $rc) || ($pwd =~ /$ORACLE_BASE/ && $CFG->DECONFIG)) { (0 != $rc) ? trace("Failed to get current working directory: @capout") : trace("The current working directory: $pwd is in Oracle Base."); } else { trace("The current working directory: $pwd"); } trace("Change working directory to safe directory $safeDir"); chdir($safeDir) or die("Unable to change directory to $safeDir: $!\n"); } trace("Pre-checks for running the rootcrs script passed."); } =head2 setConfiguredCRSHome Try to retrieve the configured CRS home from which the current stack is running, and set $CFG->configuredCRSHome =head3 Parameters None =head3 Returns Configured CRS home =head3 Usage =cut sub setConfiguredCRSHome { if (! $CFG->UPGRADE) { $CFG->configuredCRSHome($CFG->ORA_CRS_HOME); return $CFG->ORA_CRS_HOME; } my $home; trace("Get the configured CRS home from olr.loc"); $home = s_get_olr_file("crs_home"); if (! $home) { # pre-11.2 trace("Get the configured CRS home from init.cssd"); $home = s_getOldCrsHome(); } trace("Configured CRS home: ${home}"); $CFG->configuredCRSHome($home); return $home; } =head2 getConfiguredCRSHome The function for exporting the configured CRS home =head3 Parameters None =head3 Returns Configured CRS home =head3 Usage =cut sub getConfiguredCRSHome { return $CFG->configuredCRSHome; } =head1 EXPORTED FUNCTIONS =head2 startFullStack Start Oracle CRS stack in blocking mode. This subroutine can only work in post-12.1, and it should be able to work for the following two cases: 1) The stack has been completely up 2) The stack has been partially started and running =head3 Parameters CRS home location. If undef, then current home is used. =head3 Returns SUCCESS - The stack started successfully. FAILED - Failed to start the stack/stack is partially up. WARNING - Stack failed to start due to CRS-1665 (wrong node role). =cut sub startFullStack { my $crshome = $_[0]; my $role = $_[1]; my $success = FAILED; my $CRSCTL; if (! $crshome) { $CRSCTL = crs_exec_path('crsctl'); } else { $CRSCTL = catfile($crshome, 'bin', 'crsctl'); } if ($role) { trace("Starting the stack in $role role"); } my @output1 = system_cmd_capture($CRSCTL, 'start', 'crs', '-wait'); my $rc = shift @output1; trace("The return value of blocking start of CRS: $rc"); if(0 == $rc) { my @output2 = system_cmd_capture($CRSCTL, 'check', 'crs'); $rc = shift @output2; if ((0 == $rc) && (scalar(grep(/4537/, @output2)) > 0) && (scalar(grep(/4529/, @output2)) > 0) && (scalar(grep(/4533/, @output2)) > 0)) { $success = SUCCESS; trace("Oracle CRS stack completely started and running"); print_lines(@output1); } } else { if (scalar(grep(/CRS-1665/, @output1)) > 0) { trace("Failed to start the stack in $role mode"); $success = WARNING; } else { trace("Failed to start the Oracle CRS stack"); print_lines(@output1); } } return $success; } =head1 EXPORTED FUNCTIONS =head2 check_resource_state Check the state of the crsd resources when the stack is started during an install or an upgrade. =head3 Parameters [0] CRS home =head3 Returns SUCCESS or FAILED =cut sub check_resource_state { my $crshome = $_[0]; my $success = SUCCESS; my $CRSCTL; if (! $crshome) { $CRSCTL = crs_exec_path('crsctl'); } else { $CRSCTL = catfile($crshome, 'bin', 'crsctl'); } # Checking if some crsd resources are online. The 'crsctl start crs' does not # fail if any of the crsd resources fail to start. # If the crsd asm resource is offline on the first node, it may not have # registered with the asm listener, which causes the storage resource to # fail to start on the second node. trace("Checking if mgmtdb and gns resources are online"); my $string = '"((ENABLED = 1) AND ((NAME st ora.mgmtdb) OR (NAME st ora.gns) '; if (((! $CFG->UPGRADE) && isFirstNodeToStart()) || ($CFG->UPGRADE && isLegacyASM() && isFirstNodeToUpgrade())) { trace("Checking if asm resource is online"); $string = $string.'OR (NAME st ora.asm)) '; } $string = $string.'AND (STATE = OFFLINE) AND (TARGET = ONLINE))"'; my @output1 = system_cmd_capture($CRSCTL, 'stat', 'res', '-w', $string, '-attr', '"NAME"', '-v'); my $rc = shift @output1; if (0 == $rc) { if (scalar(grep(/NAME/, @output1)) > 0) { #Trace out first offline resource name in list my @res_name =split /(?:NAME=|\s)/, $output1[0]; trace(" Resource '" . $res_name[1] . "' is offline."); print_error(584, $res_name[1]); $success = FAILED; } } else { $success = FAILED; trace("Failed to check the status of resources"); print_trace_lines(@output1); } return $success; } =head1 EXPORTED FUNCTIONS =head2 startExclCRS Start Oracle CRS stack in exclusive mode. This subroutine can only work in post-12.1. =head3 Parameters None =head3 Returns TRUE - The stack is completely up. FALSE - Failed to start the stack/stack is partially up. =cut sub startExclCRS { my $is_up = FALSE; my $CRSCTL = crs_exec_path('crsctl'); my $rc = system_cmd($CRSCTL, 'start', 'crs', '-excl'); trace("The return value of blocking start of CRS: $rc"); if(0 == $rc) { my @output = system_cmd_capture($CRSCTL, 'check', 'crs'); $rc = shift @output; if ((0 == $rc) && (scalar(grep(/4638/, @output)) > 0) && (scalar(grep(/4692/, @output)) > 0) && (scalar(grep(/4529/, @output)) > 0)) { $is_up = TRUE; trace("Oracle CRS stack completely started and running"); } } else { trace("Failed to start the Oracle CRS stack in exclusive mode"); } return $is_up; } =head1 EXPORTED FUNCTIONS =head2 stopFullStack Shut down Oracle CRS stack. If the stack has been down, this func should return TRUE. =head3 Parameters [0] "force" : Stop CRS with force option [1] CRS home location. If undef, then current home is used. =head3 Returns TURE - No CRS stack is running FALSE - Failed to shut down the stack =cut sub stopFullStack { my $force = $_[0]; my $crshome = $_[1]; my $is_down = FALSE; my $CRSCTL; if (! $crshome) { $CRSCTL = crs_exec_path('crsctl'); } else { $CRSCTL = catfile($crshome, 'bin', 'crsctl'); } my @cmd; if ($force eq "force") { @cmd = ($CRSCTL, 'stop', 'crs', '-f'); } else { @cmd = ($CRSCTL, 'stop', 'crs'); } my @output = system_cmd_capture(@cmd); my $rc = shift @output; # If the stack has been down, the return code is non-zero. # We don't want the output to go to stdout because it looks # like a problem and probably gives rise to concerns. trace("The return value of stop of CRS: $rc"); my @output1 = system_cmd_capture($CRSCTL, 'check', 'crs'); my $rc1 = shift @output1; #CRS-4047: No Oracle Clusterware components configured. #CRS-4639: Could not contact Oracle High Availability Services #We should look for CRS-4047 or CRS-4639 to check stack down. if ((0 == $rc1) && ((scalar(grep(/4639/, @output1)) > 0) || (scalar(grep(/4047/, @output1)) > 0))) { $is_down = TRUE; trace("Oracle CRS stack has been shut down"); } if ((0 != $rc) && $is_down) { trace("The stack was already down before stopping it"); } else { print_lines(@output); } return $is_down; } =head1 EXPORTED FUNCTIONS =head2 startOhasdOnlyInSIHA Start Oracle Restart with '-noautostart' option. =head3 Parameters [0] Oracle Restart home =head3 Returns TURE or FALSE =cut sub startOhasdOnlyInSIHA { my $hashome = $_[0]; my $is_up = FALSE; my $CRSCTL; if ($hashome) { $CRSCTL = crs_exec_path('crsctl', $hashome); } else { $CRSCTL = crs_exec_path('crsctl'); } my @output; my $rc; my @cmd = ($CRSCTL, 'start', 'has', '-noautostart'); trace("start Oracle Restart with '-noautostart' option"); $rc = run_as_user2($CFG->HAS_USER, \@output, @cmd); trace("Return value of start of HAS with '-noautostart': $rc"); if (0 == $rc) { print_lines(@output); @output = system_cmd_capture($CRSCTL, 'check', 'has'); $rc = shift @output; if ((0 == $rc) && (scalar(grep(/4638/, @output)) > 0)) { $is_up = TRUE; trace("Oracle High Availability Services is online in SIHA"); } if(! $is_up) { trace("Failed to start OHASD only in SIHA"); } } else { trace("Failed to start OHASD only in SIHA"); } return $is_up; } =head1 EXPORTED FUNCTIONS =head2 startOhasdOnly Start Oracle CRS stack with '-noautostart' option. =head3 Parameters [0] CRS home =head3 Returns TURE or FALSE =cut sub startOhasdOnly { my $crshome = $_[0]; my $is_up = FALSE; my $CRSCTL; if ($crshome) { $CRSCTL = crs_exec_path('crsctl', $crshome); } else { $CRSCTL = crs_exec_path('crsctl'); } my @output = system_cmd_capture($CRSCTL, 'start', 'crs', '-noautostart'); my $rc = shift @output; trace("Return value of start of CRS with '-noautostart': $rc"); if (0 == $rc) { print_lines(@output); @output = system_cmd_capture($CRSCTL, 'check', 'has'); $rc = shift @output; if ((0 == $rc) && (scalar(grep(/4638/, @output)) > 0)) { $is_up = TRUE; trace("Oracle High Availability Services is online"); } if(!$is_up) { trace("Failed to start OHASD only"); } } else { trace("Failed to start OHASD only"); } return $is_up; } =head1 EXPORTED FUNCTIONS =head2 startExclNoCRS Start Oracle Clusterware in exclusive mode without starting CRS =head3 Parameters [0] CRS home =head3 Returns TURE or FALSE =cut sub startExclNoCRS { my $crshome = shift; my $is_up = FALSE; my $CRSCTL = catfile($crshome, 'bin', 'crsctl'); my $rc = system_cmd($CRSCTL, 'start', 'crs', '-excl', '-nocrs'); trace("Return value of starting CRS with '-excl' and '-nocrs' options: $rc"); if(0 == $rc) { my @output = system_cmd_capture($CRSCTL, 'check', 'crs'); $rc = shift @output; if ((0 == $rc) && (scalar(grep(/4638/, @output)) > 0) && (scalar(grep(/4529/, @output)) > 0)) { $is_up = TRUE; trace("Successfully start Oracle Clusterware in exclusive mode ". "without CRSD running"); } } else { trace("Failed to start Oracle Clusterware in exclusive mode with -nocrs"); } return $is_up; } =head1 EXPORTED FUNCTIONS =head2 createMgmtdbDir Create management DB directory for DBCA =head3 Parameters None =head3 Returns None =cut sub createMgmtdbDir { if (($CFG->defined_param('MGMT_DB')) && ("true" eq lc($CFG->params('MGMT_DB'))) && (! isRemoteGIMR()) && (! isOCRonASM())) { trace("Creating management DB directory"); my $ocr_loc = s_get_config_key("ocr", "ocrconfig_loc"); # check if it's a symbolic link if (-l $ocr_loc) { $ocr_loc = s_getAbsLink($ocr_loc); } my $pdir = dirname($ocr_loc); if (! -e $pdir) { die(dieformat(22, $pdir)); } my $CLUSTER_NAME = $CFG->params('CLUSTER_NAME'); my $clustername_dir = catdir($pdir, $CLUSTER_NAME); my $mgmtdb_dir = catdir($pdir, $CLUSTER_NAME, 'mgmtdb'); # MGMTDB directory format://mgmtdb create_dir($mgmtdb_dir); if (! -e $mgmtdb_dir) { trace("Failed to create management DB directory '$mgmtdb_dir'"); die(dieformat(22, $mgmtdb_dir)); } s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $clustername_dir) or die(dieformat(152, $clustername_dir)); s_set_perms("0750", $clustername_dir) or die(dieformat(153, $clustername_dir)); s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $mgmtdb_dir) or die(dieformat(152, $mgmtdb_dir)); s_set_perms("0750", $mgmtdb_dir) or die(dieformat(153, $mgmtdb_dir)); trace("Created management DB directory successfully"); } else { trace("No need to create management DB directory"); } } # For checking version greater than or equal to 1201 sub isVersionGTE1201 { my $is121ver; my @crs_ver = split(/\./, getcrsrelver()); if (int($crs_ver[0]) >= int('12')) { $is121ver = TRUE; }else { $is121ver = FALSE; } return $is121ver; } # For checking version greater than or equal to 12.2 sub isVersionGTE122 { my $is122ver; my @crs_ver = split(/\./, getcrsrelver()); if ( ((int($crs_ver[0]) >= int('12')) && (int($crs_ver[1]) >= int('2'))) || (int($crs_ver[0]) >= int('13')) ) { $is122ver = TRUE; }else { $is122ver = FALSE; } return $is122ver; } =head2 get_olsnodes_info Gets olsnodes output for given command line params. =head3 Parameters string with olsnodes home location. If undef, then current home is used. =head3 Returns =head4 returns a list of strings with node names. Warning messages, if any, are filtered out. =head4 result code (0 for success) as a first member of array. =cut sub get_olsnodes_info { my @nodes = (); my ($home, @args) = @_; my $cmd; if (! defined $home) { $cmd = crs_exec_path('olsnodes'); } else { $cmd = catfile( $home, 'bin', 'olsnodes' ); } # run olsnodes w/given pars my @out = system_cmd_capture(($cmd, @args)); my $rc = shift @out; # read-in interface list if (0 == $rc) { trace "---Got olsnodes out ($cmd ".join(' ',@args)."):"; foreach (0..$#out) { my $node = $out[$_]; trace $node ; # total failure should return rc, else filter out non-fatal # error messages, if any, e.g. "PRCO-nn: error....." if ($node !~ /^PR[A-Z]+-[0-9]+: /) { push @nodes, $node ; } else { trace( $node ); print_error(174); } } } else { push @nodes, "$cmd ".join(' ',@args)." failed."; } return ($rc, @nodes); } =head1 EXPORTED FUNCTIONS =head2 getNodeNumList Gets a list of node numbers =head3 Parameters [0] CRS home (input) =head3 Returns A string that looks like: :,:... otherwise, returns undef string =cut sub getNodeNumList { my $crshome = $_[0]; my $rc = 0; my @olsnodes_out; my $nodeNumList; ($rc, @olsnodes_out) = get_olsnodes_info($crshome, '-n'); if (0 != $rc) { trace("olsnodes -n (". join(' ' , @olsnodes_out). ")"); return $nodeNumList; } foreach my $elem (@olsnodes_out) { chomp($elem); $elem =~ s/\s+/:/; if ($nodeNumList) { $nodeNumList = "$nodeNumList," . "$elem"; } else { $nodeNumList = $elem; } } trace("Node number list: {$nodeNumList}"); return $nodeNumList; } =head1 EXPORTED FUNCTIONS =head2 getCurrentNodenameList Gets the list of current nodes. =head3 Parameters None =head3 Returns @node_list - list of nodes =cut sub getCurrentNodenameList { my @node_list = split (/,/, $CFG->params('NODE_NAME_LIST')); return @node_list; } =head1 EXPORTED FUNCTIONS =head2 getLocalNodeName Gets the name of local node =head3 Parameters [0] CRS home (input) =head3 Returns A string that represents the name of local node otherwise, returns undef string =cut sub getLocalNodeName { my $crshome = $_[0]; my $rc = 0; my @olsnodes_out; my $locNodeNm; ($rc, @olsnodes_out) = get_olsnodes_info($crshome, '-l'); if ((0 != $rc) || (0 == scalar(@olsnodes_out))) { trace("olsnodes -l (". join(' ' , @olsnodes_out). ")"); return $locNodeNm; } $locNodeNm = $olsnodes_out[0]; chomp($locNodeNm); trace("Local node name: {$locNodeNm}"); return $locNodeNm; } =head1 EXPORTED FUNCTIONS =head2 isASMConfigured Check if ASM configured =head3 Parameters [0] CRS home (input) =head3 Returns TRUE if configured FALSE if not configured =cut sub isASMConfigured { my $crshome = $_[0]; my $crsctl = catfile($crshome, 'bin', 'crsctl'); my @output = system_cmd_capture($crsctl, "stat", "res", "-w", "\"TYPE = ora.diskgroup.type\""); my $rc = shift @output; if (0 != $rc) { print_lines(@output); trace("crsctl stat res failed with status $rc"); die(dieformat(180, "crsctl stat res")); } if (0 == scalar(@output)) { trace("ASM is not configured"); return FALSE; } trace("ASM is configured"); return TRUE; } sub add_localOlr_OlrConfig_OcrConfig #------------------------------------------------------------------------------- # Function: add local olr to crsconfig_fileperms file # Args : none #------------------------------------------------------------------------------- { my $SUPERUSER = $CFG->SUPERUSER; my $ORACLE_OWNER = $CFG->params('ORACLE_OWNER'); my $ORA_DBA_GROUP = $CFG->params('ORA_DBA_GROUP'); my $platform = $CFG->platform_family; my $OLR_LOCATION = $CFG->OLR_LOCATION; my ($OCRCONFIG, $OLRCONFIG); # open crsconfig_fileperms my $permsfile = catfile($CFG->ORA_CRS_HOME, 'crs', 'utl', $CFG->HOST, 'crsconfig_fileperms'); open (FPFILE, ">>$permsfile") || die(dieformat(208, $permsfile, $!)); # add OLR_LOCATION if ($CFG->SIHA) { print FPFILE "$platform $OLR_LOCATION $ORACLE_OWNER $ORA_DBA_GROUP 0600\n"; } else { print FPFILE "$platform $OLR_LOCATION $SUPERUSER $ORA_DBA_GROUP 0600\n"; } if ($platform eq "unix") { $OCRCONFIG = $CFG->params('OCRCONFIG'); $OLRCONFIG = $CFG->params('OLRCONFIG'); # add OLRCONFIG if ($OLRCONFIG) { if (is_dev_env()) { print FPFILE "$platform $OLRCONFIG " . "$ORACLE_OWNER $ORA_DBA_GROUP 0644\n"; } else { print FPFILE "$platform $OLRCONFIG " . "$SUPERUSER $ORA_DBA_GROUP 0644\n"; } } # add OCRCONFIG if (! $CFG->SIHA) { if ($OCRCONFIG) { print FPFILE "$platform $OCRCONFIG $SUPERUSER " . "$ORA_DBA_GROUP 0644\n"; } } } close (FPFILE); } =head1 EXPORTED FUNCTIONS =head2 extractDiskgroups Querying CSS vote disks for getting DGs =head3 Parameters None =head3 Returns An array containing DGs =cut sub extractDiskgroups { my @dglist; my @css_votedisk = queryVoteDisks(); foreach my $votedisk (@css_votedisk) { trace("Voting disk is : $votedisk"); if ($votedisk =~ / \[(.+)\]/) { # E.g., $votedisk is: # '1. 2 282bf2a833f54f02bf4befd002fa90d6(/dev/raw/raw1) [OCRDG]' # parse $votedisk to get 'OCRDG' my $dg = $1; unless (@dglist ~~ /^$dg$/) { # Same diskgroup may be parsed from multiple lines. # The parsed value should be stored only when it's not in # the diskgroup array list. push (@dglist, $dg); } } } trace("Diskgroups found: @dglist"); return @dglist; } =head1 EXPORTED FUNCTIONS =head2 extractVotingFiles Querying CSS vote disks for getting voting Files Note: The subroutine is only applicable to the Client Cluster. =head3 Parameters None =head3 Returns An array containing voting Files =cut sub extractVotingFiles { my @dglist; my @votingfile; my @css_votedisk = queryVoteDisks(); # Example output of @css_votedisk # $css_votedisk[0] : ## STATE File Universal Id File Name Disk group # $css_votedisk[1]: -- ----- ----------------- --------- --------- # $css_votedisk[2]: 1. ONLINE 1ba3104ac10b4ff3bf9451fc7a5e265f (+RAIN/SLCGOD/VOTINGFILE/vfile.296.883028479) [RAIN] # $css_votedisk[3]: Located 1 voting disk(s). foreach my $votedisk (@css_votedisk) { trace("Voting disk is : $votedisk"); if ($votedisk =~ / \[(.+)\]/ && $votedisk =~ /\((.+)\)/) { # E.g., $votedisk is: # '1. ONLINE 1ba3104ac10b4ff3bf9451fc7a5e265f (+RAIN/SLCGOD/VOTINGFILE/vfile.296.883028479) [RAIN]' # parse $votedisk to get +RAIN/SLCGOD/VOTINGFILE/vfile.296.883028479 push (@votingfile, $1); } } trace("Voting files found: @votingfile"); return @votingfile; } =head1 EXPORTED FUNCTIONS =head2 removeVotingDisk Delete voting file(s) from the CSSD configuration when OCR is on ASM =head3 Parameters [0] CRS home [1] ASM diskgroup to store voting file(s) =head3 Returns SUCCESS or FAILED =cut sub removeVotingDisk { my $crshome = $_[0]; my $ASM_DISK_GROUP = $_[1]; my $crsctl; my $cmd; my @output; my $rc; my $dgname; if (! $crshome) { trace("Invalid CRS home"); return FAILED; } $crsctl = catfile($crshome, "bin", "crsctl"); $dgname = $ASM_DISK_GROUP; if ($CFG->platform_family ne "windows") { $ASM_DISK_GROUP = "'+$ASM_DISK_GROUP'"; } else { $ASM_DISK_GROUP = "+$ASM_DISK_GROUP"; } $cmd = "$crsctl delete css votedisk $ASM_DISK_GROUP"; @output = system_cmd_capture($cmd); $rc = shift @output; if (0 != $rc) { trace("crsctl delete for vds in $dgname ... failed"); return FAILED; } return SUCCESS; } =head1 EXPORTED FUNCTIONS =head2 removeAllVotingDisks Remove voting files on all DGs =head3 Parameters [0] asm mode =head3 Returns SUCCESS or FAILED =cut sub removeAllVotingDisks { my $asm_mode = $_[0]; my @ASM_DISK_GROUP = extractDiskgroups(); foreach my $VD (@ASM_DISK_GROUP) { if (! removeVotingDisk($CFG->ORA_CRS_HOME, $VD)) { print_error(510, $VD); return FAILED; } } if (ASM_MODE_CLIENT eq $asm_mode) { my @votingFiles = extractVotingFiles(); setOraHomeSID($asm_mode); foreach my $votingFile (@votingFiles) { trace("Removing voting file: $votingFile"); removeFileOnDG($asm_mode, $votingFile, FALSE); } } return SUCCESS; } =head1 EXPORTED FUNCTIONS =head2 addVotingDisks Add voting file(s) to the CSSD configuration when OCR is on ASM =head3 Parameters [0] CRS home [1] ASM diskgroup to store voting file(s) =head3 Returns SUCCESS or FAILED =cut sub addVotingDisks { my $crshome = $_[0]; my $ASM_DISK_GROUP = $_[1]; my $crsctl; my $addvfcmd; my $dgname; if (! $crshome) { trace("Invalid CRS home"); return FAILED; } $crsctl = catfile($crshome, 'bin', 'crsctl'); $dgname = $ASM_DISK_GROUP; if ($CFG->platform_family ne "windows") { $ASM_DISK_GROUP = "'+$ASM_DISK_GROUP'"; } else { $ASM_DISK_GROUP = "+$ASM_DISK_GROUP"; } trace("Creating voting files on ASM diskgroup $dgname"); $addvfcmd = "$crsctl replace votedisk $ASM_DISK_GROUP"; my @output = system_cmd_capture($addvfcmd); my $rc = shift @output; if (0 != $rc) { trace("Voting disks add failed"); return FAILED; } return SUCCESS; } =head1 EXPORTED FUNCTIONS =head2 removeVotingfiles Delete voting file(s) from the CSSD configuration when OCR is on filesystem =head3 Parameters [0] CRS home [1] Voting files(s) to be deleted =head3 Returns SUCCESS or FAILED =cut sub removeVotingfiles { my $crshome = $_[0]; my $VOTING_FILES = $_[1]; my $crsctl; my $delvfcmd; if (! $crshome) { trace ("Invalid CRS home"); return FAILED; } $crsctl = catfile($crshome, 'bin', 'crsctl'); $delvfcmd = "$crsctl delete css votedisk @$VOTING_FILES"; my @output = system_cmd_capture($delvfcmd); my $rc = shift @output; if (0 != $rc) { error("Voting files deletion failed"); return FAILED; } trace("Voting files deletion succeeded"); return SUCCESS; } =head1 EXPORTED FUNCTIONS =head2 addVotingFiles Add voting file(s) to the CSSD configuration when OCR is on filesystem =head3 Parameters [0] CRS home [1] Voting files(s) to be added =head3 Returns SUCCESS or FAILED =cut sub addVotingFiles { my $crshome = $_[0]; my $VOTING_FILES = $_[1]; my $crsctl; my $addvfcmd; if (! $crshome) { trace("Invalid CRS home"); return FAILED; } $crsctl = catfile($crshome, 'bin', 'crsctl'); $addvfcmd = "$crsctl add css votedisk @$VOTING_FILES"; my @output = system_cmd_capture($addvfcmd); my $rc = shift @output; if ($rc != 0) { trace("Voting files add failed"); return FAILED; } trace("Voting files add succeeded"); return SUCCESS; } =head1 EXPORTED FUNCTIONS =head2 start_crsd_and_check Start resource ora.crsd and check if it is online =head3 Parameters [0] CRS home =head3 Returns SUCCESS or FAILED =cut sub start_crsd_and_check { my $crshome = shift; my $cmd; my @chk; my $rc = -1; my $retries = 120; my $crsctl = catfile($crshome, 'bin', 'crsctl'); $cmd = "$crsctl start resource ora.crsd -init"; trace("Executing \'$cmd\'"); system_cmd("$cmd"); $cmd = "$crsctl check crs"; while ($retries--) { @chk = system_cmd_capture($cmd); $rc = shift @chk; if ((0 == $rc) && ((scalar(grep(/4537/, @chk)) > 0) || (scalar(grep(/4692/, @chk)) > 0))) { trace("The resource ora.crsd is online"); last; } sleep(5); } if ($retries > 0) { return SUCCESS; } else { return FAILED; } } =head1 EXPORTED FUNCTIONS =head2 start_excl_crs Start Oracle CRS stack in exclusive mode =head3 Parameters [0] CRS home =head3 Returns TRUE or FALSE =cut sub start_excl_crs { my $crshome = shift; my $cmd; my @output; my $rc = -1; my $is_up = FALSE; my $crsctl = catfile($crshome, 'bin', 'crsctl'); $cmd = "$crsctl start crs -excl"; trace("Executing \'$cmd\'"); $rc = system_cmd("$cmd"); trace("The return value of start of CRS exclusive mode: $rc"); $cmd = "$crsctl check crs"; @output = system_cmd_capture($cmd); $rc = shift @output; if ((0 == $rc) && (scalar(grep(/4638/, @output)) > 0) && (scalar(grep(/4692/, @output)) > 0) && (scalar(grep(/4529/, @output)) > 0)) { $is_up = TRUE; trace("Cluster Ready Services is online in exclusive mode"); } return $is_up; } =head1 EXPORTED FUNCTIONS =head2 isSIHA Check if an Oracle Restart home is configured =head3 Parameters None =head3 Returns TRUE or FALSE =cut sub isSIHA { my $ret= FALSE; my $local_only = s_get_config_key("ocr", "local_only"); if ($local_only =~ m/true/i) { $ret = TRUE; } return $ret; } =head1 EXPORTED FUNCTIONS =head2 stop_crsd_and_check Stop resource ora.crsd and check if it is offline =head3 Parameters [0] CRS home =head3 Returns SUCCESS or FAILED =cut sub stop_crsd_and_check { my $crshome = shift; my $cmd; my @chk; my $stop_rc = -1; my $rc = -1; my $crsctl = catfile($crshome, 'bin', 'crsctl'); $cmd = "$crsctl stop resource ora.crsd -init -f"; trace("Executing \'$cmd\'"); $stop_rc = system_cmd("$cmd"); trace("The return value of stop of ora.crsd: $stop_rc"); $cmd = "$crsctl check crs"; @chk = system_cmd_capture($cmd); $rc = shift @chk; if ((0 == $rc) && (scalar(grep(/4535/, @chk)) > 0)) { return SUCCESS; } else { return FAILED; } } sub isVersionMatch { my $ver1 = $_[0]; my $ver2 = $_[1]; my $status; chomp $ver1; chomp $ver2; trace("version1 is $ver1"); trace("version2 is $ver2"); if ($ver1 eq $ver2) { $status = TRUE; } else { $status = FALSE; } trace("Version match status is $status"); return $status; } sub check_NewCrsStack #------------------------------------------------------------------------------- # Function: Check if the stack is up from current CrsHome # Args : none #------------------------------------------------------------------------------- { # Forward merge fix for bug 12640884 # - LNX64-11203-UD: CRSCTL CHECK CSSD HANG, 10.2.0.5 -> 11.2.0.3 # Modify the check_NewcrsStack routine to not use the crsctl check command # for cssd, evmd and crsd and only use the check_service routine to get # the status trace("check new crs stack"); my $status = TRUE; my $new_crshome = $CFG->ORA_CRS_HOME; if ((!check_service("ora.cssd", 3)) && (!check_service("ora.crsd", 3)) && (!check_service("ora.evmd", 3))) { $status = FALSE; } if ($status) { trace ("Oracle Grid Infrastructure is running from $new_crshome"); } else { trace ("Oracle Grid Infrastructure is not running from $new_crshome"); } return $status; } =head1 EXPORTED FUNCTIONS =head2 getNodeStatus Gets the status of each node in the cluster =head3 Parameters [0] CRS home =head3 Returns A hash array containing active/inactive nodes. The active nodes are marked with '1'. =cut sub getNodeStatus { my $crshome = $_[0]; my $rc = 0; my @olsnodes_out; my %nodeStatus; ($rc, @olsnodes_out) = get_olsnodes_info($crshome, '-s'); if (0 != $rc) { die "Cannot get active nodes (" . join(' ',@olsnodes_out). ")"; } foreach my $nodeinfo (@olsnodes_out) { if ($CFG->DEBUG) { trace("nodeinfo: $nodeinfo"); } my @nodeinf = split(/\s+/, $nodeinfo); if ($nodeinf[1] =~ /Active/) { $nodeStatus{${nodeinf[0]}} = 1; } else { $nodeStatus{${nodeinf[0]}} = 0; } } return %nodeStatus; } =head1 EXPORTED FUNCTIONS =head2 getActiveNodeRoles Gets active node roles of the nodes in the cluster =head3 Parameters [0] CRS home =head3 Returns A hash array containing active node roles. =cut sub getActiveNodeRoles { my $crshome = $_[0]; my $rc = 0; my @olsnodes_out; my %nodeRoles; ($rc, @olsnodes_out) = get_olsnodes_info($crshome, '-a'); if (0 != $rc) { trace("Cannot get active node roles(" . join(' ',@olsnodes_out). ")"); return %nodeRoles; } foreach my $nodeinfo (@olsnodes_out) { trace("nodeinfo: $nodeinfo"); my @nodeinf = split(/\s+/, $nodeinfo); $nodeRoles{${nodeinf[0]}} = lc($nodeinf[1]); } return %nodeRoles; } sub add_rim_listener #------------------------------------------------------------------------------- # Function: Adds the rim listener except for Extended Cluster/ # Domain Services Cluster/Member Cluster. # Args : none # Returns : TRUE if 'srvctl add listener -leaflistener' succeeds # FALSE if 'srvctl add listener -leaflistener' fails #------------------------------------------------------------------------------- { my $success = TRUE; my $isExtendedCluster = (lc(trim($CFG->params('EXTENDED_CLUSTER'))) eq lc(CLUSTER_EXTENDED)); my $isMemberCluster = (lc(trim($CFG->params('CLUSTER_CLASS'))) eq lc(CLUSTER_CLASS_MEMBER)); if(isBigCluster() && (!$isExtendedCluster) && (!isDSCConfigured()) && (!$isMemberCluster)) { my $run_as_owner = TRUE; my $status = srvctl($run_as_owner, "add listener -leaflistener"); if (${status}) { trace ("srvctl add listener -leaflistener ... passed"); } else { $success = FALSE; } } return $success; } =head2 exec_cvu_baseline_collect The subroutine for executing CVU baseline collect command =head3 Parameters [0] baseline report name [1] the comma-separated list of node names =head3 Returns SUCCESS or FAILED =cut sub exec_cvu_baseline_collect { my $report_file = $_[0]; my $node_list = $_[1]; trace("Collecting CVU baseline ..."); my $cluvfy = crs_exec_path('cluvfy'); my $cmd = "$cluvfy comp baseline -collect cluster -n $node_list -rootscript -saveToAllNodes -reportname $report_file"; my $rc = run_as_user($CFG->params('ORACLE_OWNER'), $cmd); if (0 != $rc) { trace("Failed to collect CVU baseline"); return FAILED; } return SUCCESS; } =head1 EXPORTED FUNCTIONS =head2 checkGIStack Check CRS stack health =head3 Parameters CRS home location. If undef, then current home is used. =head3 Returns GI_STACK_DOWN : CRS stack is completely down GI_STACK_UP : CRS stack is fully up GI_STACK_PARTIAL: CRS stack is partially running =cut sub checkGIStack { my $crshome = $_[0]; my $stat = GI_STACK_DOWN; my $CRSCTL; if (! $crshome) { $CRSCTL = crs_exec_path('crsctl'); } else { $CRSCTL = catfile($crshome, 'bin', 'crsctl'); } my @output = system_cmd_capture($CRSCTL, 'check', 'crs'); my $rc = shift @output; if ((0 == $rc) && (scalar(grep(/4537/, @output)) > 0) && (scalar(grep(/4529/, @output)) > 0) && (scalar(grep(/4533/, @output)) > 0)) { $stat = GI_STACK_UP; trace("Oracle CRS stack is completely up"); } return $stat; } =head1 EXPORTED FUNCTIONS =head2 genTimeStamp Generates a time stamp in the format YYYY-MM-DD_hh-mm-ss(AM|PM) =head3 Parameters None =head3 Returns A string containing the time stamp. =cut sub genTimeStamp { my ($sec, $min, $hour, $day, $month, $year) = (tz_localtime()) [0, 1, 2, 3, 4, 5]; my $timeOfDay = "AM"; $year = $year + 1900; $month = $month + 1; if ($hour > 12) { $hour -= 12; $timeOfDay = "PM"; } return sprintf("%04d-%02d-%02d_%02d-%02d-%02d%s", $year, $month, $day, $hour, $min, $sec, $timeOfDay); } sub isReusedg { my $isreusedg = FALSE; if ($CFG->params('REUSEDG') =~ m/true/i) { $isreusedg = TRUE; } trace("Reuse Disk Group is set to $isreusedg"); return $isreusedg; } =head1 EXPORTED FUNCTIONS =head2 isFirstNodeToConfig Check if the local node is the first node to be configured =head3 Parameters [0] The name of local node [IN] =head3 Returns TRUE or FALSE =cut sub isFirstNodeToConfig { my $localNode = $_[0]; my $ret; if (isAddNode($localNode)) { trace("An add node scenario"); $ret = FALSE; } else { if ( isCkptexist("ROOTCRS_FIRSTNODE", "-global") && isCkptSuccess("ROOTCRS_FIRSTNODE", "-global")) { $ret = FALSE; } else { if (isForcedFirstNode()) { $ret = TRUE; } else { $ret = FALSE; } } } trace("ret=$ret; localNode=$localNode; isFirstNode=$ret"); return $ret; } #------------------------------------------------------------------------------- # Function: Prints the lines received to the console # Args : Lines to print # Returns : none #------------------------------------------------------------------------------- sub print_lines { output_lines(TRUE, FALSE, @_); } #------------------------------------------------------------------------------- # Function: Prints the lines received to the trace file # Args : Lines to print # Returns : none #------------------------------------------------------------------------------- sub trace_lines { output_lines(FALSE, TRUE, @_); } #------------------------------------------------------------------------------- # Function: Prints the lines received to the console and to the trace file # Args : Lines to print # Returns : none #------------------------------------------------------------------------------- sub print_trace_lines { output_lines(TRUE, TRUE, @_); } #------------------------------------------------------------------------------- # Function: Prints the lines received to the console and/or to the trace file # Args : [0] Boolean stating if the lines should be printed to the console # [1] Boolean stating if the lines should be sent to the trace file # [2..n] Lines to print # Returns : none #------------------------------------------------------------------------------- sub output_lines { my $print = shift @_; my $trace = shift @_; foreach my $line (@_) { if ($print) { print "$line\n"; } if ($trace) { trace($line); } } } ## The same log level setting support is used in the tests. # To set logging levels during startup, you can define an env variable # that specifies both deamons and components. The environment variable # is HAS_DAEMON_LOGGING and the format of the variable is: # #[~#[~...]] # For example: # ctss#CSSCLNT=5,CRSCCL=3 # will generate the following command: # crsctl set log ctss "CSSCLNT=5,CRSCCL=3" # and: # ctss#CSSCLNT=5,CRSCCL=3~css#CSSD=5 # will generate the following commands: # crsctl set log ctss "CSSCLNT=5,CRSCCL=3" # crsctl set log css "CSSD=5" sub set_logging { my $crsctl = catfile($CFG->ORA_CRS_HOME, 'bin', 'crsctl'); my @comps = split('~', $ENV{'HAS_DAEMON_LOGGING'}); for my $compspec (@comps) { my ($compname, $logset) = split('#', $compspec); trace("Setting logging levels of $compname to: $logset"); system_cmd_capture($crsctl, 'set', 'log', $compname, "\"$logset\""); } } # Bounce the ohasd so that the ohasd resources/types take effect # This also makes sure the ora.drivers.acfs or ora.asm is not online, # otherwise, 'acfsroot install' would fail to unload the driver that # is still in use in case of rerun. # # This must be between create_ohasd_resources and perform_installUSMDriver sub bounce_ohasd { trace("Attempt to bounce ohasd"); unless (stopFullStack("force")) { if ($CFG->DECONFIG && $CFG->FORCE) { print_error(349); $CFG->DECONFIG_ERROR(TRUE); return FAILED; } else { die(dieformat(349)); } } unless (startOhasdOnly()) { if ($CFG->DECONFIG && $CFG->FORCE) { print_error(117); $CFG->DECONFIG_ERROR(TRUE); return FAILED; } else { die(dieformat(117)); } } } # Bounce the ohasd in SIHA Environments so that the ohasd resources/types take # the installation user roles # # This must be after to perform_installUSMDriver sub bounce_ohasd_SIHA { trace("Attempt to bounce ohasd in SIHA"); stopFullStack_SIHA("force") || die(dieformat(348)); startOhasd_SIHA() || die(dieformat(460)); } # Stop SIHA Stack # # sub stopFullStack_SIHA { my $force = $_[0]; my $crshome = $_[1]; my $is_down = FALSE; my $CRSCTL; if (! $crshome) { $CRSCTL = crs_exec_path('crsctl'); } else { $CRSCTL = catfile($crshome, 'bin', 'crsctl'); } my @cmd; if ($force eq "force") { @cmd = ($CRSCTL, 'stop', 'has', '-f'); } else { @cmd = ($CRSCTL, 'stop', 'has'); } my @output = system_cmd_capture(@cmd); my $rc = shift @output; # If the stack has been down, the return code is non-zero. # We don't want the output to go to stdout because it looks # like a problem and probably gives rise to concerns. trace("The return value of stop of SIHA: $rc"); if (0 == $rc) { print_lines(@output); } @output = system_cmd_capture($CRSCTL, 'check', 'has'); $rc = shift @output; #CRS-4047: No Oracle Clusterware components configured. #CRS-4639: Could not contact Oracle High Availability Services #We should look for CRS-4047 or CRS-4639 to check stack down. if ((0 == $rc) && ((scalar(grep(/4639/, @output)) > 0) || (scalar(grep(/4047/, @output)) > 0))) { $is_down = TRUE; trace("Oracle SIHA stack has been shut down"); } return $is_down; } # # # sub startOhasd_SIHA { my $crshome = $_[0]; my $is_up = FALSE; my $CRSCTL; if ($crshome) { $CRSCTL = crs_exec_path('crsctl', $crshome); } else { $CRSCTL = crs_exec_path('crsctl'); } my @output = system_cmd_capture($CRSCTL, 'start', 'has'); my $rc = shift @output; trace("Return value of start of SIHA: $rc"); if (0 == $rc) { print_lines(@output); @output = system_cmd_capture($CRSCTL, 'check', 'has'); $rc = shift @output; if ((0 == $rc) && (scalar(grep(/4638/, @output)) > 0)) { $is_up = TRUE; trace("Oracle High Availability Services is online"); if ($CFG->UPGRADE) { trace("Starting CSS"); my ($rc1, @output1) = CSS_start(); if (! $rc1) { print_trace_lines(@output1); print_error(201); exit 1; } } } if(!$is_up) { trace("Failed to start OHASD"); } } else { trace("Failed to start OHASD"); } return $is_up; } sub getcrsrelver { my $home = $_[0]; my ($crsctl); my $verstring; if (! defined $home) { $crsctl = crs_exec_path('crsctl'); } else { $crsctl = catfile($home, 'bin', 'crsctl' ); } my @cmd = ($crsctl, 'query', 'crs', 'releaseversion'); my @out = system_cmd_capture(@cmd); my $rc = shift @out; # if succeeded, parse to ver numbers, output must be a single line, # version is 5 numbers, major to minor (see above) if ($rc == 0) { $verstring = getVerInfo($out[0]); trace( "Got CRS release version: ".join('.', $verstring) ); } else { error ("@cmd ... failed rc=$rc with message:\n @out \n"); } return $verstring; } #------------------------------------------------------------------------------- # Function: Returns the path to the qosctl script # Args : none # Returns : Path to the qosctl script #------------------------------------------------------------------------------- sub get_qosctl_path { my $pgm = $CFG->s_get_qosctl_path_p; return &$pgm(); } =head1 EXPORTED FUNCTIONS =head2 isHomeShared Check if the GI home is shared with the active nodes =head3 Parameters [0] Active nodes in the cluster =head3 Returns TRUE or FALSE =cut sub isHomeShared { my $activeNodes = $_[0]; my @active_nodes; if (! defined $activeNodes) { @active_nodes = split(/,/, $CFG->params('NODE_NAME_LIST')); } else { @active_nodes = @$activeNodes; } if (0 == scalar(@active_nodes)) { trace("The active node list is null"); return FALSE; } if (1 == scalar(@active_nodes)) { trace("Only one node is active"); return FALSE; } my $nodelist = join(',', @active_nodes); #use a path that's always writable in the grid home to do sharedness check. my $pathtochk = catdir($CFG->params('ORACLE_HOME'), 'crs', 'install'); my $status; my @program = ('-chkshare', -oh , $pathtochk, '-localnode', $CFG->HOST, '-nodelist', $nodelist); my $args_str = join(' ', @program); trace("checking if path $pathtochk is shared"); my ($rc, @capout) = cluutil(TRUE, $args_str); if ( $rc == 0 ) { if (scalar(grep(/TRUE/, @capout)) > 0) { trace("The oracle home " . $CFG->params('ORACLE_HOME') . " is shared "); $status = TRUE; } elsif (scalar(grep(/FALSE/, @capout)) > 0) { trace("The oracle home " . $CFG->params('ORACLE_HOME') . " is not shared "); $status = FALSE; } } else { print_lines(@capout); trace("cluutil $args_str failed with status $rc"); die(dieformat(180, "cluutil $args_str")); } return $status; } =head1 EXPORTED FUNCTIONS =head2 getLsnrUsername Retrieve the listener username =head3 Parameters None =head3 Returns Listener username =cut sub getLsnrUsername { my $lsnrUname; if (($CFG->defined_param('LISTENER_USERNAME')) && $CFG->params('LISTENER_USERNAME') && (($CFG->params('LISTENER_USERNAME')) !~ /^%/)) { $lsnrUname = $CFG->params('LISTENER_USERNAME'); } else { $lsnrUname = $CFG->params('ORACLE_OWNER'); } return $lsnrUname; } =head1 EXPORTED FUNCTIONS =head2 createNetLsnrWithUsername Create a network listener with given user name =head3 Parameters [0] The owner of listener [1] Listener name. When not specified, it defaults to "LISTENER" =head3 Returns None =cut sub createNetLsnrWithUsername { my $lsnr_username = $_[0]; my $lsnr_name = $_[1]; $lsnr_name = ($lsnr_name) ? ($lsnr_name) : "LISTENER"; if (isFirstNodeToStart()) { my $run_as_owner; my $status; if ($lsnr_username ne ($CFG->params('ORACLE_OWNER'))) { trace("Create Net Listener '$lsnr_name' with the username '$lsnr_username'"); $run_as_owner = FALSE; # run as root $status = srvctl($run_as_owner, "add listener -l ${lsnr_name} -user ${lsnr_username}"); } else { trace("Create Net Listener '$lsnr_name' as GI user"); $run_as_owner = TRUE; # run as Oracle owner $status = srvctl($run_as_owner, "add listener -l ${lsnr_name}"); } my $traceuser = ($lsnr_username ne ($CFG->params('ORACLE_OWNER'))) ? "-u $lsnr_username" : ""; if (TRUE == $status) { trace("add listener -l $lsnr_name " . "$traceuser" . " ... passed"); } else { exit(1); } } } =head1 EXPORTED FUNCTIONS =head2 stopNetLsnr Stop the given node listener =head3 Parameters [0] GI home [1] Listener name. When not specified, it defaults to "LISTENER" =head3 Returns None =cut sub stopNetLsnr { my $crs_home = $_[0]; my $lsnrName = $_[1]; $lsnrName = ($lsnrName) ? ($lsnrName) : "LISTENER"; trace("Stopping node listener '$lsnrName' ..."); my $lsnr_name; my @clunodes = split(',', $CFG->params('NODE_NAME_LIST')); foreach my $node (@clunodes) { if (isOldVersionLT112()) { $lsnr_name = $lsnrName."_".uc($node); trace("Created pre-11.2 style listener name: $lsnr_name"); my $srvctl = catfile($crs_home, 'bin', 'srvctl'); my $env = $ENV{'SRVM_TRACE'}; undef $SRVM_TRACE; my @output = system_cmd_capture($srvctl, 'stop', 'listener', '-n', $node, '-l', $lsnr_name); $ENV{'SRVM_TRACE'} = $env; my $rc = shift(@output); if ((0 == $rc) || (2 == $rc)) { trace("Successfully stopped listener '$lsnr_name' on node '$node'"); } else { print_lines(@output); die(dieformat(180, "srvctl stop listener -n $node -l $lsnr_name")); } } else { $lsnr_name = $lsnrName; my $cmd = "stop listener -l ${lsnr_name} -n ${node} -f"; my $run_as_owner = FALSE; # run as root my $status = srvctl($run_as_owner, $cmd, $crs_home); if (TRUE == $status) { trace("Successfully stopped listener '$lsnr_name' on node '$node'"); } else { exit(1); } } } } =head1 EXPORTED FUNCTIONS =head2 startNetLsnr Start the given node listener =head3 Parameters [0] Whether or not to start the listener on current node TRUE - on current node FALSE - on all nodes [1] Listener user name [2] Listener name. When not specified, it defaults to "LISTENER" =head3 Returns None =cut sub startNetLsnr { my $local_node = $_[0]; my $lsnr_usrname = $_[1]; my $lsnr_name = $_[2]; if ($local_node && isRimNode()) { trace("Bypass startig node listener on a leaf node"); return; } $lsnr_name = ($lsnr_name) ? ($lsnr_name) : "LISTENER"; trace("Start node listener '$lsnr_name' with user name '$lsnr_usrname'"); my $cmd; my $host = $CFG->HOST; my $srvctlbin = crs_exec_path('srvctl'); my @output; if ($local_node) { $cmd = "${srvctlbin} start listener -l ${lsnr_name} -n $host"; } else { $cmd = "${srvctlbin} start listener -l ${lsnr_name}"; } my @temp_env = setSrvctlTrace($lsnr_usrname); my $status = run_as_user2($lsnr_usrname, \@output, ${cmd}); resetSrvctlTrace($lsnr_usrname, \@temp_env); if ((0 == $status) || (2 == $status)) { trace("Successfully started node listener '$lsnr_name'"); } else { print_lines(@output); trace("srvctl start listener -l ${lsnr_name} failed with status $status"); die(dieformat(180, "srvctl start listener -l ${lsnr_name}")); } } =head1 EXPORTED FUNCTIONS =head2 startListeners Start all local listeners =head3 Parameters [0] Whether or not to start listeners on current node TRUE - on current node FALSE - on all nodes =head3 Returns TRUE or FALSE =cut sub startListeners { my $local_node = $_[0]; if ($local_node && isRimNode()) { trace("Bypass startig local listeners on a leaf node"); return TRUE; } my $success = TRUE; my ($cmd, $status); my $srvctl = catfile($CFG->ORA_CRS_HOME, "bin", "srvctl"); my $host = $CFG->HOST; my @output; if ($local_node) { $cmd = "${srvctl} start listener -n $host"; } else { $cmd = "${srvctl} start listener"; } my @temp_env = setSrvctlTrace(getLsnrUsername()); $status = run_as_user2(getLsnrUsername(), \@output, $cmd); resetSrvctlTrace(getLsnrUsername(), \@temp_env); if ((0 == $status) || (2 == $status)) { trace ("$cmd ... success"); } else { print_lines(@output); trace("$cmd failed with status $status"); print_error(180, $cmd); $success = FALSE; } return $success; } =head1 EXPORTED FUNCTIONS =head2 removeNetLsnr Remove the current network listener =head3 Parameters [0] GI home [1] Listener name. When not specified, it defaults to "LISTENER" =head3 Returns None =cut sub removeNetLsnr { my $crs_home = $_[0]; my $name = $_[1]; $name = ($name) ? ($name) : "LISTENER"; trace("Remove Net Listener [$name]"); my $lsnr_name; if (isOldVersionLT112()) { my $rc; my @output; my @clunodes = split(',', $CFG->params('NODE_NAME_LIST')); foreach my $node (@clunodes) { $lsnr_name = $name."_".uc($node); trace("Created pre-11.2 style listener name: $lsnr_name"); if (isOldVersionLT111()) { my $crs_unregister = catfile($crs_home, 'bin', 'crs_unregister'); my $resName = "ora\.".$node."\.".$lsnr_name."\.lsnr"; @output = system_cmd_capture($crs_unregister, $resName); $rc = shift(@output); if ((0 == $rc) || (scalar(grep(/CRS-2613/, @output)) > 0) || (scalar(grep(/CRS-0210/, @output)) > 0)) { trace("Successfully removed listener resource '$resName'"); } else { trace("crs_unregister $resName failed with status $rc"); die(dieformat(180, "crs_unregister $resName")); } } else { my $rc = srvctl_capture(FALSE, \@output, "remove listener -n $node -l", $crs_home); if ((0 == $rc) || (2 == $rc)) { trace("remove listener -n $node -l $lsnr_name ... passed"); } else { print_lines(@output); die(dieformat(180, "srvctl remove listener -n $node -l $lsnr_name")); } } } return; } $lsnr_name = $name; my $run_as_owner = FALSE; # run as root my $status = srvctl($run_as_owner, "remove listener -l ${lsnr_name} -f", $crs_home); if (TRUE == $status) { trace("remove listener -l $lsnr_name ... passed"); } else { exit(1); } } sub getCurrentNetLsnrs_GT112 { my $srvctl = $_[0]; my @cmd = ($srvctl, 'config', 'listener', '-S', '1'); my @output = system_cmd_capture(@cmd); my $rc = shift @output; my %netLsnrs; if (0 == $rc) { my @catch_expr = ('res_name={ora.(.+?).lsnr}', 'oh={(.+?)}', 'ports={(.+?)}'); foreach my $line ( @output ) { my ($name, $oh, $port); if ( $line =~ /$catch_expr[0]/ ) { $name = $1; if ( $line =~ /$catch_expr[1]/ ) { $oh = $1; if ( $line =~ /$catch_expr[2]/ ) { $port = $1; my %lsnrinfo = ( 'lsnr_home' => $oh, 'port' => $port ); $netLsnrs{$name} = \%lsnrinfo; trace("Listener recorded: "); } } } } return %netLsnrs; } else { if (scalar(grep(/PRCN-2044/, @output)) > 0) { trace("No listener exists"); } else { print_lines(@output); trace("srvctl config listener failed with status $rc"); die(dieformat(180, "srvctl config listener")); } return %netLsnrs; } } =head1 EXPORTED FUNCTIONS =head2 getCurrentNetLsnrs The subroutine for retrieving all existing listeners from a given CRS home =head3 Parameters [0] GI home =head3 Returns A hash array containing all existing listeners and listener homes =cut sub getCurrentNetLsnrs { my $crs_home = $_[0]; my %netLsnrs; trace("Retrieve all existing listeners from crs home: $crs_home"); my $rc; my @output; my $srvctl = catfile($crs_home, 'bin', 'srvctl'); if (isOldVersionLT112()) { my @clunodes = split(',', $CFG->params('NODE_NAME_LIST')); foreach my $node (@clunodes) { my $env = $ENV{'SRVM_TRACE'}; undef $SRVM_TRACE; @output = system_cmd_capture($srvctl, 'config', 'listener', '-n', $node); $ENV{'SRVM_TRACE'} = $env; $rc = shift(@output); if (0 == $rc) { foreach my $line (@output) { my $nodeName = (split(/\s+/, $line))[0]; my $lsnrName = (split(/\s+/, $line))[1]; my $ucNodeName = uc($nodeName); my $lsnrBase = $lsnrName; $lsnrBase =~ s/[_]$ucNodeName$//; if (isCkptPropertyExists("ROOTCRS_NODECONFIG", $lsnrBase)) { trace("Listener '$lsnrBase' has been upgraded, hence bypassing query ..."); next; } if (! defined($netLsnrs{$lsnrBase})) { my %lsnr_info; $lsnr_info{'lsnr_home'} = getLsnrHome($crs_home, $nodeName, $lsnrName); $lsnr_info{'port'} = -1; $netLsnrs{$lsnrBase} = \%lsnr_info; } } } else { print_lines(@output); trace("srvctl config listener -n $node failed with status $rc"); die(dieformat(180, "srvctl config listener -n $node")); } } } else { %netLsnrs = getCurrentNetLsnrs_GT112($srvctl); } trace("All existing listeners: []"); return %netLsnrs; } sub getLsnrHome { my $crs_home = $_[0]; my $node_name = $_[1]; my $lsnr_name = $_[2]; my $lsnr_home; my $rc; my @output; my $resName; $lsnr_name = ($lsnr_name) ? ($lsnr_name) : "LISTENER"; trace("Listener name: $lsnr_name"); if (isOldVersionLT112()) { $resName = "ora\.".$node_name."\.$lsnr_name"."\.lsnr"; trace("Pre-11.2 listener resource name: $resName"); my $crs_stat = catfile($crs_home, 'bin', 'crs_stat'); @output = system_cmd_capture($crs_stat, '-p', $resName); $rc = shift(@output); if (0 != $rc) { trace("crs_stat -p $resName failed with status $rc"); die(dieformat(180, "crs_stat -p $resName")); } } else { $resName = "ora\.".$lsnr_name."\.lsnr"; trace("Listener resource name: $resName"); my $crsctl = catfile($crs_home, 'bin', 'crsctl'); @output = system_cmd_capture($crsctl, "stat", "res", $resName, "-p"); $rc = shift(@output); if (0 != $rc) { print_lines(@output); trace("crsctl stat res $resName -p failed with status $rc"); die(dieformat(180, "crsctl stat res $resName -p")); } } foreach my $line (@output) { if ($line =~ /ACTION_SCRIPT/) { my $actionScript = (split(/=/, $line))[1]; $lsnr_home = (split(/bin/, $actionScript))[0]; trace("Listener home = $lsnr_home"); chop($lsnr_home); trace("Listener home after chopping: $lsnr_home"); last; } } if ($lsnr_home =~ /\%CRS_HOME\%/) { $lsnr_home = $CFG->OLD_CRS_HOME; } return $lsnr_home; } =head1 EXPORTED FUNCTIONS =head2 upgradeNetLsnrWithUsername Upgrade a network listener with given user name =head3 Parameters [0] The owner of listener [1] Listener name. When not specified, it defaults to "LISTENER" =head3 Returns None =cut sub upgradeNetLsnrWithUsername { my $lsnr_username = $_[0]; my $lsnr_home = $_[1]; my $lsnr_name = $_[2]; my $lsnr_port = $_[3]; my $status; my $run_as_owner; $lsnr_name = ($lsnr_name) ? ($lsnr_name) : "LISTENER"; trace("Upgrade net listener with username."); trace("lsnr_name=$lsnr_name,lsnr_user=$lsnr_username,home=$lsnr_home,port=$lsnr_port"); $run_as_owner = FALSE; # run as root my $cmd; if ( $lsnr_port != -1) { $cmd = "add listener -l ${lsnr_name} -oraclehome ${lsnr_home} -user ${lsnr_username} -endpoints ${lsnr_port} -upgrade"; } else { $cmd = "add listener -l ${lsnr_name} -oraclehome ${lsnr_home} -user ${lsnr_username} -upgrade"; } $status = srvctl($run_as_owner, $cmd); my $traceuser = ($lsnr_username ne ($CFG->params('ORACLE_OWNER'))) ? "-u $lsnr_username" : ""; if (TRUE == $status) { trace("add listener -l $lsnr_name -oraclehome $lsnr_home" . "$traceuser" . " -upgrade ... passed"); } else { exit(1); } } =head1 EXPORTED FUNCTIONS =head2 deleteMGMTDB Delete the management DB by running dropdb script =head3 Parameters [0] CRS home from which the management DB is running =head3 Returns SUCCESS or FAILED =cut sub deleteMGMTDB { my $mgmtdb_home = shift; my $ret = SUCCESS; my $DROPDB; my $cmd; trace("Dropping the Management DB ..."); $DROPDB = catfile($CFG->ORA_CRS_HOME, 'crs', 'install', 'dropdb'); $cmd = "$DROPDB $mgmtdb_home"; trace("Running dropdb standalone script..."); my @capout; my $status = run_as_user2($CFG->params('ORACLE_OWNER'), \@capout, ${cmd}); trace("Return code of 'dropdb': $status \nOutput: @capout"); if (0 != $status) { $ret = FAILED; print_lines(@capout); trace("Failed to drop Management DB"); } return $ret; } =head1 EXPORTED FUNCTIONS =head2 isMgmtdbConfigured Check if management DB is configured on server/client. =head3 Parameters [0] CRS home where "srvctl config mgmtdb" command should be executed =head3 Returns TRUE or FALSE =cut sub isMgmtdbConfigured { my $crsHome = $_[0]; my $run_as_owner = FALSE; my $config = TRUE; my ($rc, @output); $rc = srvctl_capture($run_as_owner, \@output, "config mgmtdb", $crsHome); my $cmdStr = "srvctl config mgmtdb"; if(0 != $rc) { if ((scalar(grep(/PRCR-1001/, @output)) > 0) || (scalar(grep(/PRKZ-1125/, @output)) > 0 )) { $config = FALSE; } else { print_lines(@output); die(dieformat(180, "srvctl config mgmtdb")); } } trace("isMgmtdbConfigured: $config"); return $config; } =head1 EXPORTED FUNCTIONS =head2 isMgmtdbRunningOnLocalNode Check if mgmtdb is running on the local node =head3 Parameters [0] CRS home =head3 Returns TRUE or FALSE =cut sub isMgmtdbRunningOnLocalNode { my $crshome = $_[0]; my $SRVCTL; my @output; my $rc; my $cmd; my $hostname = tolower_host(); my $run_as_owner = TRUE; $cmd = "status mgmtdb -S 1"; $rc = srvctl_capture($run_as_owner, \@output, $cmd, $crshome); # Output as follows: # #@=result[0]: dbunique_name={_mgmtdb} inst_name={-MGMTDB} node_name={slc03zyw} # up={true} state_details={Open} internal_state={STABLE} if ((0 == $rc) || (2 == $rc)) { my $nodename; foreach my $line (@output) { if (($line =~ /dbunique_name=\{_mgmtdb\}/) && ($line =~ /node_name=\{(.+?)\}/)) { $nodename = $1; trace("Mgmtdb is running on node: $nodename; local node: $hostname"); if ($hostname eq lc($nodename)) { trace("Mgmtdb is running on the local node"); return TRUE; } } } } trace("Mgmtdb is not running on this node: $hostname"); return FALSE; } =head1 EXPORTED FUNCTIONS =head2 getActiveNodes Get the number of active nodes with the given node role =head3 Parameters [0] CRS home [1] A valid node role =head3 Returns A number greater or equal to 0 =cut sub getActiveNodes { my $crshome = $_[0]; my $node_role = $_[1]; trace("Counting the number of active $node_role node in the cluster"); my $nodeCount = 0; my %nodeRoles = getActiveNodeRoles($crshome); for my $clunode (keys %nodeRoles) { next if ($node_role ne $nodeRoles{$clunode}); $nodeCount++; } trace("There are $nodeCount active $node_role nodes in the cluster."); return $nodeCount; } =head1 EXPORTED FUNCTIONS =head2 discoverVotingFiles Querying CSS vote disks for getting DGs if voting files are put on ASM, or getting their locations if voting files are put on NAS. =head3 Parameters [0] A reference to an array that contains diskgroups or paths of voting files [OUT] =head3 Returns "1" if voting files are put on ASM, and the OUT parameter [0] returns a reference to an array containing diskgroups. "0" if voting files are put on NAS, and the OUT parameter [0] returns a reference to an array containing paths of voting files. =cut sub discoverVotingFiles { my $vfiles_ref = $_[0]; my @css_votedisk; my $currentHome = getCrsHome(); if (isOldVersionLT121() && ($currentHome ne $CFG->ORA_CRS_HOME)) { # Fix bug 23000412 @css_votedisk = queryVoteDisks_112(); } else { @css_votedisk = queryVoteDisks(); } foreach my $votedisk (@css_votedisk) { trace(" Line: $votedisk"); if ($votedisk =~ /\[(.+)\]/) { push (@$vfiles_ref, $1); } } if (scalar(@$vfiles_ref) > 0) { trace("Voting files found in ASM diskgroup: @$vfiles_ref"); return 1; } foreach my $votedisk (@css_votedisk) { if ($votedisk =~ /\[\]/) { my $vdisk; my ($dummy, $text) = split (/\(/, $votedisk); ($vdisk, $dummy) = split (/\)/, $text); push (@$vfiles_ref, $vdisk); } } trace ("Voting files found on NAS: @$vfiles_ref"); return 0; } # # Platform specific actions are done here. # Used by install and upgrade. This function is idempotent. # sub osd_setup { # perform platform-specific setup actions # only for fresh install if (SUCCESS != s_osd_setup()) { die(dieformat(193)); } else { trace("Platform specific setup actions are done"); } } =head1 EXPORTED FUNCTIONS =head2 isPathShared Check if the given path is shared between cluster nodes =head3 Parameters [0] A path to check, which should always be writable by ORACLE_OWNER [1] A string with the node list separated by commas, if the argument is not passed then NODE_NAME_LIST is used =head3 Returns TRUE or FALSE =cut sub isPathShared { my $pathToChk = $_[0]; my $nodelist = $_[1]; my @capout = (); my $rc = -1; my $status = FALSE; unless ($nodelist) { $nodelist = $CFG->params('NODE_NAME_LIST'); } my @program = ('-chkshare', -oh , $pathToChk, '-localnode', $CFG->HOST, '-nodelist', $nodelist); my $args_str = join(' ', @program); trace("Checking if path [$pathToChk] is shared"); ($rc, @capout) = cluutil(TRUE, $args_str, $CFG->params('ORACLE_HOME')); trace("Output: @capout"); if (0 == $rc) { if (scalar(grep(/TRUE/, @capout)) > 0) { trace("The path [$pathToChk] is shared"); $status = TRUE; } elsif (scalar(grep(/FALSE/, @capout)) > 0) { trace("The path [$pathToChk] is not shared"); $status = FALSE; } } else { print_lines(@capout); trace("cluutil $args_str failed with status $rc"); die(dieformat(180, "cluutil $args_str")); } return $status; } =head1 EXPORTED FUNCTIONS =head2 kfodListDiskgroups Getting all disk groups by issuing 'kfod op=groups' =head3 Parameters [0] CRS home =head3 Returns Array containing both the return code and disk group names: all disk groups used by Clusterware. =cut # Command Output Foramt: #-------------------------------------------------------------------------------- # Group Size Free Redundancy Name #================================================================================ # 1: 102400 Mb 97784 Mb EXTERN DATA sub kfodListDiskgroups { my $crshome = $_[0]; my $kfod; if (! $crshome) { $kfod = catfile($CFG->ORA_CRS_HOME, 'bin', 'kfod'); } else { $kfod = catfile($crshome, 'bin', 'kfod') } trace("Getting disk groups by using kfod"); my @cmd = ($kfod, 'op=groups'); my ($rc, @output) = system_cmd_capture(@cmd); my @diskgroups; if ($rc == 0) { foreach my $line (@output) { if ($line =~ /\s+[1-9]+:\s+/) { trace($line); my @elements = split(/\s+/, $line); my $dg = pop @elements; push(@diskgroups, $dg); } } trace("All diskgroups used by Clusterware: @diskgroups"); } else { print_lines(@output); trace(join(' ', @cmd) . " failed with status $rc"); print_error(180, join(' ', @cmd)); } return ($rc, @diskgroups); } # Query the ora.mgmtdb resource to get the value of the SPFILE attribute sub getMgmtdbSPfile { trace("Attempting to get SPFILE."); my $ora_home = shift; my $SPfile_params; # Query the ora.mgmtdb resource to get the SPFILE. my $crsctl = crs_exec_path('crsctl', $ora_home); my @cmd = ($crsctl, 'stat', 'res', 'ora.mgmtdb', '-p'); my ($rc, @output) = system_cmd_capture(@cmd); if ( $rc == 0 ) { foreach my $line (@output) { if ( $line =~ /SPFILE=/ ) { trace($line); $SPfile_params = (split(/=/, $line))[1]; trace("SPfile data: $SPfile_params"); } } } else { print_lines(@output); trace("crsctl stat res ora.mgmtdb -p failed with status $rc"); die(dieformat(180, "crsctl stat res ora.mgmtdb -p")); } return $SPfile_params; } # Create a new pfile for starting up the MGMTDB sub createMgmtdbPfile { my ($crshome, $spfile_params) = @_; my $PName = 'init-dropmgmtdb.ora'; my $pfile = catfile($crshome,'dbs', $PName); trace("Trying to creat a new pfile: $pfile"); if (-e $pfile) { trace("Detected $pfile exists. Now removing the old pfile and creating a new one."); unlink $pfile or warn "Could not unlink $pfile: $!"; } open (DIRSFILE, ">$pfile") or die(dieformat(208, $pfile, $!)); print DIRSFILE "SPFILE=\'$spfile_params\'\n"; close (DIRSFILE); s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $pfile); s_set_perms("0640", $pfile); trace("Successfully created a new pfile: $pfile"); my $installNode = getInstallNode(); if($CFG->FIRST && isNodeAlive($installNode)) { trace("Copy the pfile to installer node."); my $ret = copy_file_to_nodes($pfile,$pfile,TRUE,$installNode); if (SUCCESS != $ret) { trace("Failed to copy the parameter file '$pfile' " . "to remote nodes"); die(dieformat(186, $pfile, $pfile, $installNode, $ret)); } } return $pfile; } =head1 EXPORTED FUNCTIONS =head2 setCRSResourceUseAttr The function for enabling or disabling CRS resources when starting the stack =head3 Parameters [0] CRS home [1] the string value: "enable" or "disable" =head3 Returns TRUE or FALSE =cut sub setCRSResourceUseAttr { my $crshome = $_[0]; my $action = $_[1]; my $suc = FALSE; my $CRSCTL = catfile($crshome, 'bin', 'crsctl'); my $enable = 0; if ('enable' eq $action) { $enable = 1; } elsif ('disable' eq $action) { $enable = 0; } else { trace("Invalid action passed in: $action"); return $suc; } trace("Set RESOURCE_USE_ENABLED to $enable"); my @output = system_cmd_capture($CRSCTL, 'set', 'resource', 'use', $enable); my $rc = shift @output; if (0 == $rc) { $suc = TRUE; trace("Successfully set RESOURCE_USE_ENABLED to $enable"); } return $suc; } =head1 EXPORTED FUNCTIONS =head2 findActiveGIProcs The function for finding the active GI processes =head3 Parameters None =head3 Returns A hash array that contains PIDs and PID files of all running GI processes =cut sub findActiveGIProcs { trace("Finding running GI processes ..."); my %activePIDs; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $hostname = $CFG->HOST; my $outputDir = catdir($ORACLE_BASE, 'crsdata', $hostname, 'output'); my @pidFiles = glob(catfile($outputDir, "*.pid")); foreach my $pidFile (@pidFiles) { trace("Peek at the pid file <$pidFile>"); my @lines = read_file($pidFile); my $pid = trim($lines[0]); trace("PID in <$pidFile> is: $pid"); if ($pid =~ /^[0-9]+$/) { my $rc = kill(0, $pid); if ($rc) { if ($CFG->platform_family ne "windows") { my $procCheck = s_verify_PID($pid, $pidFile); next if (! $procCheck); } trace("The GI process identified by PID<$pid> is running"); $activePIDs{$pid} = $pidFile; } } else { trace("WARNING: pid '$pid' for $pidFile is non-numeric string"); } } return %activePIDs; } =head1 EXPORTED FUNCTIONS =head2 termActiveGIProcs The function for terminating the active GI processes =head3 Parameters A hash array that contains PIDs and PID files of active GI processes =head3 Returns None =cut sub termActiveGIProcs { my $pidsRef = $_[0]; my %activePIDs = %{$pidsRef}; my @pids = (keys %activePIDs); trace("Terminate following GI processes [@pids]"); my $csspid = undef; for my $pid (keys %activePIDs) { if ($activePIDs{$pid} =~ /ocssd/) { $csspid = $pid; } else { trace("Terminate the GI process with PID<$pid>"); kill(9, $pid); } } # ocssd is the last one getting killed if (defined $csspid) { trace("Terminate OCSSD process with PID <$csspid>"); kill(9, $csspid); } } =head1 EXPORTED FUNCTIONS =head2 saveOraHome Get the existing Oracle Home from environment variable 'ORACLE_HOME'. =head3 Parameters None =head3 Returns Existing Oracle Home in environment variable 'ORACLE_HOME' =cut sub saveOraHome { my $oraHome = $ENV{'ORACLE_HOME'}; trace("Saved the existing Oracle Home in environment variable: $oraHome"); return $oraHome; } =head1 EXPORTED FUNCTIONS =head2 resetOraHome Reset the environment variable 'ORACLE_HOME' as the value saved before. =head3 Parameters [0] Oracle Home saved by subroutine 'saveOraHome()' =head3 Returns SUCCESS =cut sub resetOraHome { my $oraHome = $_[0]; $ENV{'ORACLE_HOME'} = $oraHome; trace("Reset the environment variable 'ORACLE_HOME' as: $oraHome"); return SUCCESS; } sub setOraHomeSID { my $asm_mode = $_[0]; my $crsHome = $_[1]; if (! defined $crsHome) { $crsHome = getCrsHome(); } if (ASM_MODE_CLIENT ne $asm_mode) { my $crsctl = catfile($crsHome, "bin", "crsctl"); my @statcmd = ($crsctl, "stat", "res", "ora.asm", "-init", "-p"); open STATOUT, "@statcmd |"; my @output1 = ; close (STATOUT); my @txt = grep (/GEN_USR_ORA_INST_NAME/, @output1); trace("ASM SID attribute: @txt"); my ($key, $val) = split(/=/, $txt[0]); chomp($val); trace("ORACLE_SID = $val"); $ENV{'ORACLE_SID'} = $val; } $ENV{'ORACLE_HOME'} = $crsHome; trace("ORACLE_HOME = $crsHome"); } sub getASMMode { my $relVer = getcrsrelver(s_get_olr_file("crs_home")); return ASM_MODE_LEGACY if(-1 == versionComparison($relVer, "12.1.0.0.0")); my $rc = 1; my $asm_mode; my $global_gpnp_profile = get_peer_profile_file(FALSE); my $local_gpnp_profile = get_peer_profile_file(TRUE); trace("Global GPNP profile: $global_gpnp_profile"); trace("Local GPNP profile: $local_gpnp_profile"); # the gpnp profile is not copied to the global area until # push_clusterwide_gpnp_setup() is called. Use the node local profile in # that case. The global profile can be absent during initial configuration # on the first node only. if (-f $global_gpnp_profile) { trace("Try to read ASM mode from the global stage profile"); ($rc, $asm_mode) = gpnp_get_asm_mode($global_gpnp_profile); } if (0 != $rc) { if (-f $local_gpnp_profile) { trace("Try to read ASM mode from the node-specific profile"); ($rc, $asm_mode) = gpnp_get_asm_mode($local_gpnp_profile); } } if (0 != $rc) { trace("Unable to get ASM mode from Oracle Clusterware GPnP profile"); if (! $asm_mode) { trace("Undefined ASM mode: $asm_mode"); die(dieformat(509)); } else { $asm_mode = trim($asm_mode); if ($asm_mode eq '') { trace("ASM mode is not present in the profile, " . "hence defaults to legacy"); $asm_mode = ASM_MODE_LEGACY; } } } trace("ASM mode = $asm_mode"); return $asm_mode; } =head1 EXPORTED FUNCTIONS =head2 run_cmd_as_usr_with_backticks Function for running an external command as given user, using backticks to execute that command =head3 Parameters [0] A reference to an array that contains the command to be executed [1] User name =head3 Returns Array containing both the return code and the captured output =cut sub run_cmd_as_usr_with_backticks { my $cmdlistref = $_[0]; my $usr = $_[1]; my @cmdlist = @{$cmdlistref}; if ($CFG->platform_family eq 'unix') { my $SU = "/bin/su"; @cmdlist = ($SU, $usr, '-c \'', @cmdlist, '\''); } my $cmd = join(' ', @cmdlist); trace("Run command --$cmd-- as $usr"); my @output = `$cmd`; my $rc = $?; if (0 == $rc) { trace("$cmd successfully executed"); } else { my $retCode = $rc >> 8; trace("The command exited with the return code $retCode"); } chomp(@output); if (scalar(@output) > 0) { trace(join("\n> ", ("Command output:", @output)), "\n>End Command output"); } return ($rc, @output); } =head1 EXPORTED FUNCTIONS =head2 modify102Resources Modify the ACTION_SCRIPT attribute of 10.2 resources to use a variable CRS home '%ORA_CRS_HOME%' =head3 Parameters None =head3 Returns SUCCESS/FAILED =cut sub modify102Resources { my $run_as_owner = FALSE; my $status; trace("Modifying 10.2 resources"); $status = srvctl($run_as_owner, "upgrade model -pretb"); if ($status == TRUE) { trace ("'srvctl upgrade model -pretb' ... success"); return SUCCESS; } else { trace ("'srvctl upgrade model -pretb' ... failed"); return FAILED; } } =head1 EXPORTED FUNCTIONS =head2 removeFileOnDG Remove the given file or directory on DG. =head3 Parameters [0] asm mode [1] file or directory needed to be removed on DG [2] true if is directory, false otherwise =head3 Returns SUCCESS/FAILED =cut sub removeFileOnDG { my $asm_mode = $_[0]; my $file = $_[1]; my $isDir = $_[2]; my $asmcmd = catfile($CFG->ORA_CRS_HOME, "bin", "asmcmd"); trace("Remove ASM file on DG: $file"); my @cmd; my $cmdOptions = ($isDir) ? "-fr" : "-f"; if (ASM_MODE_CLIENT eq $asm_mode ) { @cmd = ($asmcmd, "--nocp", "--privilege sysdba", "rm", $cmdOptions, $file); } else { @cmd = ($asmcmd, "--nocp", "rm", $cmdOptions, $file); } my @capout; my $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@capout, @cmd); trace("Output: @capout;\nReturn Code: $rc."); if (($rc == 0) || (scalar(grep(/ASMCMD-8002/i, @capout)) > 0)) { return SUCCESS; } else { print_lines(@capout); if ($CFG->DECONFIG && $CFG->FORCE) { print_error(427, $file, "@cmd"); $CFG->DECONFIG_ERROR(TRUE); return FAILED; } else { die(dieformat(427, $file, "@cmd")); } } } =head1 EXPORTED FUNCTIONS =head2 startCrsWithoutResources Start CRS without starting the resources. GI Stack should be fully stopped before calling this function. =head3 Parameters [0] CRS home =head3 Returns None =cut sub startCrsWithoutResources { my $crshome = $_[0]; trace("Starting CRS without resources..."); trace("OHASD needs to be up for disabling CRS resource"); startOhasdOnly($crshome) || die(dieformat(117)); trace("Set the attribute to start CRS without starting the resources"); setCRSResourceUseAttr($crshome, 'disable') || die(dieformat(180, 'crsctl set resource use')); stopFullStack("force", $crshome) || die(dieformat(349)); startFullStack($crshome) || die(dieformat(117)); trace("Re-enable RESOURCE_USE_ENABLED"); setCRSResourceUseAttr($crshome, 'enable') || die(dieformat(180, 'crsctl set resource use')); trace("CRS w/o resources started successfully."); } #------------------------------------------------------------------------------- # Function: This function is used to initialize the permissions on the ADR # directories to the GI user by running clsecho as the GI user. # The ADR home is OB/diag/crs # Args: # Returns: #------------------------------------------------------------------------------- sub initialize_ADR_dirs { my $rc = -1; my @output = (); my @cmd = (); my $ret = SUCCESS; my $platform = s_get_platform_family(); my $CLSECHO; # Run CLSECHO as the GI user so the directories # are created with that user as the owner if ($platform eq 'windows') { $CLSECHO = crs_exec_path("clsecho.exe"); } else { $CLSECHO = crs_exec_path("clsecho"); } if (! (-x $CLSECHO)) { trace("The CRS executable file 'clsecho' does not exist."); return FAILED; } @cmd = ($CLSECHO, "-p has", "-f clsrsc", "-m 567", "-l"); $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @cmd); if (0 != $rc) { trace("Failed to execute the command \"@cmd\" (rc=$rc), ". "with the message:\n".join("\n", @output)."\n"); $ret = FAILED; } return $ret; } =head1 EXPORTED FUNCTIONS =head2 skipSharednessCheck This function compares the file system type of the "/" file system and the Oracle home file system, if both are the same, it returns TRUE to skip the sharedness check. =head3 Parameters None =head3 Returns TRUE/FALSE =cut sub skipSharednessCheck { if ($CFG->platform_family eq "windows") { trace("The sharedness check can be skipped on Windows."); return TRUE; } my $configuredGIHome = s_get_olr_file("crs_home"); my $rootFsType = s_getFSType("/"); my $gihomeFsType = s_getFSType($configuredGIHome); if ($rootFsType eq "" || $gihomeFsType eq "") { # failed to get the file system type, sharedness check cannot be skipped trace("The sharedness check cannot be skipped."); return FALSE; } elsif ($rootFsType eq $gihomeFsType) { trace("The file system types of root and GI home file system are the same."); trace("The sharedness check can be skipped."); return TRUE; } else { trace("The file system types of root and GI home file system are different."); trace("The sharedness check cannot be skipped."); return FALSE; } } =head2 initial_cluster_validation Perform validations for the cluster installation as well as initializes some component files =head3 Parameters None =head3 Returns None, errors result in termination of the script =cut sub initial_cluster_validation { validate_SICSS() || die(dieformat(290)); if (!CSS_CanRunRealtime($CFG)) { die(dieformat(294)); } if (isAddNode($CFG->HOST)) { if ((isOCRonASM()) && ($CFG->params('CRS_STORAGE_OPTION') != 1)) { $CFG->params('CRS_STORAGE_OPTION', 1); } elsif ((! isOCRonASM()) && ($CFG->params('CRS_STORAGE_OPTION') == 1)) { $CFG->params('CRS_STORAGE_OPTION', 2); } } } ####--------------------------------------------------------- #### Function for registering ohasd service # ARGS: 0 sub perform_register_ohasd { trace ("Registering ohasd"); my $srv = 'ohasd'; my $ckptName = "ROOTCRS_OHASD"; my $ckptStatus; my $oraohasd = $CFG->compOHASD; if (isCkptexist($ckptName)) { $ckptStatus = getCkptStatus($ckptName); trace("'$ckptName' state is $ckptStatus"); if (($ckptStatus eq CKPTSTART) || ($ckptStatus eq CKPTFAIL)) { trace("Unregistering OHASD service configirations"); $oraohasd->unregisterOHASDService(); $CFG->isNodeCrashed(FALSE); } elsif ($ckptStatus eq CKPTSUC) { trace("ohasd already registered"); $CFG->wipCkptName("ROOTCRS_STACK"); return SUCCESS; } } writeCkpt($ckptName, CKPTSTART); $CFG->wipCkptName($ckptName); my $rc; if (! $CFG->UPGRADE) { $rc = $oraohasd->configureCurrentNode($oraohasd->REGISTER_OHASD_SERVICE); } else { $rc = $oraohasd->upgradeCurrentNode($oraohasd->REGISTER_OHASD_SERVICE); } if ($rc == SUCCESS) { writeCkpt($ckptName, CKPTSUC); } else { die(dieformat(317)); } $CFG->wipCkptName("ROOTCRS_STACK"); trace("ohasd is now registered"); } sub perform_start_ohasd { my $oraohasd = $CFG->compOHASD; (! $CFG->UPGRADE) ? ($oraohasd->configureCurrentNode($oraohasd->START_OHASD_SERVICE)) : ($oraohasd->upgradeCurrentNode($oraohasd->START_OHASD_SERVICE)); } sub perform_configure_hasd { my $oraohasd = $CFG->compOHASD; my $ckptName = "ROOTCRS_INITRES"; my $ckptStatus; if (isCkptexist($ckptName)) { $ckptStatus = getCkptStatus($ckptName); trace("'$ckptName' state is $ckptStatus"); if (($ckptStatus eq CKPTSTART) || ($ckptStatus eq CKPTFAIL)) { trace("Removing OHASD init resources and types"); $oraohasd->cleanupOHASDResources(); $CFG->isNodeCrashed(FALSE); } elsif ($ckptStatus eq CKPTSUC) { trace("OHASD Resources are already configured"); $CFG->wipCkptName("ROOTCRS_STACK"); return SUCCESS; } } writeCkpt($ckptName, CKPTSTART); $CFG->wipCkptName($ckptName); (! $CFG->UPGRADE) ? ($oraohasd->configureCurrentNode($oraohasd->CREATE_OHASD_RESOURCES)) : ($oraohasd->upgradeCurrentNode($oraohasd->CREATE_OHASD_RESOURCES)); if (FAILED == setCurrentNodeRole()) { writeCkpt($ckptName, CKPTFAIL); exit(1); } else { trace("Successfully set the role for the local node"); } writeCkpt($ckptName, CKPTSUC); $CFG->wipCkptName("ROOTCRS_STACK"); return SUCCESS; } sub create_ohasd_resources { trace ("Creating OHASD resources and dependencies"); my $status = perform_configure_hasd(); if ($status) { trace ("Successfully created OHASD resources for cluster and ASM"); } else { print_error(195); } } sub crsctlResource { my @out; my $rc = -1; my ($action, $resName, $resType, $resAttr, $force) = @_; trace("$action '$resName' ..."); my $crsctl = crs_exec_path('crsctl'); if ($action eq "add") { my @cmd = ($crsctl, "add", "resource", $resName, "-attr", '"', join(',', @{$resAttr}), '"', "-type", $resType, "-init", $force); if (! is_dev_env() && ($CFG->platform_family eq "windows")) { push @cmd, '-buildowner'; } @out = system_cmd(@cmd); $rc = shift @out; if ($rc != 0) { die(dieformat(271, $resName)); } } elsif ($action eq "modify") { my @cmd = ($crsctl, "modify", "resource", $resName, "-attr", '"', $resAttr, '"', "-init"); @out = system_cmd(@cmd); $rc = shift @out; } elsif ($action eq "start" || $action eq "stop") { @out = system_cmd($crsctl, $action, "resource", $resName, "-init"); $rc = shift @out; } elsif ($action eq "delete") { @out = system_cmd_capture($crsctl, "delete", "resource", $resName, "-f", "-init"); $rc = shift @out; } else { trace("Unknown action '$action' for '$resName'"); return FAILED; } if ($rc == 0) { trace("Succesfully executed action: $action, for resource $resName"); } else { trace("Failed to $action for resource $resName"); return FAILED; } return SUCCESS; } ####---------------------------------------------------------- #### Function for checking if any OHASD resources have existed # ARGS: None # Returns: TRUE or FALSE sub areResTypeRegistered { my @output; my $rc = -1; my $crsctl = crs_exec_path('crsctl'); trace("Check if OHASD resource types have been registered"); @output = system_cmd_capture($crsctl, "stat", "type", "-init"); $rc = shift @output; if ($rc != 0) { error("Failed to check status of init resource types"); return FALSE; } my @typeNm = grep (/TYPE_NAME/, @output); foreach my $type (@typeNm) { my @val = split(/=/, $type); if ($val[1] =~ /ora\..+\.type/) { my $res = $val[1]; trace("A resource type with the name '$res' is registered"); return TRUE; } } trace("Not found any registered resource type"); return FALSE; } sub getcursoftversion { my $host = $_[0]; my $cmd; my @out; my $rc; my $verstring; my $oraocr = $CFG->compOCR; setConfiguredCRSHome(); trace("Getting current software version on $host"); # Fix bug 17455758: the function isOldVersionLT121 cannot safely be used # here because if getcursoftversion is invoked in sub get_oldconfig_info # to query the old CRS version for the join node case, # $CFG->oldconfig('ORA_CRS_VERSION') is empty. if ((! ($CFG->JOIN)) && (! isOldVersionLT121())) { return getsoftversion_woOCRDUMP($host); } my $crsctl = catfile(getConfiguredCRSHome(), 'bin', 'crsctl'); if ($CFG->SIHA) { $cmd = "$crsctl query has softwareversion"; } else { $cmd = "$crsctl query crs softwareversion $host"; } @out = system_cmd_capture("$cmd"); $rc = shift @out; if ($rc == 0) { $verstring = getVerInfo($out[0]); trace( "Got CRS softwareversion for $host: $verstring" ); } else { print_lines(@out); die(dieformat(484, $host)); } chomp $verstring; trace ("The software version on $host is $verstring"); return $verstring; } sub getsoftversion_woOCRDUMP { my $host = $_[0]; my $cmd; my @out; my $rc; my $verstring; my $lnode = tolower_host(); trace("Getting current software version on $host using crsctl"); my $crsctl = catfile(getConfiguredCRSHome(), 'bin', 'crsctl'); if ($CFG->SIHA) { $cmd = "$crsctl query has softwareversion"; @out = system_cmd_capture("$cmd"); $rc = shift(@out); } else { if (lc($host) =~ /\b$lnode\b/i) # if this is the local node { trace("Query CRS software version on the local host: $host"); $cmd = "$crsctl query crs softwareversion -l"; @out = system_cmd_capture("$cmd"); $rc = shift(@out); } else { trace("Query CRS software version on the remote node: $host"); $cmd = "$crsctl query crs softwareversion $host -n"; @out = system_cmd_capture("$cmd"); $rc = shift(@out); # When the stack on the local node is down, try to connect the OCR # and read from it. if ($rc != 0) { trace("Make an effort to directly read from OCR"); $cmd = "$crsctl query crs softwareversion $host"; @out = system_cmd_capture("$cmd"); $rc = shift(@out); } } } if ($rc == 0) { $verstring = getVerInfo($out[0]); trace( "Got CRS softwareversion for $host: $verstring"); } else { print_lines(@out); die(dieformat(484, $host)); } chomp $verstring; trace ("The software version on $host is $verstring"); return $verstring; } #------------------------------------------------------------------------------ # Function: Copy file from local path to remote path for given list of nodes if # the second parameter is true the copy will be performed aS # ORACLE_OWNER, otherwise it will perform as root. # Args : [0] Source file name # [1] Destination remote path # [2] True if copy as ORACLE_OWNER, FALSE otherwise # [3] Comma separated list of nodes to perform the copy # Returns : SUCCESS or FAILURE #------------------------------------------------------------------------------ sub copy_file_to_nodes { my $src = $_[0]; my $dst = $_[1]; my $run_as_owner = $_[2]; my $nodelist = $_[3]; my $crshome = $CFG->params('ORACLE_HOME'); my $nodeListFile; (undef, $nodeListFile) = tempfile(); open(NODELISTFILE, ">${nodeListFile}") or die(dieformat(207, $nodeListFile, $!)); print NODELISTFILE "NODE_NAME_LIST=$nodelist\n"; close (NODELISTFILE); s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $nodeListFile) or die(dieformat(152, $nodeListFile)); s_set_perms("0744", $nodeListFile) or die(dieformat(153, $nodeListFile)); my @args = ('-copy', '-sourcefile', $src, '-destfile', $dst, '-nodelistfile', $nodeListFile, '-oraclehome', $crshome); my $args_str = join(' ', @args); trace("Copying file $src to $dst in nodes $nodelist"); my ($rc, @capout) = cluutil($run_as_owner, $args_str); # Delete temporary file s_remove_file($nodeListFile); if (0 != $rc) { print_trace_lines(@capout); die(dieformat(186, $src, $dst, $nodelist, $rc)); } return SUCCESS; } sub logsDirectory { # Set the log directory to the DC_HOME so that the logs do not get deleted # by the deinstall operation. The DC_HOME is set only during a deinstall. if ($CFG->defined_param('HOME_TYPE')) { return $CFG->params('DC_HOME'); } return catfile($CFG->params('ORACLE_BASE'), 'crsdata', tolower_host(), 'crsconfig'); } sub writeGlobalCkpt #------------------------------------------------------------------------------- # Function: Add a global check point with its value to the global check point file # ARG1 : Check point Name # ARG2 : Check point value # ARG3 : "-transferfile" or undef # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- { my $ckptName = $_[0]; my $ckptState = $_[1]; my $transfer = $_[2]; my $crshome = $CFG->ORA_CRS_HOME; my $ORACLE_BASE = $CFG->params('ORACLE_BASE'); my $run_as_user = TRUE; my @program = ('-ckpt', '-global', '-oraclebase', $ORACLE_BASE, '-writeckpt', '-name', $ckptName, '-state', $ckptState, '-nodelist', $CFG->params('NODE_NAME_LIST')); if (($transfer eq "-transferfile") && !isOracleBaseShared()) { push(@program, $transfer); } my $args_str = join(' ', @program); my ($rc, @capout) = cluutil($run_as_user, $args_str, $crshome); if ($rc != 0) { print_lines(@capout) if (CKPTSUC eq $ckptState); trace("cluutil $args_str failed with status $rc"); trace("failed to write global ckpt '$ckptName' with status '$ckptState'"); die(dieformat(175, $ckptName, $ckptState, $rc)) if (CKPTSUC eq $ckptState); return FAILED; } else { trace("succeeded to write global ckpt '$ckptName' with status '$ckptState'"); return SUCCESS; } } sub pullGlobalCkptFileNIndex #----------------------------------------------------------------- # Function: Pull global check point file and index.xml file from a reachable node # ARGS : None # Returns : None #----------------------------------------------------------------- { my $global_ckpt_file = catfile ($CFG->params('ORACLE_BASE'), "crsdata", "\@global", "crsconfig", "ckptGridHA_global.xml"); my $global_ckpt_index = catfile ($CFG->params('ORACLE_BASE'), "crsdata", "\@global", "crsconfig", "index.xml"); if (isAddNode($CFG->HOST)) { verify_gpnp_dirs($CFG->ORA_CRS_HOME, $CFG->params('GPNPGCONFIGDIR'), $CFG->params('GPNPCONFIGDIR'), $CFG->HOST, $CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP')); my $rmtNodeList = $CFG->params('NODE_NAME_LIST'); my $orauser = $CFG->params('ORACLE_OWNER'); trace("Copying global check point file and index.xml from a reachable node."); copy_file_from_livenode($global_ckpt_file, $global_ckpt_file, $rmtNodeList) || die(dieformat(571, $global_ckpt_file, $rmtNodeList, $global_ckpt_file)); copy_file_from_livenode($global_ckpt_index, $global_ckpt_index, $rmtNodeList) || die(dieformat(571, $global_ckpt_index, $rmtNodeList, $global_ckpt_index)); } if ($CFG->JOIN) { my $rmtNode = $CFG->EXISTINGNODE; trace("Copying global check point file and index.xml from an existing node."); copy_file_from_node($global_ckpt_file, $rmtNode, $global_ckpt_file) || die(dieformat(571, $global_ckpt_file, $rmtNode, $global_ckpt_file)); copy_file_from_node($global_ckpt_index, $rmtNode, $global_ckpt_index) || die(dieformat(571, $global_ckpt_index, $rmtNode, $global_ckpt_index)); } } sub isOPCDom0 # ------------------------------------------------------- # Function : The OPC dom0 is identified by checking for the existence # of the /etc/nimbula_version file. # Args : none # Returns : TRUE or FALSE # ------------------------------------------------------- { my $file = catfile("/etc", "nimbula_version"); if ( -e $file) { trace("This is OPC dom0 environment"); return TRUE; } else { trace("This is not OPC dom0 environment"); return FALSE; } } sub isOPCDomu # ------------------------------------------------------- # Function : The OPC Domu is identified by checking # the OPC_CLUSTER_TYPE is DoM-U or not. # Args : none # Returns : TRUE or FALSE # ------------------------------------------------------- { if ( ($CFG->defined_param('OPC_CLUSTER_TYPE')) && (lc($CFG->params('OPC_CLUSTER_TYPE')) eq "dom-u") ) { trace("This is OPC dom-u environment"); return TRUE; } else { trace("This is not OPC dom-u environment"); return FALSE; } } sub isODADomu # ------------------------------------------------------- # Function : The ODA Domu is identified by checking # the ODA_CLUSTER_TYPE is DoM-U or not. # Args : none # Returns : TRUE or FALSE # ------------------------------------------------------- { if ( ($CFG->defined_param('ODA_CLUSTER_TYPE')) && (lc($CFG->params('ODA_CLUSTER_TYPE')) eq "dom-u") ) { trace("This is ODA dom-u environment"); return TRUE; } else { trace("This is not ODA dom-u environment"); return FALSE; } } =head1 EXPORTED FUNCTIONS =head2 isASMProxyConfigured Check if ASM proxy is configured =head3 Parameters [0] CRS home where "srvctl config asm -proxy" command should be executed =head3 Returns TURE or FALSE =cut sub isASMProxyConfigured { my $crsHome = $_[0]; my $config = TRUE; my @out; my $rc = srvctl_capture(FALSE, \@out, "config asm -proxy", $crsHome); if(0 != $rc) { if (scalar(grep(/PRCR-1001/, @out)) > 0) { $config = FALSE; } else { print_lines(@out); die(dieformat(180, "srvctl config asm -proxy")); } } trace("isASMProxyConfigured: $config"); return $config; } sub copy_file_from_node #----------------------------------------------------------------- # Function: copy a specified file from a specified node # ARG1 : source file name # ARG2 : source node where the file is copied from # ARG3 : local file name # Returns : SUCCESS or FAILED #----------------------------------------------------------------- { my $sourceFile = $_[0]; my $rmtNode = $_[1]; my $destFile = $_[2]; my $run_as_user = TRUE; my @cmd = ('-copy', '-sourcefile', $sourceFile, '-sourcenode', $rmtNode, '-destfile', $destFile, '-nodelist', $CFG->HOST); my $args_str = join(' ', @cmd); my ($rc, @capout) = cluutil($run_as_user, $args_str); if (0 != $rc) { trace("Execution of remote copy command failed"); return FAILED; } return SUCCESS; } #------------------------------------------------------------------------------- # Find a reachable one in remote node list, and then copy given file from the # reachable node # ARG1 : remote file name # ARG2 : local file name # ARG3 : remote node list # Returns : SUCCESS or FAILED #------------------------------------------------------------------------------- sub copy_file_from_livenode { my $rmtFile = $_[0]; my $locFile = $_[1]; my $rmtNodelist = $_[2]; my $run_as_user = TRUE; my $nodeListFile; (undef, $nodeListFile) = tempfile(); open(NODELISTFILE, ">${nodeListFile}") or die(dieformat(207, $nodeListFile, $!)); print NODELISTFILE "NODE_NAME_LIST=$rmtNodelist\n"; close(NODELISTFILE); s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $nodeListFile) or die(dieformat(152, $nodeListFile)); s_set_perms("0440", $nodeListFile) or die(dieformat(153, $nodeListFile)); my @args = ('-copy', '-sourcefile', $rmtFile, '-fromnodesfile', $nodeListFile, '-destfile', $locFile, '-nodelist', $CFG->HOST); my $args_str = join(' ', @args); my ($rc, @capout) = cluutil($run_as_user, $args_str); trace(@capout); s_remove_file($nodeListFile); if (0 != $rc) { trace("Execution of remote copy command failed"); return FAILED; } return SUCCESS; } =head1 EXPORTED FUNCTIONS =head2 compareFiles Compare the contents of two given files =head3 Parameters [0] File1 [1] File2 =head3 Returns TRUE if two files have identical contents, otherwise FALSE =cut sub compareFiles { my $file1 = $_[0]; my $file2 = $_[1]; my $rt = FALSE; trace("Comparing two files by content: $file1 and $file2"); unless (open(FILE1, "<$file1")) { trace("Could not open file $file1 for reading (error: $!)"); return $rt; } unless (open(FILE2, "<$file2")) { trace("Could not open file $file2 for reading (error: $!)"); return $rt; } my $file1Cnt = join("", ); my $file2Cnt = join("", ); close(FILE1); close(FILE2); if ($file1Cnt eq $file2Cnt) { trace("Two files have identical contents"); $rt = TRUE; } else { trace("Two files have different contents"); } return $rt; } sub get_has_version { my $home = $_[0]; my @ver; my $crsctl; if (! defined $home) { $crsctl = crs_exec_path('crsctl'); } else { $crsctl = catfile($home, 'bin', 'crsctl' ); } # run "crsctl query crs releaseversion" -- local CSS must be up # Example output: # Oracle Clusterware version on the local node is [11.2.0.0.2] my @cmd = ($crsctl, 'query', 'has', 'releaseversion'); my @out = system_cmd_capture(@cmd); my $rc = shift @out; my $verstring; # if succeeded, parse to ver numbers, output must be a single line, # version is 5 numbers, major to minor (see above) if ($rc == 0) { $verstring = getVerInfo($out[0]); @ver = split(/\./, $verstring); trace( "Got High Availability release version: ".join('.', @ver) ); } elsif ($rc != 0) { @cmd = ($crsctl, 'query', 'crs', 'releaseversion'); @out = system_cmd_capture(@cmd); $rc = shift @out; if ($rc == 0) { $verstring = getVerInfo($out[0]); @ver = split(/\./, $verstring); trace( "Got Single Instance CSS release version: ".join('.', @ver) ); } elsif ($rc != 0) { @cmd = ($crsctl, 'query', 'crs', 'activeversion'); @out = system_cmd_capture(@cmd); $rc = shift @out; if ($rc == 0) { $verstring = getVerInfo($out[0]); @ver = split(/\./, $verstring); trace( "Got Single Instance CSS release version: ".join('.', @ver) ); } else { print_lines(@out); my $mycmd = join(' ', @cmd); trace ("@cmd ... failed rc=$rc with message:\n @out \n"); die(dieformat(180, $mycmd)); } } } else { print_lines(@out); my $mycmd = join(' ', @cmd); trace ("@cmd ... failed rc=$rc with message:\n @out \n"); die(dieformat(180, $mycmd)); } return @ver; } =head1 EXPORTED FUNCTIONS =head2 enable_start_acfs This routine is used to enable or start ACFS volume resources by calling 'srvctl' =head3 Parameters [0] 'enable' or 'start' [1] Node name [2] A reference to an array of volume devices =head3 Returns None =cut sub enable_start_acfs { my $verb = $_[0]; my $nodename = $_[1]; my $dev_ref = $_[2]; my @volDevs = @{$dev_ref}; # On Windows, the volume devices must differently be handled as # an option passed to 'srvctl enable/start filesystem'. E.g.: # srvctl.bat start filesystem -device # "\\.\asm-volhome-369,\\.\asm-voldata-159" if ($CFG->platform_family eq 'windows') { for (my $i = 0; $i < scalar(@volDevs); $i++) { my $vol = $volDevs[$i]; $volDevs[$i] = "\\\\\.\\$vol"; } } my $dev_list = join(',', @volDevs); trace("Attempt made on $nodename to $verb disk devices '$dev_list' for ACFS"); my $run_as_owner = FALSE; my $cmd; if ($CFG->platform_family eq 'windows') { $cmd = "$verb filesystem -device \"$dev_list\" -node $nodename"; } else { $cmd = "$verb filesystem -device $dev_list -node $nodename"; } if (! srvctl($run_as_owner, $cmd)) { exit(1); } return; } =head1 EXPORTED FUNCTIONS =head2 disableRes The function for disabling the given CRS resource on the specific node =head3 Parameters [0] The name of resource to be disabled [1] On which node the resource will be disabled [2] Unsupported or not to use 'crsctl' CLI commands SV >= 12.1.0.2 : TRUE SV < 12.1.0.2 : FALSE [3] CRS home =head3 Returns None =cut sub disableRes { my $resName = $_[0]; my $srvName = $_[1]; my $unsupported = $_[2]; my $crshome = $_[3]; trace("Attempt to disable resource[$resName] on node[$srvName]"); my $CRSCTL; if ($crshome) { $CRSCTL = catfile($crshome, 'bin', 'crsctl'); } else { $CRSCTL = crs_exec_path('crsctl'); } my $attr = "\"ENABLED\@SERVERNAME($srvName)=0\""; my @cmd = ($CRSCTL, 'modify', 'resource', $resName, '-attr', $attr, ($unsupported == TRUE) ? '-unsupported' : ''); my @out = system_cmd_capture(@cmd); my $rc = shift @out; if (0 != $rc) { print_lines(@out); die(dieformat(602, $resName, $srvName)); } return; } =head1 EXPORTED FUNCTIONS =head2 queryEnabledAndOnlineRes Query CRSD for all eligible resources that are enabled and being online on the specified node =head3 Parameters [0] CRS home [1] The nodename as part of filter for query [2] The resource suffix like "acfs" or "advm", .etc [3] The array of strings of filter type like ("ora.volume.type"). Be careful, pass a reference array as parameter using "\". \@array. =head3 Returns A comma separated list including all qualified resources =cut sub queryEnabledAndOnlineRes { my $crshome = $_[0]; my $host = $_[1]; my $res_suffix = $_[2]; my $filter_type = ""; if (scalar(@_) > 3) { $filter_type = makeFilterType($_[3]); } my $CRSCTL; my @allres; my $reslist; if ($crshome) { $CRSCTL = catfile($crshome, 'bin', 'crsctl'); } else { $CRSCTL = crs_exec_path('crsctl'); } trace("Retrieve all 'ora.*.$res_suffix' resources that " . "are enabled and online on $host"); my $filter = "\"((ENABLED = 1) AND (NAME st ora.) AND (NAME en .$res_suffix) AND (STATE = ONLINE) AND (LAST_SERVER = $host)$filter_type)\""; my @output = system_cmd_capture($CRSCTL, 'stat', 'res', '-w', $filter); my $rc = shift @output; if (0 != $rc) { print_lines(@output); die(dieformat(603)); } foreach my $line (@output) { if($line =~ /NAME/) { trace("Entry: {$line}"); my @name = split(/=/, $line); my $resName = $name[1]; trace("Res name: $resName"); push(@allres, $resName); } } if (scalar(@allres) > 0) { $reslist = join(',', @allres); } return $reslist; } =head1 EXPORTED FUNCTIONS =head2 queryRes Query CRSD for all eligible resources in the cluster =head3 Parameters [0] CRS home [1] The resource suffix like "acfs" or "advm", .etc [2] The array of strings of filter type like ("ora.volume.type") Be careful, pass a reference array as parameter using "\". \@array. =head3 Returns A comma separated list including all qualified resources =cut sub queryRes { my $crshome = $_[0]; my $res_suffix = $_[1]; my $filter_type = ""; if (scalar(@_) > 2) { $filter_type = makeFilterType($_[2]); } my $CRSCTL; my @allres; my $reslist; if ($crshome) { $CRSCTL = catfile($crshome, 'bin', 'crsctl'); } else { $CRSCTL = crs_exec_path('crsctl'); } trace("Retrieve all 'ora.*.$res_suffix' resources in the cluster"); my $filter = "\"((NAME en .$res_suffix)$filter_type)\""; my @output = system_cmd_capture($CRSCTL, 'stat', 'res', '-w', $filter); my $rc = shift @output; if (0 != $rc) { print_lines(@output); die(dieformat(603)); } foreach my $line (@output) { if($line =~ /NAME/) { trace("Entry: {$line}"); my @name = split(/=/, $line); my $resName = $name[1]; trace("Res name: $resName"); push(@allres, $resName); } } if (scalar(@allres) > 0) { $reslist = join(',', @allres); } return $reslist; } =head1 INTERNAL FUNCTIONS =head2 makeFilterType Convert an array of types into a query filter string. =head3 Parameters [0] The array of strings of filter type like ("ora.volume.type") =head3 Returns A query filter string =cut sub makeFilterType { my @filter_array = @{$_[0]}; my $filter_string = ""; #We are expecting at least one type in the array if (@filter_array) { my $first_was_done = 0; $filter_string = " AND ("; foreach my $type (@filter_array) { trace("Adding res type: $type"); if ($first_was_done) { $filter_string .= " OR (TYPE = $type)"; } else { #First element in the list $filter_string .= "(TYPE = $type)"; $first_was_done = 1; } } $filter_string .= ")"; } trace("Returning query filter string: $filter_string"); return $filter_string; } =head1 EXPORTED FUNCTIONS =head2 disable_enable_filesystem This routine is used to disable or enable the file system by calling 'srvctl' =head3 Parameters [0] 'enable' or 'disable' [1] Volume device path =head3 Returns SUCCESS or FAILED =cut sub disable_enable_filesystem { my $verb = $_[0]; my $volDev = $_[1]; my $dev; if ($CFG->platform_family eq 'windows') { $dev = "\\\\\.\\$volDev"; } else { $dev = $volDev; } trace("Attempt made to $verb disk device '$dev' for ACFS"); my $run_as_owner = FALSE; my $cmd; if ($CFG->platform_family eq 'windows') { $cmd = "$verb filesystem -device \"$dev\""; } else { $cmd = "$verb filesystem -device $dev" } if (! srvctl($run_as_owner, $cmd)) { return FAILED; } return SUCCESS; } # ------------------------------------------------------------------------------ # Function: Gets the localtime making sure the correct time zone value is set. # This function behaves exactly as "localtime" call but before # returning the value it sets the time zone to the value of the time # zone set in the params file: $CFG->params('TZ'). # Windows expects an offset insteand of a named timezone, and the # parameter contains a named time zone and Windows works correctly # without setting the time zone, therefor if the root script is # executed under Windows, this function only gets the localtime. # # Returns: The same value as localtime but with the correct time zone set. # ------------------------------------------------------------------------------ sub tz_localtime { my $platform = s_get_platform_family(); if ($CFG && $CFG->can('params') && $CFG->defined_param('TZ') && ($ENV{'TZ'} ne $CFG->params('TZ')) && ($platform ne 'windows')) { $ENV{'TZ'} = $CFG->params('TZ'); tzset; } return localtime; } sub TraceOptions { my $options; if ($CFG->DOWNGRADE) { $options = $options . "-downgrade "; } if ($CFG->FORCE) { $options = $options . "-force "; } if ($CFG->LASTNODE) { $options = $options . "-lastnode "; } if ($options) { trace ("options=$options"); } } sub isOLRConfiguredCkpt #------------------------------------------------------------------------------ # Function: Check if OLR is already configured by checking the local ckpt # Args : None # Returns : TRUE if OLR is configured # FALSE if OLR is not configured #------------------------------------------------------------------------------ { my $ckptName = "ROOTCRS_OLR"; if (isCkptexist($ckptName)) { my $ckptStatus = getCkptStatus($ckptName); trace("'$ckptName' state is '$ckptStatus'"); if (isCkptSuccess($ckptName)) { trace("OLR is already initialized"); $CFG->wipCkptName("ROOTCRS_STACK"); return TRUE; } } trace("Initializing OLR now.."); writeCkpt($ckptName, CKPTSTART); $CFG->wipCkptName($ckptName); return FALSE; } # ------------------------------------------------------------------------------ # Function: Check whether the given IP address is an IPv6 address. # # Returns: TRUE / FALSE # ------------------------------------------------------------------------------ sub isIpv6 { my $ip = $_[0]; my @capout = (); my $rc = -1; my $status = FALSE; my @program = ('-isipv6', $ip); my $args_str = join(' ', @program); trace("Checking if '$ip' is IPv6"); ($rc, @capout) = cluutil(TRUE, $args_str, $CFG->params('ORACLE_HOME')); trace("Output: @capout"); if (0 == $rc) { if (scalar(grep(/TRUE/, @capout)) > 0) { trace("'$ip' is IPv6"); $status = TRUE; } elsif (scalar(grep(/FALSE/, @capout)) > 0) { trace("'$ip' is not IPv6"); $status = FALSE; } } else { print_lines(@capout); trace("cluutil $args_str failed with status $rc"); die(dieformat(180, "cluutil $args_str")); } return $status; } # ------------------------------------------------------------------------------ # Function: Expand the IPv6 address # E.g., 2606:b400:2:1845:: ==> 2606:b400:0002:1845:0:0:0:0 # # Returns: An IP address in its full textual representation. # ------------------------------------------------------------------------------ sub expandIpv6 { my $ip = $_[0]; trace("IP address before expansion: " . $ip); if ($ip =~ /::/) # E.g., 2606:b400:2:1845:: ==> 2606:b400:2:1845:0:0:0:0 { # count how many ':0:' need to be added into the IPv6 address my $temp_ip = $ip; my $count = ($temp_ip =~ s/:/#/g) - 2; if ($ip =~ /^::/) # e.g., ::2606:b400:2000:1845 { while ($count != 7) { $ip =~ s/::/::0:/; $count++; } } elsif ($ip =~ /::$/) # e.g., 2606:b400:2000:1845:: { while ($count != 7) { $ip =~ s/::/:0::/; $count++; } } else { while ($count != 6) # e.g., 2606:b400::2000:1845 { $ip =~ s/::/:0::/; $count++; } } $ip =~ s/::/:/; # the ':' at the beginning/end need to be removed $ip =~ s/^://; $ip =~ s/:$//; } # E.g., 2606:b400:2:1845:0:0:0:0 ==> 2606:b400:0002:1845:0000:0000:0000:0000 my @sub_ips = split(/:/,$ip); $ip = ""; foreach my $sub_ip (@sub_ips) { my $temp_sub_ip = $sub_ip; my $count = ($temp_sub_ip =~ s/./#/g); while ($count != 4) { $sub_ip = "0".$sub_ip; $count++; } $ip = $ip.$sub_ip.':'; } $ip =~ s/:$//; trace("IP address after expansion: " . $ip); return $ip; } # ------------------------------------------------------------------------------ # Function: Check if the test environment has emulated nodes. # # Returns: True if the test environment has emulated nodes, false otherwise. # ------------------------------------------------------------------------------ sub is_emulated_nodes { return (defined $ENV{'PCW_EMULATED_NODES'}) && ($ENV{'PCW_EMULATED_NODES'} > 0); } sub crsdResource { my $action = $_[0]; my $resource = $_[1]; my $crsctl = crs_exec_path('crsctl'); my $rc = 0; if ( $action eq 'status' ) { my @out = system_cmd_capture( $crsctl, 'status', 'resource', $resource ); if ( ( scalar( grep( /2613/, @out ) ) > 0 ) ) { trace("Resource $resource is not registered"); $rc = 0; } else { trace("Resource $resource is registered"); $rc = 1; } } elsif ( $action eq 'delete' ) { my @out = system_cmd_capture( $crsctl, 'delete', 'resource', $resource, '-f', '-i' ); my $status = shift @out; if ( $status != 0 ) { trace("Delete of resource $resource failed : @out"); $rc = 0; } else { trace("Delete of resource $resource successful"); $rc = 1; } } return $rc; } # This check make sure all leaf nodes have been deconfigured/downgraded before # deconfiguring/downgrading the last hub node sub checkLeafNodes { return SUCCESS if ($CFG->FORCE); my $leafnodes; my %nodeRoles; if ($CFG->DOWNGRADE) { my $activenode_roles; my $globalCkptName = "ROOTCRS_CLUSTERINFO"; my $prpt_nodeRoles = "CLUSTERNODE_ROLES"; if (! isCkptexist($globalCkptName, "-global") || ! isCkptSuccess($globalCkptName,, "-global")) { # ROOTCRS_CLUSTERINFO's value is not CKPTSUCCESS or does not exist means # first node upgrade failed, no need to check leaf node. return SUCCESS; } $activenode_roles = trim(getCkptPropertyValue($globalCkptName, $prpt_nodeRoles, "-global")); trace("nodes and roles: $activenode_roles"); my @output = split(',', $activenode_roles); foreach my $line (@output) { my @tempArray = split(':', $line); $nodeRoles{$tempArray[0]} = lc($tempArray[1]); } # remove the nodes that's been downgraded from %nodeRoles my $localNode = tolower_host(); my @downgradedNodes; foreach my $node (keys %nodeRoles) { if (($localNode ne $node) && isNodeAlive($node)) { my $remoteCrsHome = s_getRemoteCrsHome($node); trace("CRS home of remote node $node is $remoteCrsHome"); if ($remoteCrsHome ne $CFG->ORA_CRS_HOME) { trace("CRS home of remote node $node is not equal to the CRS ". "home of current node $localNode: ". $CFG->ORA_CRS_HOME); trace("$node has been downgraded."); push @downgradedNodes, $node; } } } foreach my $node (@downgradedNodes) { delete($nodeRoles{$node}); } } elsif ($CFG->DECONFIG) { %nodeRoles = getActiveNodeRoles($CFG->ORA_CRS_HOME); } if (! %nodeRoles) { if ($CFG->DOWNGRADE) { die(dieformat(617, tolower_host())); } elsif ($CFG->DECONFIG) { die(dieformat(618, tolower_host())); } } trace("Do not deconfigure/downgrade the last hub node if there are still " . "leaf nodes"); if (lc(NODE_ROLE_HUB) eq lc($nodeRoles{($CFG->HOST)})) { my $hubNodesLeft = 0; my $leafNodesLeft = 0; for my $clunode (keys %nodeRoles) { next if ($CFG->HOST =~ /^$clunode$/i); if (lc(NODE_ROLE_HUB) eq lc($nodeRoles{$clunode})) { trace("Still have hub nodes left."); $hubNodesLeft = 1; last; } if (lc(NODE_ROLE_RIM) eq lc($nodeRoles{$clunode})) { $leafNodesLeft = 1; $leafnodes = $leafnodes . $clunode . ","; } } if ((! $hubNodesLeft) && ($leafNodesLeft)) { $leafnodes =~ s/,$//; if ($CFG->DOWNGRADE) { die(dieformat(616, $leafnodes)); } elsif ($CFG->DECONFIG) { die(dieformat(451, $leafnodes)); } } } } # ------------------------------------------------------------------------------ # Function: Checks in the checkpoint if the oracle home path is shared across # the cluster. # # Returns: True if the oracle home is shared among nodes, false otherwise. # ------------------------------------------------------------------------------ sub isOracleHomeShared { my $OH_is_shared = FALSE; my $cached_value = $CFG->isOracleHomeShared_cache; my $ckpt_name = 'ROOTCRS_STACK'; my $property_name = 'ORACLE_HOME_SHARED'; if ($cached_value) { $OH_is_shared = ($cached_value eq 'true') ? TRUE : FALSE; } else { # If we currently have only one node, we won't touch anything # in the checkpoint file we just return true if ($CFG->params('NODE_NAME_LIST') !~ /,/) { $OH_is_shared = TRUE; } elsif (!is_dev_env()) { if (isCkptPropertyExists($ckpt_name, $property_name)) { my $stored_value = trim(getCkptPropertyValue($ckpt_name, $property_name)); $OH_is_shared = ($stored_value eq 'true') ? TRUE : FALSE; } else { my $OH = $CFG->params('ORACLE_HOME'); $OH_is_shared = isPathShared(catdir($OH, 'gpnp')); unless(writeCkptProperty($ckpt_name, $property_name, $OH_is_shared ? 'true' : 'false')) { die(die_format(619, $OH)); } } } $CFG->isOracleHomeShared_cache($OH_is_shared ? 'true' : 'false'); } trace("ORACLE_HOME is shared: $OH_is_shared"); return $OH_is_shared; } # ------------------------------------------------------------------------------ # Function: Checks in the checkpoint if the oracle base path is shared accross # the cluster. # # Returns: True if the oracle home is shared among nodes, false otherwise. # ------------------------------------------------------------------------------ sub isOracleBaseShared { my $OB_is_shared = FALSE; my $cached_value = $CFG->isOracleBaseShared_cache; my $ckpt_name = 'ROOTCRS_STACK'; my $property_name = 'ORACLE_BASE_SHARED'; if ($cached_value) { $OB_is_shared = ($cached_value eq 'true') ? TRUE : FALSE; } else { # If we currently have only one node, we won't touch anything # in the checkpoint file we just return true if ($CFG->params('NODE_NAME_LIST') !~ /,/) { $OB_is_shared = TRUE; } elsif (!is_dev_env()) { if (isCkptPropertyExists($ckpt_name, $property_name)) { my $stored_value = trim(getCkptPropertyValue($ckpt_name, $property_name)); $OB_is_shared = ($stored_value eq 'true') ? TRUE : FALSE; } else { my $OB = $CFG->params('ORACLE_BASE'); $OB_is_shared = isPathShared($OB); unless(writeCkptProperty($ckpt_name, $property_name, $OB_is_shared ? 'true' : 'false')) { die(die_format(619, $OB)); } } } $CFG->isOracleBaseShared_cache($OB_is_shared ? 'true' : 'false'); } trace("ORACLE_BASE is shared: $OB_is_shared"); return $OB_is_shared; } #------------------------------------------------------------------------------- # Function: Retrieves the node number with olsnodes -l -n # # Returns: The node number or zero on failure #------------------------------------------------------------------------------- sub getLocalNodeNumber { my $node_number = 0; my ($rc, @nodes) = get_olsnodes_info($CFG->ORA_CRS_HOME, '-l', '-n'); if ($rc == 0 && $nodes[0] =~ /\w+\s+(\d+)/) { $node_number = $1; } return $node_number; } =head1 EXPORTED FUNCTIONS =head2 startSihaStack Start ohasd using "crsctl start has" =head3 Parameters None =head3 Returns SUCCESS - ohasd started successfully. FAILED - Failed to start ohasd =cut sub startSihaStack { trace("Starting Oracle Restart"); my $success; my $crsctl = crs_exec_path('crsctl'); my @output = system_cmd_capture("$crsctl start has"); my $rc = shift @output; if (0 == $rc) { trace ("ohasd started successfully"); $success = SUCCESS; } else { trace ("failed to start ohasd"); $success = FAILED; } return $success; } # For the first node install/upgrade: # 1) stores the mapping between node:site in global variable: $CFG->node2site; # 2) Extended Cluster # 2.1) For Exadata, GUIDs will be retrieved from CELL; # A site associated with a QUORUM failure group might be on NAS and # not configured on a CELL. In that case, the site GUID will be # generated by CLSCFG. # 2.2) For Non-Exadata, GUID will be generated by CLSCFG; # 3) Non-Extended Cluster # 3.1) For Exadata, try to retrieve the GUID from the CELL. If # the site is configured use the GUID, otherwise generate it. # 3.2) For Non-Exadata, only one GUID is needed. # Cluster name will be used as the site name for cluster; # Node name will be used as the site name for SIHA; # 4) stores the mapping between site:GUID in global variable: $CFG->site2guid; # 5) stores the GUIDs in parameter file 'crsgenconfig_params' in the parameter # 'EXTENDED_CLUSTER_SITE_GUIDS', and push the parameter file # 'crsgenconfig_params' to all remote nodes. # # For non-first node install/upgrade: # 1) stores the mapping between node:site in global variable: $CFG->node2site; # 2) reads the GUIDs from parameter file 'crsgenconfig_params' and stores it in # global variable: $CFG->site2guid. # # For add node install: # 1) pulls the parameter file 'crsgenconfig_params' from a reachable node. sub gen_site_GUIDs { # Step I: get the sites information my @sites; # extended cluster will get its sites from parameter 'EXTENDED_CLUSTER' if (lc($CFG->params('EXTENDED_CLUSTER')) eq CLUSTER_EXTENDED) { @sites = split(/,/, $CFG->params('EXTENDED_CLUSTER_SITES')); trace("Sites retrieved from 'EXTENDED_CLUSTER_SITES': @sites"); } # non-extended cluster will get its site from its cluster/host name elsif ($CFG->SIHA) { my $hostname = tolower_host(); my $site4SIHA; if (length($hostname) > 15) { trace("The maximum length of the site name supported is 15 chars, " . "hence trimming the hostname $hostname to get the site name."); $site4SIHA = substr($hostname,0,15); } else { $site4SIHA = $hostname; } push @sites, $site4SIHA; trace("Site name for SIHA: $site4SIHA"); } else { push @sites, trim($CFG->params('CLUSTER_NAME')); } # Step II: prepare the hash variable $CFG->node2site configure_node2site(@sites); # Step III: prepare the hash variable $CFG->site2guid configure_site2guid(@sites); # Step IV: prepare the site/GUID argument for configuring/upgrading # OCR and OLR configure_site_guid_params(@sites); } # prepare the hash variable $CFG->node2site sub configure_node2site { my @sites = @_; my @NODES_N_SITES; my $NODE_NAME_LIST = lc($CFG->params('NODE_NAME_LIST')); my $isExtendedCluster = (lc($CFG->params('EXTENDED_CLUSTER')) eq CLUSTER_EXTENDED); if (! $CFG->SIHA) { # No leaf nodes in extended cluster # Get the nodes from HUB_NODE_LIST for extended cluster. For # non-extended cluster, get the nodes from the NODE_NAME_LIST. # For extended cluster, HUB_NODE_LIST is in format of: # node1:site1, node2:site2, etc. # For non-extended cluster, both HUB_NODE_LIST, RIM_NODE_LIST and # NODE_NAME_LIST do not have the cluster name. # They have only the comma separated list of nodes. if ($isExtendedCluster) { my $HUB_NODE_LIST = $CFG->params('HUB_NODE_LIST'); trace ("HUB_NODE_LIST: $HUB_NODE_LIST"); my @HUB_NODES_N_SITES = split (/,/, $HUB_NODE_LIST); push @NODES_N_SITES, @HUB_NODES_N_SITES; } else { trace("It is non-extended cluster. Get node list from NODE_NAME_LIST, " . "and site from cluster name."); trace ("NODE_NAME_LIST: $NODE_NAME_LIST"); @NODES_N_SITES = split (/,/, $NODE_NAME_LIST); } foreach my $node_n_site (@NODES_N_SITES) { if ($node_n_site =~ /:/) { my @item = split (/:/, $node_n_site); my $node = lc(trim($item[0])); my $site = trim($item[1]); trace("The site for node $node is: $site"); if (@sites ~~ /$site/) # if $site is in @sites { # site is case insensitive and case preserving my $lower_site = lc($site); $CFG->node2site($node,$lower_site); } else { trace("The site: $site for node: $node was not in " . "'EXTENDED_CLUSTER_SITES'."); die(dieformat(636, $node, $site)); } } elsif (!$isExtendedCluster) { # for non-extended cluster, we tolerate the HUB_NODE_LIST and # RIM_NODE_LIST containing only node without site. # E.g., HUB_NODE_LIST=, my $node = lc(trim($node_n_site)); my $site = join('', @sites); trace("The site for node $node is: $site"); # site is case insensitive and case preserving my $lower_site = lc($site); $CFG->node2site($node,$lower_site); } else { $node_n_site = trim($node_n_site); trace("Could not get the site for node: $node_n_site."); die(dieformat(634, $node_n_site)); } } } else # SIHA does not have parameters HUB_NODE_LIST and RIM_NODE_LIST { my $SIHA_nodename = tolower_host(); my $site = join('', @sites); trace("The site for SIHA node: $SIHA_nodename is: $site"); # site is case insensitive and case preserving my $lower_site = lc($site); $CFG->node2site($SIHA_nodename, $lower_site); } # Fix bug 21981335 if($CFG->JOIN && !$isExtendedCluster) { my $node = tolower_host(); my $site = join('', @sites); unless (@NODES_N_SITES ~~ /$node/) # if $node is NOT in @NODES { trace("$node is not in 'NODE_NAME_LIST'."); trace("The site for join node $node is: $site"); # site is case insensitive and case preserving my $lower_site = lc($site); $CFG->node2site($node,$lower_site); } } # add node case if (isAddNode($CFG->HOST)) { trace ("NODE_NAME_LIST: $NODE_NAME_LIST"); my @NODES = split (/,/, $NODE_NAME_LIST); trace("Node names retrieved from 'NODE_NAME_LIST': @NODES"); my @ADD_NODE_LIST = split(/,/, $CFG->params('CLUSTER_NEW_HOST_NAMES')); trace("CLUSTER_NEW_HOST_NAMES: @ADD_NODE_LIST"); if (!$isExtendedCluster) { my $site = join('', @sites); foreach my $ADD_NODE (@ADD_NODE_LIST) { $ADD_NODE = lc(trim($ADD_NODE)); trace("The site for add node $ADD_NODE is: $site"); unless (@NODES ~~ /$ADD_NODE/) # if $ADD_NODE is NOT in @NODES { trace("$ADD_NODE is not in 'NODE_NAME_LIST'."); # site is case insensitive and case preserving my $lower_site = lc($site); $CFG->node2site($ADD_NODE,$lower_site); } } } else # Add Node Case for extended cluster { my @ADD_NODE_SITE_LIST = split(/,/, $CFG->params('CLUSTER_NEW_NODE_SITES')); trace("CLUSTER_NEW_NODE_SITES: @ADD_NODE_SITE_LIST"); if (scalar (@ADD_NODE_LIST) != scalar (@ADD_NODE_SITE_LIST)) { trace("The number of add nodes in parameter 'CLUSTER_NEW_HOST_NAMES' ". "is not equal to the number of sites in parameter " . "'CLUSTER_NEW_NODE_SITES'. Hence unable to match add node " . "with site."); die(dieformat(642)); } foreach my $ADD_NODE (@ADD_NODE_LIST) { $ADD_NODE = lc(trim($ADD_NODE)); my $SITE = shift(@ADD_NODE_SITE_LIST); trace("The site for add node $ADD_NODE is: $SITE"); my $lower_SITE = lc($SITE); my @lower_sites; foreach my $site (@sites) { push @lower_sites, lc($site); } if (@lower_sites ~~ /$lower_SITE/) # if $SITE is in @sites { unless (@NODES ~~ /$ADD_NODE/) # if $ADD_NODE is NOT in @NODES { trace("$ADD_NODE is not in 'NODE_NAME_LIST'."); # site is case insensitive and case preserving my $lower_SITE = lc($SITE); $CFG->node2site($ADD_NODE,$lower_SITE); } } else { trace("The site: $SITE for add node: $ADD_NODE appears in " . "'CLUSTER_NEW_NODE_SITES', but not in " . "'EXTENDED_CLUSTER_SITES'. "); die(dieformat(643, $ADD_NODE, $SITE)); } } } } } # prepare the hash variable $CFG->site2guid sub configure_site2guid { my @sites = @_; # sites from parameter 'EXTENDED_CLUSTER_SITES' my @cell_sites; # sites from cell config my $isExtendedCluster = (lc($CFG->params('EXTENDED_CLUSTER')) eq CLUSTER_EXTENDED); # For all EXADATA configurations (extended and non-extended), # this function will retrieve the site GUID from the CELL using # the 'kfod op=cellconfig" command. if (is_Exadata()) { my $crsHome = $CFG->ORA_CRS_HOME; $ENV{'ORACLE_HOME'} = $crsHome; trace("ORACLE_HOME = $crsHome"); my @output; my $rc = -1; my $xmlFile = catfile($CFG->ORA_CRS_HOME, "crs", "install", "exadata_cell_config.xml"); my $KFOD = catfile($CFG->ORA_CRS_HOME, 'bin', 'kfod'); my @program = ($KFOD, 'op=cellconfig'); $rc = run_as_user2($CFG->params('ORACLE_OWNER'), \@output, @program); trace("kfod op=cellconfig rc: $rc"); if (0 != $rc) { trace("execute 'kfod op=cellconfig' failed with error: @output"); die(dieformat(180, join(' ', @program))); } else { # The output of 'kfod op=cellconfig' is in XML structure. # Hence, store the output in an XML file and parse it to get the SITEs # and GUIDs for Exadata. # E.g. 'HQ' is the site name and '8fe49abfd9ab3d66a0b6e84dde100789' is # the GUID in following example: # # cell_site_id # 8fe49abf-d9ab-3d66-a0b6-e84dde100789 # # # cell_site_name # HQ # if (-e $xmlFile) { s_remove_file($xmlFile); } open (FILE, ">$xmlFile") || die(dieformat(185, $xmlFile, $!)); print FILE ""; print FILE @output; print FILE ""; close FILE; $CFG->zero_site_guid(FALSE); my $parser = XML::Parser->new(Handlers=>{ Char=>\&get_exadata_site_guid_char}); $parser->parsefile($xmlFile); my $exadata_sites = $CFG->exadata_sites; $exadata_sites =~ s/,$//; my $exadata_guids = $CFG->exadata_guids; $exadata_guids =~ s/,$//; $exadata_guids =~ s/-//g; trace("Exadata Sites: ". $exadata_sites); trace("Exadata GUIDs: ". $exadata_guids); @cell_sites = split(/,/,$exadata_sites); my @GUIDs = split(/,/,$exadata_guids); my @uniqueSites; foreach my $GUID (@GUIDs) { my $site = shift(@cell_sites); next if (@uniqueSites ~~ /$site/); $site = trim($site); $GUID = trim($GUID); trace("The GUID for exadata site $site is $GUID"); # site is case insensitive and case preserving my $lower_site = lc($site); $CFG->site2guid($lower_site,$GUID); push @uniqueSites, $site; } @cell_sites = split(/,/,$exadata_sites); } # Check whether the GUIDs for all sites in 'EXTENDED_CLUSTER_SITES' can be # retrieved from cell config my $finished = TRUE; my @sites_with_QG; my @FGs = split(/,/, $CFG->params('CDATA_FAILURE_GROUPS')); my @SITEs = split(/,/, $CFG->params('CDATA_SITES')); my @QGs = split(/,/, $CFG->params('CDATA_QUORUM_GROUPS')); my %fg2site; foreach my $fg (@FGs) { my $site = shift(@SITEs); my $lower_site = lc(trim($site)); trace("The site for failure group $fg is $site"); if($fg2site{$fg} eq '') { $fg2site{$fg} = $lower_site; } } foreach my $qg (@QGs) { my $site = $fg2site{$qg}; trace("The site for quorum group $qg is $site"); unless (@sites_with_QG ~~ $site) { push @sites_with_QG, $site; } } my @bkp_FGs = split(/,/, $CFG->params('CDATA_BACKUP_FAILURE_GROUPS')); my @bkp_SITEs = split(/,/, $CFG->params('CDATA_BACKUP_SITES')); my @bkp_QGs = split(/,/, $CFG->params('CDATA_BACKUP_QUORUM_GROUPS')); foreach my $fg (@bkp_FGs) { my $site = shift(@bkp_SITEs); my $lower_site = lc(trim($site)); trace("The site for backup failure group $fg is $site"); if($fg2site{$fg} eq '') { $fg2site{$fg} = $lower_site; } } foreach my $qg (@QGs) { my $site = $fg2site{$qg}; trace("The site for quorum group $qg is $site"); unless (@sites_with_QG ~~ $site) { push @sites_with_QG, $site; } } foreach my $site (@sites) { my $lower_site = lc($site); # site name is case insensitive if ($CFG->site2guid($lower_site) eq "") { if (@sites_with_QG ~~ /$lower_site/) { trace("No CELL is associated with site name '$site'."); trace("The site '$site' is associated with quorum FG or " . "backup quorum FG."); trace("The site GUID for this site will be generated using CLSCFG."); $finished = FALSE; } else { if ($isExtendedCluster) { trace("The cluster is extended and no CELL is configured with " . "site $site."); die(dieformat(648,$site)); } else { trace("No CELL is associated with site name '$site'."); trace("The cluster is non extended. The site GUID for this site " . "will be generated using CLSCFG"); $finished = FALSE; } } } } return SUCCESS if ($finished); } # Get the site GUIDs from the 'crsgenconfig_params' file, if present. # Else generate and store them in the crsgenconfig_params file. Then # copy the file to remote nodes. my @GUIDs; my $gotGUIDs = FALSE; my $firstNodeConfig = ($CFG->nodeAttributeConfig eq FIRST_NODE_TO_CONFIG); my $firstNodeUpgrade = ($CFG->nodeAttributeUpgrade eq FIRST_NODE_TO_UPGRADE); my $sihaNodeConfig = ($CFG->nodeAttributeConfig eq SIHA_NODE_TO_CONFIG); my $sihaNodeUpgrade = ($CFG->nodeAttributeUpgrade eq SIHA_NODE_TO_UPGRADE); trace("Current node is: " . $CFG->nodeAttributeConfig . $CFG->nodeAttributeUpgrade); my $paramFile = catfile($CFG->ORA_CRS_HOME, "crs", "install", "crsgenconfig_params"); my $NODE_NAME_LIST = $CFG->params('NODE_NAME_LIST'); # For add/join node case, pull the parameter file 'crsgenconfig_params' # from reachable node if (isAddNode($CFG->HOST) || $CFG->JOIN) { trace("Pull the parameter file 'crsgenconfig_params' which contains " . "the GUID info from a live node."); copy_file_from_livenode($paramFile, $paramFile, $NODE_NAME_LIST) || die(dieformat(571, $paramFile, $NODE_NAME_LIST, $paramFile)); } if (-e $paramFile) # it is on remote node or firstnode rerun { my @params = read_file($paramFile); foreach my $line (@params) { chomp ($line); next if ($line =~ /^#|\$|^\s*$/); if ($line =~ /EXTENDED_CLUSTER_SITE_GUIDS/) { my ($tmp_name, $GUID_list) = split(/=/, $line); chomp ($GUID_list); trace("Reading 'EXTENDED_CLUSTER_SITE_GUIDS' from: $paramFile"); trace("EXTENDED_CLUSTER_SITE_GUIDS = $GUID_list"); @GUIDs = split(/,/,$GUID_list); $gotGUIDs = TRUE; foreach my $site (@sites) { $site = trim($site); # site is case insensitive and case preserving my $lower_site = lc($site); my $cell_GUID = $CFG->site2guid($lower_site); if ($cell_GUID ne '') { trace("GUID for $site has been retrieved from cell config"); trace("The GUID for site $site is $cell_GUID"); next; } my $GUID = shift(@GUIDs); $GUID = trim($GUID); trace("The GUID for site $site is $GUID"); $CFG->site2guid($lower_site,$GUID); } } } } trace("firstNodeConfig: $firstNodeConfig; " . "firstNodeUpgrade: $firstNodeUpgrade; " . "sihaNodeConfig: $sihaNodeConfig; " . "sihaNodeUpgrade: $sihaNodeUpgrade"); if(($CFG->UPGRADE && ($firstNodeUpgrade || $sihaNodeUpgrade)) || (!$CFG->UPGRADE && ($firstNodeConfig || $sihaNodeConfig)) ) { # If the first node does not have the parameter file or have the # parameter file but failed to read the GUIDs from it. if (!$gotGUIDs) { # generate GUID for each site my $GUIDsList; my @lower_cell_sites; foreach my $cell_site (@cell_sites) { push @lower_cell_sites, lc($cell_site); } foreach my $site (@sites) { $site = trim($site); my $lower_site = lc($site); if (@lower_cell_sites ~~ /$lower_site/) { trace("GUID for $site has been retrieved from cell config"); next; } trace("Generating GUID for site: $site"); my $clscfg = catfile($CFG->ORA_CRS_HOME, 'bin', 'clscfg'); my $status = FALSE; my @out = (); my $GUID; @out = system_cmd_capture("$clscfg -guid"); $status = shift @out; if (0 == $status) { $GUID = shift @out; $GUID = trim($GUID); trace ("Successfully generated GUID: $GUID for site: $site"); # site is case insensitive and case preserving my $lower_site = lc($site); $CFG->site2guid($lower_site,$GUID); } else { trace ("Failed to generate GUID for site: $site, rc = $status, ". "Message:\n @out \n"); die(dieformat(631, $site)); } $GUIDsList = $GUIDsList . $GUID . ","; } $GUIDsList =~ s/,$//; trace("GUIDs: $GUIDsList"); # write the 'EXTENDED_CLUSTER_SITE_GUIDS' into parameter file open (FILE, ">$paramFile") || die(dieformat(185, $paramFile, $!)); my $EXTENDED_CLUSTER_SITE_GUIDS = "EXTENDED_CLUSTER_SITE_GUIDS=" . $GUIDsList; print FILE "$EXTENDED_CLUSTER_SITE_GUIDS\n"; close FILE; s_set_ownergroup($CFG->params('ORACLE_OWNER'), $CFG->params('ORA_DBA_GROUP'), $paramFile); s_set_perms("0664", $paramFile); } if (! $CFG->SIHA) { # copy the file 'crsgenconfig_params' to remote nodes my $ret = copy_file_to_nodes($paramFile,$paramFile,TRUE, $NODE_NAME_LIST); if (SUCCESS != $ret) { trace("Failed to copy the parameter file '$paramFile' " . "to remote nodes"); die(dieformat(186, $paramFile, $paramFile, $NODE_NAME_LIST, $ret)); } } } elsif (!$gotGUIDs) # for non-first node { # It should have the parameter file at this time. Otherwise, user should # manually copy it from an existing cluster node which has been # configured. trace("Failed to get the 'EXTENDED_CLUSTER_SITE_GUIDS' from " . "parameter file: $paramFile"); trace("Parameter file need to be manually copied from an existing " . "cluster node which has been configured."); die(dieformat(635, $paramFile)); } return SUCCESS; } # This subroutine will check each Character element in a given XML file and # retrieve the SITE and GUID information for Exadata sub get_exadata_site_guid_char { my( $parseinst, $data ) = @_; $data = trim($data); return if (!$data); if ($CFG->exadata_site_found) { # site/GUID not initialized if ($CFG->zero_site_guid) { trace("The site GUID is not initialied, skip it"); $CFG->zero_site_guid(FALSE); } else { my $sites = $CFG->exadata_sites . $data . ","; $CFG->exadata_sites($sites); } $CFG->exadata_site_found(FALSE); return; } if ($CFG->exadata_guid_found) { # site/GUID not initialized if ($data eq "00000000-0000-0000-0000-000000000000") { $CFG->zero_site_guid(TRUE); } else { my $guids = $CFG->exadata_guids . $data . ","; $CFG->exadata_guids($guids); } $CFG->exadata_guid_found(FALSE); return; } if(lc($data) eq "cell_site_name") { $CFG->exadata_site_found(TRUE); return; } if(lc($data) eq "cell_site_id") { $CFG->exadata_guid_found(TRUE); return; } } # prepare the site/GUID argument for configuring/upgrading OCR and OLR sub configure_site_guid_params { my @sites = @_; my $NODE_NAME_LIST = $CFG->params('NODE_NAME_LIST'); # during install/upgrade my $local_host; if(is_dev_env() && defined($ENV{'ORA_PCW_EMULATED_HOST_NAME'})) { # use emulated host name in dev env for node2site mapping only $local_host = $ENV{'ORA_PCW_EMULATED_HOST_NAME'}; } else{ $local_host = tolower_host(); } my $local_site = $CFG->node2site($local_host); my $local_GUID = $CFG->site2guid($local_site); trace("Local Host: $local_host; Local Site in lower case: $local_site; ". "Local GUID: $local_GUID"); $local_GUID = " -z " . $local_GUID; $CFG->clscfg_guid_arg_z($local_GUID); trace("clscfg_guid_arg_z: " . $CFG->clscfg_guid_arg_z); my $siteGUID = "-y "; my $GUID; foreach my $site (@sites) { my $lower_site = lc(trim($site)); my $GUID = $CFG->site2guid($lower_site); $siteGUID = $siteGUID . $site . ":" . $GUID . ","; } $siteGUID =~ s/,$//; $CFG->clscfg_site2guid_arg_y($siteGUID); trace("clscfg_site2guid_arg_y: " . $CFG->clscfg_site2guid_arg_y); my $firstNodeConfig = ($CFG->nodeAttributeConfig eq FIRST_NODE_TO_CONFIG); my $firstNodeUpgrade = ($CFG->nodeAttributeUpgrade eq FIRST_NODE_TO_UPGRADE); if(($CFG->UPGRADE && $firstNodeUpgrade)||(!$CFG->UPGRADE && $firstNodeConfig)) { my $nodesite = "-h "; my @nodelist = split(/,/ , $NODE_NAME_LIST); foreach my $node (@nodelist) { $node = trim($node); my $lower_node = lc($node); # site name is case preserving # site name retrieved from $CFG->node2site has been lower cased my $original_site; my $lower_site = $CFG->node2site($lower_node); foreach my $site (@sites) { $site = trim($site); $original_site = $site if ($lower_site eq lc($site)); } $nodesite = $nodesite . $node . ":" . $original_site . ","; } $nodesite =~ s/,$//; $CFG->clscfg_node2site_arg_h($nodesite); trace("clscfg_node2site_arg_h: " . $CFG->clscfg_node2site_arg_h); } } #------------------------------------------------------------------------------- # Function: Verifies if the management databse resource is either globally enabled # or globally disabled and enabled locally on any node. # # Args : [0] - The CRS home where the mgmtdb is running. # # Returns : 1 if the management databse is enabled, 0 otherwise. #------------------------------------------------------------------------------- sub isMgmtDbEnabled { my $home = $_[0]; my $crsctl = crs_exec_path('crsctl', $home); my @cmd = ($crsctl, 'stat', 'res', 'ora.mgmtdb', '-f'); my ($rc, @out) = system_cmd_capture(@cmd); if (0 != $rc) { print_lines(@out); trace(join(' ', @cmd) . " failed with status $rc"); die(dieformat(180, join(' ', @cmd))); } my $line_found = FALSE; my $result = FALSE; foreach my $line (@out) { # The output should contain a line # with the following format: # ENABLED=0 or ENABLED=1 # ENABLED@SERVERNAME()=1 or ENABLED@SERVERNAME()=0 if (($line =~ /^ENABLED=/) || ($line =~ /^ENABLED\@SERVERNAME/)) { my @enabled = split(/=/, $line); $result = $enabled[1]; $line_found = TRUE; last if($result); } } # If the line with the required info was # not found, print the output and die unless ($line_found) { print_lines(@out); die(dieformat(632)) } return $result; } sub getNetworkType #------------------------------------------------------------------------------- # Function: to get the network type by command 'srvctl config network -S 1' # Args : none # Returns : the network type #------------------------------------------------------------------------------- { my $networkType = undef; my $run_as_owner = TRUE; my @output; my $srvctl_args = "config network -S 1"; my $rc = srvctl_capture($run_as_owner, \@output, $srvctl_args); if (0 != $rc) { print_lines(@output); die(dieformat(180, "srvctl $srvctl_args")); } else { # the output would be like below: # #@=result[0]: res_name={ora.net1.network} id={1} subnet={10.208.144.0} # subnet6={} netmask={255.255.248.0} prefixlen6={} interfaces={eth0} # dhcpservertype={dhcp} nettype6={} pingtargets={} leaf_network={false} # enabled={true} enabled_nodes={} disabled_nodes={} # dhcpservertype is populated for ipv4 - static, dhcp or mixed # nettype6 is populated for ipv6 - static, autoconfig or mixed foreach my $line (@output) { if (($line =~ /dhcpservertype=\{(\S+)\}/) || ($line =~ /nettype6=\{(\S+)\}/)) { $networkType = $1; $networkType = lc(trim($networkType)); } } } trace("network type: $networkType"); return $networkType; } #------------------------------------------------------------------------------- # Function: Populates the cluster properties in the global variables - # $CFG->clscfg_clsprop_olr_arg_p and $CFG->clscfg_clsprop_ocr_arg_p # # Args : None # # Returns : TRUE On Success # FALSE On Failure #------------------------------------------------------------------------------- sub getClusterProperties { my $clsProp = "-p "; # 1. Populate the Cluster Class my $cluster_class = getClusterClass(); if ($cluster_class ne '') { $clsProp = $clsProp . "CLUSTER_CLASS:" . $cluster_class; } # More cluster properties go here, separated by "," if ($clsProp eq "-p ") { # Cluster properties are not set. Return trace("Cluster properties are not set"); return TRUE; } # Populate cluster properties in OLR $CFG->clscfg_clsprop_olr_arg_p($clsProp); trace("clscfg_clsprop_olr_arg_p: " . $CFG->clscfg_clsprop_olr_arg_p); # Populate cluster properties in OCR (required only on first node) if (($CFG->UPGRADE && ($CFG->nodeAttributeUpgrade eq FIRST_NODE_TO_UPGRADE))|| (!$CFG->UPGRADE && ($CFG->nodeAttributeConfig eq FIRST_NODE_TO_CONFIG))) { $CFG->clscfg_clsprop_ocr_arg_p($clsProp); trace("clscfg_clsprop_ocr_arg_p: " . $CFG->clscfg_clsprop_ocr_arg_p); } return TRUE; } #------------------------------------------------------------------------------- # Function: Get the cluster class from crsconfig parameters # # Args : None # # Returns : Cluster Class #------------------------------------------------------------------------------- sub getClusterClass { my $class; my $cluster_class = lc(trim($CFG->params('CLUSTER_CLASS'))); if ($cluster_class eq lc(CLUSTER_CLASS_STANDALONE)) { $class = CLUSTER_CLASS_STANDALONE; } elsif ($cluster_class eq lc(CLUSTER_CLASS_DOMAINSERVICES)) { $class = CLUSTER_CLASS_DOMAINSERVICES; } elsif ($cluster_class eq lc(CLUSTER_CLASS_DOMAIN_STR)) { $class = CLUSTER_CLASS_DOMAINSERVICES; } elsif ($cluster_class eq lc(CLUSTER_CLASS_MEMBER)) { $class = CLUSTER_CLASS_MEMBER; } elsif ($cluster_class eq '') { # Cluster Class is not set. i.e upgrade case. Default to "Standalone" while # upgrading from pre-12.2 if ( ($CFG->UPGRADE) && (isOldVersionLT122()) ) { $class = CLUSTER_CLASS_STANDALONE; } else { $class = ''; } } return $class; } #------------------------------------------------------------------------------- # Function: Get the cluster class using crsctl command # # Args : None # # Returns : Cluster Class #------------------------------------------------------------------------------- sub getClusterClass_crsctl { my $crsctl; my $class; my @cmd; $crsctl = crs_exec_path('crsctl'); @cmd = ($crsctl, 'get', 'cluster', 'class'); my @output = system_cmd_capture(@cmd); my $rc = shift @output; if (0 != $rc) { print_lines(@output); if ($CFG->DECONFIG && $CFG->FORCE) { print_error(644); $CFG->DECONFIG_ERROR(TRUE); return; } else { die(dieformat(644)); } } else { if (scalar(grep(/Standalone/, @output)) > 0) { $class = CLUSTER_CLASS_STANDALONE; } elsif (scalar(grep(/Domain/, @output)) > 0) { $class = CLUSTER_CLASS_DOMAINSERVICES; } elsif (scalar(grep(/Member/, @output)) > 0) { $class = CLUSTER_CLASS_MEMBER; } } return $class; } #------------------------------------------------------------------------------- # Function: Check if it is ODA olite environment # # Args : None # # Returns : TRUE or FALSE #------------------------------------------------------------------------------- sub isODALite { if ($CFG->defined_param('ODA_CONFIG') && (lc($CFG->params('ODA_CONFIG')) eq 'olite')) { trace("This is ODA olite environment."); return TRUE; } else { trace("This is not ODA olite environment."); return FALSE; } } #------------------------------------------------------------------------------- # Function: Check if it is ODA SingleIP environment # # Args : None # # Returns : TRUE or FALSE #------------------------------------------------------------------------------- sub isODASIP { if ($CFG->defined_param('ODA_CONFIG') && (lc($CFG->params('ODA_CONFIG')) eq 'odasip')) { trace("This is ODA SingleIP environment."); return TRUE; } else { trace("This is not ODA SingleIP environment."); return FALSE; } } #------------------------------------------------------------------------------- # Function: Check if it is ODA IaaS environment # # Args : None # # Returns : TRUE or FALSE #------------------------------------------------------------------------------- sub isODAIaaS { if ($CFG->defined_param('ODA_CONFIG') && (lc($CFG->params('ODA_CONFIG')) eq 'odaiaas')) { trace("This is ODA IaaS environment."); return TRUE; } else { trace("This is not ODA IaaS environment."); return FALSE; } } sub isODABMIaaS { if ($CFG->platform_family eq 'windows') { return FALSE; } return s_is_ODA_BMIaaS(); } sub printEnvVars { trace("Print system environment variables:"); foreach my $key (sort keys(%ENV)) { trace("$key = $ENV{$key}"); } } 1;