#!/usr/bin/perl
# 
# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      rootcrs.pl
#
#    DESCRIPTION
#      Clusterware configuration script for Oracle Clusterware home
#
#    NOTES
#      This is run once during configuration or patching of Oracle
#      clusterware home.
#      This script does the following:
#      1) Setup permissions of binaries and DSOs in Oracle Clusterware home
#      2) Setup OLR for storing Oracle local registry data
#      3) Setup GPnP wallet and profile
#      4) Setup and copy files for CLSINIT daemon
#      5) Start CLSINIT daemon
#      6) Copy required CRS resources for CLSINIT to start
#
#      NOTE: This is run during Oracle Clusterware home configurations only.
#            It should not be used for any other homes such as SI HA home,
#            or Oracle RAC homes.
#
#    MODIFIED   (MM/DD/YY)
#    jesugonz    04/29/16 - add convert to extended
#    luoli       03/28/16 - Fix bug 23005641
#    xyuan       11/26/15 - Fix bug 22200719
#    luoli       04/06/15 - Fix bug 20811547
#    bbeelamk    02/06/15 - Fix bug 20465849
#    luoli       01/12/15 - Separate downgrade/deconfig flow
#    xyuan       05/22/14 - Fix bug 18650453
#    muhe        04/29/14 - Fix bug 18675566
#    xyuan       03/31/14 - Add -cleanpids option for prepatch
#    muhe        03/24/14 - Fix bug 17596579
#    luoli       02/18/14 - Fix bug 18161639
#    luoli       01/15/14 - Support ocrbackupfile for downgrade
#    madoming    01/02/14 - Fix bugs 18001290 and 18024785.
#    rdasari     12/03/13 - support force for install
#    luoli       11/25/13 - write ckpts in ocr
#    madoming    11/22/13 - Fix bug 17841639
#    rdasari     11/13/13 - exclude file not used for unlock
#    madoming    10/28/13 - Add validation for current working directory
#    xyuan       10/07/13 - Fix bug 17549800: add -rollback option for
#                           rootcrs.pl
#    sidshank    04/07/13 - Add an option LANG to be passed during automatic
#                           execution.
#    xyuan       08/31/12 - Fix bug 14535011 - Add '-init' option
#    xyuan       08/12/12 - Fix bug 14464512
#    xyuan       04/24/12 - Add options for joining an upgraded cluster
#    sidshank    04/20/12 - adding -auto option to be used internally by root
#                           automation alone.
#    xyuan       02/20/12 - Add -prepatch & -postpatch
#    xyuan       02/02/12 - Force upgrade support
#    xyuan       01/08/12 - -oldcrshome and -version are not needed any more
#                           for downgrade command
#    xyuan       10/20/11 - XbranchMerge ksviswan_bug-12630162 from
#                           st_has_11.2.0.3.0
#    sidshank    08/11/11 - removing check_Superuser from here for bug 12739826
#    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
#    dpham       03/28/11 - New for 12c
# 

################ Documentation ################

# The SYNOPSIS section is printed out as usage when incorrect parameters
# are passed

=head1 NAME

  rootcrs.pl - Configure Oracle Clusterware

=head1 SYNOPSIS

  rootcrs.pl [-verbose] [-upgrade]
             [-paramfile <parameter-file>] 
             [-deconfig [-deinstall] [-keepdg] [-lastnode] | -downgrade [-ocrbackupfile <file name>] [-online]] [-force]
             [-unlock [-crshome <path to CRS home>]] 
             [-lock [-crshome <path to CRS home>]]
             [[-prepatch [-cleanpids] | -postpatch] [-norestart] [-nonrolling] [-rollback]] [-dstcrshome <path to CRS home>]
             [-join -existingnode <node name>]
             [-init] [-first -force] [-cleansockets]
             [-move -dstcrshome <path to CRS home>]
             [-converttoextended [-first -sites <sites list>] -site <node site>]

  Options:
   -verbose    Run this script in verbose mode
   -upgrade    Oracle HA is being upgraded from previous version
   -lock       Lock CRS home
   -paramfile  Complete path of file specifying HA parameter values
   -lastnode   Force the node this is executing on to be considered the
               last node of the deconfiguration and perform actions associated
               with deconfiguring the last node
   -downgrade  Downgrade the clusterware
   -deconfig   Remove Oracle Clusterware to allow it to be uninstalled or reinstalled
   -deinstall  Reset the permissions on CRS home during de-configuration
   -keepdg     Keep existing diskgroups during de-configuration
   -force      Force the execution of steps in install or delete that cannot be 
	       verified to be safe
   -unlock     Unlock CRS home 
   -crshome    Complete path of crs home.
   -prepatch   Perform required steps before the Oracle patching tool (Opatch) is invoked
   -cleanpids  Terminate Oracle Clsuterware active processes. Must use with -prepatch option. 
   -postpatch  Perform required steps after the Oracle patching tool (Opatch) is invoked
   -nonrolling Perform steps for -prepatch and -postpatch in a non-rolling fashion
   -norestart  Leave CRS stack down after patching
   -rollback   Perform steps specific to rollback for -prepatch and -postpatch 
   -dstcrshome Complete path of CRS home for out-of-place patching or move operation
   -join       Add the local node to an upgraded cluster
   -existingnode An already upgraded cluster node. Must use with join option
   -init       Reset the permissions of all files and directories under CRS home
   -ocrbackupfile Restore OCR with the given OCR backup file.
   -first      Force a node other than the installer node as the first node, 
               during install or upgrade. Must use with -force option.
   -cleansockets Remove socket files created by the Grid Infrastructure
                 processes. Stop the GI stack if not already stopped.
   -online     Downgrade the cluster without bringing the stack down on all 
               nodes. Must be used with -downgrade option.
   -move       Change the Oracle Grid Infrastructure home path. The new home
               location is specified by the -dstcrshome option.
   -converttoextended Converts the cluster to an extended cluster.
   -sites      List of sites to add to the cluster configuration. Must be used
               with -first. Must be used on the first node to convert to
               extended cluster.
   -site       The site the local node is associated with.

  To see the full manpage for this program, execute:
    perldoc rootcrs.pl

=head1 DESCRIPTION

  This script performs the operations necessary to install the Oracle
  Clusterware stack on a node of a cluster.  It must be run once on
  each node and it must be run by an authorized user, e.g. root on UNIX
  platforms, Administrator on Windows.

  Upon successful completion on each node, the Oracle Clusterware will
  be executing on that node.

=cut

################ End Documentation ################

use strict;
use English;
use File::Basename;
use File::Spec::Functions;
use Getopt::Long qw(:config no_auto_abbrev);
use Pod::Usage;
use Cwd;

BEGIN {
   my $program_dir = dirname($PROGRAM_NAME);
   # Add the directory of this file to the search path
   push @INC, "$program_dir";
   # Add the path of GI home into the @INC path
   push @INC, "$program_dir/../../perl/lib";
}

# root scripts modules
use crsinstall;
use crsupgrade;
use crsdeconfig;
use crspatch;
use crsutils;
use oraacfs;
use crsdowngrade;
use crsconvtoext;

my ($DEBUG, $DECONFIG, $FORCE, $LASTNODE, $UPGRADE, $DOWNGRADE, $INIT,
    $MOVE, $UNLOCK, $LOCK, $HELP, $DEINSTALL, $KEEPDG, $AUTO,
    $OCRBACKUPFILE, $FIRST, $CLEANSOCKETS, $ONLINE, $CONVERT_TO_EXTENDED,
    $SITES, $SITE) = 0;

my $REMOTENODE;
my $LANG;

# For patching
my ($PREPATCH, $CLEANPIDS, $POSTPATCH, $NONROLLING, $NORESTART, $ROLLBACK) = 0;
my $DESTCRSHOME;

# For adding the local node to an upgraded cluster
my $JOIN = 0;
my $EXISTINGNODE;

# pull all parameters defined in crsconfig_params (if it exists)
# as variables in Perl
my $PARAM_FILE = catfile(dirname($0), 'crsconfig_params');

# pull all parameters defined in crsconfig_addparams
my $addfile = catfile(dirname($0), 'crsconfig_addparams');

# pull all definitions in s_crsconfig_defs (if it exists) as variables in Perl
# this file might not exist for all platforms
my $osdfile = catfile(dirname($0), 's_crsconfig_defs');

# Parse command line args
# If an incorrect option is specified, the perl POD at the beginning
# of the file is parsed into a man page
# the return code to give when the incorrect parameters are passed
my $usage_rc = 1;

GetOptions('verbose'         => \$DEBUG,
           'paramfile=s'     => \$PARAM_FILE,
           'deconfig'        => \$DECONFIG,
           'deinstall'       => \$DEINSTALL,
           'keepdg'          => \$KEEPDG,
           'move'            => \$MOVE,
           'upgrade'         => \$UPGRADE,
           'force'           => \$FORCE,
           'lastnode'        => \$LASTNODE,
           'downgrade'       => \$DOWNGRADE,
           'unlock'          => \$UNLOCK,
           'crshome=s'       => \$DESTCRSHOME,
           'lock'            => \$LOCK,
           'remotenode'      => \$REMOTENODE,
           'prepatch'        => \$PREPATCH,
           'postpatch'       => \$POSTPATCH,
           'cleanpids'       => \$CLEANPIDS,
           'dstcrshome=s'    => \$DESTCRSHOME,
           'nonrolling!'     => \$NONROLLING,
           'norestart!'      => \$NORESTART,
           'auto'            => \$AUTO,
           'join'            => \$JOIN,
           'existingnode=s'  => \$EXISTINGNODE,
           'init'            => \$INIT,
           'lang=s'          => \$LANG,
           'help!'           => \$HELP,
           'rollback'        => \$ROLLBACK,
           'ocrbackupfile=s' => \$OCRBACKUPFILE,
           'first'           => \$FIRST,
           'cleansockets'    => \$CLEANSOCKETS,
           'online'          => \$ONLINE,
           'converttoextended' => \$CONVERT_TO_EXTENDED,
           'sites=s'         => \$SITES,
           'site=s'          => \$SITE
          ) or pod2usage($usage_rc);

# Check validity of command line args
pod2usage(-msg => "Invalid extra options passed: @ARGV",
          -exitval => $usage_rc) if (@ARGV);

combinedArgs({"deconfig" => $DECONFIG},
  {"deinstall" => $DEINSTALL, "keepdg" => $KEEPDG}) || pod2usage(0);

combinedArgs({"prepatch" => $PREPATCH, "postpatch" => $POSTPATCH},
  {"nonrolling" => $NONROLLING, "norestart" => $NORESTART,
   "rollback" => $ROLLBACK}) || pod2usage(0);

combinedArgs({"upgrade" => $UPGRADE},
  {"join" => $JOIN, "existingnode" => $EXISTINGNODE}) || pod2usage(0);

combinedArgs({"join" => $JOIN}, {"existingnode" => $EXISTINGNODE}) || pod2usage(0);

combinedArgs({"existingnode" => $EXISTINGNODE}, {"join" => $JOIN}) || pod2usage(0);

combinedArgs({"deconfig" => $DECONFIG}, {"lastnode" => $LASTNODE}) || pod2usage(0);

if ($DECONFIG || $DOWNGRADE || $UPGRADE)
{
  combinedArgs({"deconfig" => $DECONFIG, "downgrade" => $DOWNGRADE, "upgrade" => $UPGRADE},
    {"force" => $FORCE}) || pod2usage(0);
}

if (!$CONVERT_TO_EXTENDED)
{
  combinedArgs({"force" => $FORCE},{"first" => $FIRST}) || pod2usage(0);
}
else
{
  combinedArgs({"first" => $FIRST}, {"sites" => $SITES}) || pod2usage(0);
}

combinedArgs({"prepatch" => $PREPATCH}, {"cleanpids" => $CLEANPIDS}) || pod2usage(0);

combinedArgs({"downgrade" => $DOWNGRADE}, {"online" => $ONLINE}) || pod2usage(0);

combinedArgs({"dstcrshome" => $DESTCRSHOME}, {"move" => $MOVE}) || pod2usage(0);

#-------------------------------------------------------------------------------
# MAIN SCRIPT BODY
#-------------------------------------------------------------------------------
local $SIG{'__DIE__'} = sub { dietrap(@_); };
# Catch Control-C
local $SIG{INT}       = sub { dietrap(@_); };
# Catch termination signal (kill)
# Kill signal (kill -9) can be neither trapped nor ignored
local $SIG{TERM} = sub { dietrap(@_); };

if (! $DEBUG) { $DEBUG = $ENV{'ORA_INSTALL_DEBUG'}; } 

if ($HELP) { 
   pod2usage(0);
}
elsif ($DECONFIG) { 
   crsdeconfig->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    DECONFIG            => $DECONFIG,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    FORCE               => $FORCE,
                    DEINSTALL           => $DEINSTALL,
                    KEEPDG              => $KEEPDG,
                    LASTNODE            => $LASTNODE,
                    crscfg_trace        => TRUE,
		    LANG                => $LANG,
                    AUTO                => $AUTO
                   );
}
elsif ($UNLOCK) {
      crspatch->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    UNLOCK              => $UNLOCK,
                    DESTCRSHOME         => $DESTCRSHOME,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    crscfg_trace        => TRUE
                   );

   unlockCRSPatchHome();
}
elsif ($LOCK) {
      crspatch->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    LOCK                => $LOCK,
                    DESTCRSHOME         => $DESTCRSHOME,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    crscfg_trace        => TRUE
                   );

   lockHome();
}
elsif ($MOVE) {
      crspatch->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    GIMOVE              => $MOVE,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    DESTCRSHOME         => $DESTCRSHOME,
                    crscfg_trace        => TRUE
                   );
}
elsif ($UPGRADE) { 
    crsupgrade->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    UPGRADE             => $UPGRADE,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    FORCE               => $FORCE,
                    REMOTENODE          => $REMOTENODE,
                    JOIN                => $JOIN,
                    EXISTINGNODE        => $EXISTINGNODE,
                    crscfg_trace        => TRUE,
		    LANG                => $LANG,
                    AUTO                => $AUTO,
                    FIRST               => $FIRST
                   );
}
elsif ($DOWNGRADE) {
   crsdowngrade->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    DOWNGRADE           => $DOWNGRADE,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    FORCE               => $FORCE,
                    ONLINE              => $ONLINE,
                    OCRBACKUPFILE       => $OCRBACKUPFILE,
                    crscfg_trace        => TRUE
                   );
}
elsif ($PREPATCH) {
      crspatch->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    PREPATCH            => $PREPATCH,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    NONROLLING          => $NONROLLING,
                    DESTCRSHOME         => $DESTCRSHOME,
                    NORESTART           => $NORESTART,
                    ROLLBACK            => $ROLLBACK,
                    CLEANPIDS           => $CLEANPIDS, 
                    crscfg_trace        => TRUE
                   );
}
elsif ($POSTPATCH) {
      crspatch->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    POSTPATCH           => $POSTPATCH,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    NONROLLING          => $NONROLLING,
                    ROLLBACK            => $ROLLBACK,
                    DESTCRSHOME         => $DESTCRSHOME,
                    NORESTART           => $NORESTART,
                    crscfg_trace        => TRUE
                   );
}
elsif ($INIT) {
      crsinstall->new(SIHA                => FALSE,
                      DEBUG               => $DEBUG,
                      paramfile           => $PARAM_FILE,
                      osdfile             => $osdfile,
                      addfile             => $addfile,
                      crscfg_trace        => TRUE,
                      init                => $INIT
                     );
}
elsif ($CLEANSOCKETS) {
      crsdeconfig->new(SIHA                => FALSE,
                       DEBUG               => $DEBUG,
                       paramfile           => $PARAM_FILE,
                       osdfile             => $osdfile,
                       addfile             => $addfile,
                       crscfg_trace        => TRUE,
                       CLEANSOCKETS        => $CLEANSOCKETS
                     );
}
elsif ($CONVERT_TO_EXTENDED)
{
         crsconvtoext->new(SIHA          => FALSE,
                           paramfile     => $PARAM_FILE,
                           osdfile       => $osdfile,
                           addfile       => $addfile,
                           crscfg_trace  => TRUE,
                           DEBUG         => $DEBUG,
                           FORCE         => $FORCE,
                           first         => $FIRST,
                           sites         => $SITES,
                           site          => $SITE
                          );
}
else { # fresh install
    crsinstall->new(SIHA                => FALSE,
                    DEBUG               => $DEBUG,
                    paramfile           => $PARAM_FILE,
                    osdfile             => $osdfile,
                    addfile             => $addfile,
                    REMOTENODE          => $REMOTENODE,
                    FORCE               => $FORCE,
                    crscfg_trace        => TRUE,
		    LANG                => $LANG,
		    AUTO                => $AUTO,
                    FIRST               => $FIRST
                   );
}

sub combinedArgs
{
  my %req_opts = %{$_[0]};
  my %opt_opts = %{$_[1]};

  for my $optElem (keys %opt_opts)
  {
    next if (! $opt_opts{$optElem});

    my @missingOpts;
    for my $reqElem (keys %req_opts)
    {
      if (! $req_opts{$reqElem})
      {
        push(@missingOpts, $reqElem);
      }
    }

    if (scalar(@missingOpts) == scalar(keys %req_opts))
    {
      print("One or more options required but missing: @missingOpts\n");
      return FALSE;
    }
  }

  return TRUE;
}



0;
