# $Header: has/install/crsconfig/oracss.pm /st_has_11.2.0/6 2013/07/04 07:36:59 mkallana Exp $
#
# OraCSS.pm
#
# Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. 
#

=head1 NAME

  oracss.pm  Oracle clusterware CSS component configuration/startup package

=head1 DESCRIPTION

   This package contains functions required for initial configuration
   and startup of the CSS component of Oracle clusterware

=cut

package oracss;

use strict;
use English;
use File::Temp qw/ tempfile /;
use File::Spec::Functions;
use File::Find ();

use crsconfig_lib;

use constant CSS_EXCL_SUCCESS             => 1;
use constant CSS_EXCL_FAIL_CLUSTER_ACTIVE => 2;
use constant CSS_EXCL_FAIL                => 3;

use Exporter;
use vars qw(@ISA @EXPORT @EXPORT_OK);
@ISA = qw(Exporter);

my @exp_func  = qw(CSS_start_exclusive CSS_start_clustered CSS_start
                   CSS_is_configured CSS_upgrade CSS_get_old_VF_string
                   CSS_prep_old_VFs CSS_add_vfs CSS_delete_vfs
                   CSS_downgrade_VFs CSS_stop CSS_CanRunRealtime
                   CSS_Clean_Local_Endpts);

my @exp_const = qw(CSS_EXCL_SUCCESS CSS_EXCL_FAIL
                   CSS_EXCL_FAIL_CLUSTER_ACTIVE);

push @EXPORT, @exp_func, @exp_const;

=head1 EXPORTED FUNCTIONS

=head2 CSS_start_exclusive

   Start the CSSD in exclusive mode.  The CSSD when started in this
   mode requires no configuration to have completed.

=head3 Parameters

   None

=head3 Returns

  CSS_EXCL_SUCCESS             - The CSSD was     started successsfully
  CSS_EXCL_FAIL_CLUSTER_ACTIVE - The CSSD was not started successsfully
                                 because other nodes are alive
  CSS_EXCL_FAIL                - The CSSD was not started successsfully

=cut

sub CSS_start_exclusive {
  trace("Starting CSS in exclusive mode");
  my $ret;
  my @output = CSS_start("-env", "CSSD_MODE=-X");
  my $rc     = shift @output;

  # If we had a failure, check to see if it is becasue another node is
  # up and if it is, we ignore the failure
  my @node_up = grep(/CRS\-4402/, @output);
  if (scalar(@node_up) > 0) {
    $ret = CSS_EXCL_FAIL_CLUSTER_ACTIVE;
    for my $line (@node_up) {
      print "$line\n";
      trace($line);
    }
    CSS_stop();
  }
  elsif ($rc) { $ret = CSS_EXCL_SUCCESS; }
  else { $ret = CSS_EXCL_FAIL; }

  for my $line (@output) {
    # if we were successful, or we failed due to unexpected reasons.
    # print out the msgs
    if ($ret == CSS_EXCL_SUCCESS || $ret == CSS_EXCL_FAIL) {
      print "$line\n";
      trace($line); # put to both user and log file
    }
  }

  return $ret;
}

=head2 CSS_start_clustered

   Start the CSSD in clsutered (normal) mode

=head3 Parameters

   None

=head3 Returns

  TRUE  - The CSSD was     started successsfully
  FALSE - The CSSD was not started successsfully

=cut

sub CSS_start_clustered {
  trace("Starting CSS in clustered mode");

  my $rc = start_resource("ora.cssd", "-init");

  return $rc;
}

=head2 CSS_stop

  Stop the CSSD

=head3 Parameters

  None

=head3 Returns

  TRUE  - The CSS component is     stopped
  FALSE - The CSS component is not stopped

=cut

sub CSS_stop {
  my $stop_success = TRUE;

  $stop_success = stop_resource("ora.cssd", "-init");
  if (!$stop_success) { trace("CSS shutdown failed"); }

  stop_resource("ora.cssdmonitor", "-init");

  return $stop_success;
}

=head2 CSS_CanRunRealtime

  Verifies that the CSSD can run realtime

=head3 Parameters

  crsconfig_lib class object

=head3 Returns

  TRUE  - The CSSD can    run realtime
  FALSE - The CSSD cannot run realtime

=cut

sub CSS_CanRunRealtime {
  my $cfg       = shift;
  my $rc = TRUE;

  if ($OSNAME eq "aix") {
    my $user = $cfg->params('ORACLE_OWNER');

    my @out = system_cmd_capture('/usr/sbin/lsuser', '-a',
                                 'capabilities', $user);
    my $status  = shift @out;
    chomp @out;
    my $capstr = (split(' ', $out[0]))[1];
    $capstr =~ s/^ *capabilities=//;
    my %caps = map { lc($_) => 1 } (split(',', $capstr));
    my @req_caps = ('CAP_NUMA_ATTACH', 'CAP_BYPASS_RAC_VMM',
                    'CAP_PROPAGATE');
    my @needed_caps;
    for my $cap (@req_caps) {
      if (!$caps{lc($cap)}) {
        push @needed_caps, $cap;
        $rc = FALSE;
      }
    }

    if ($rc) {
      my @msg = ("User $user has the required capabilities to run CSSD",
                 "in realtime mode");
      print "@msg\n";
      trace(@msg);
    }
    else {
      my @msg =
        ("User $user is missing the following capabilities required to",
         "run CSSD in realtime:");
      print "@msg\n";
      trace(@msg);
      @msg = (" ", join(',', @needed_caps));
      print "@msg\n";
      trace(@msg);
      @msg = ("To add the required capabilities, please run:");
      print "@msg\n";
      trace(@msg);
      @msg = ("   /usr/bin/chuser capabilities=" . join(',', @req_caps),
             $user);
      print "@msg\n";
      trace(@msg);
    }
  }

  return $rc;
}

=head2 CSS_Clean_Local_Endpts

   Start the CSSD in clsutered (normal) mode

=head3 Parameters

   Config Object

=head3 Returns

  TRUE  - The CSSD was     started successsfully
  FALSE - The CSSD was not started successsfully

=cut

sub CSS_Clean_Local_Endpts {
  my $cfg       = shift;

  if ($cfg->platform_family eq 'unix') {
    my @epdirs = (catdir('', 'tmp', '.oracle'),
                  catdir('', 'var', 'tmp', '.oracle'));
    if ($OSNAME eq "aix") {
      trace("Cleaning the CSS local endpoints");

      for my $dir (@epdirs) {
        File::Find::find({wanted => \&CSS_Local_Endpt}, $dir);
      }
    }
  }

  return;
}

=head2 CSS_upgrade

  Performs operations required for upgrade of CSS

=head3 Parameters

  crsconfig_lib class object

=head3 Returns

  SUCCESS - The CSS component has been successfully upgraded
  FAILED  - The CSS component upgrade failed

=cut

sub CSS_upgrade {
  my $cfg       = shift;
  my $rc        = SUCCESS;
  my $vfds;

  trace ("Upgrading the existing voting disks!");
  my $cmdrc = run_crs_cmd('cssvfupgd');
  if ($cmdrc != 0) {
    $rc = FAILED;
    error("Upgrade of voting files failed");
  }

  return $rc;
}

=head2 CSS_get_old_VF_string

  Gets old VF list from OCR and updates VF discovery string

=head3 Parameters

  None

=head3 Returns

  The CSS VF list obtained from OCR, or NULL if unable to obtain

=cut

sub CSS_get_old_VF_string {
  my $vfds;
  my $crsctl = crs_exec_path('crsctl');

  trace ("Obtaining the existing voting disks");
  my @vflist = system_cmd_capture($crsctl, 'get', 'css', 'vfdiscstring');
  my $cmdrc = shift @vflist;

  if ($cmdrc == 0) { $vfds = join(',', @vflist); }
  else {
    error("Unable to get voting file list for upgrade, return code $cmdrc");
    for my $line (@vflist) { print "$line\n"; trace($line); }
  }

  return $vfds;
}

=head2 CSS_prep_old_VFs

  Prepares older voting files that may have had the skgfr block cleaned

=head3 Parameters

  None

=head3 Returns

  SUCCESS - The voting files are all successfully prepared
  FAILED  - One or more voting files are not prepared

=cut

sub CSS_prep_old_VFs {
  my $cfg = shift;
  my $rc     = SUCCESS;
  my $clsfmt = crs_exec_path('clsfmt');
  my @vflist = split(',', $cfg->VF_DISCOVERY_STRING);

  trace ("Preparing the existing voting disks");
  for my $vf (@vflist) {
    my @cmdout = system_cmd_capture($clsfmt, 'css', $vf);
    my $cmdrc  = shift @cmdout;

    if ($cmdrc == 0) { for my $line (@cmdout) { trace($line); } }
    else {
      $rc = FAILED;
      error("Unable to prepare voting file $vf, return code $cmdrc");
      for my $line (@cmdout) { print "$line\n"; trace($line); }
    }
  }

  return $rc;
}


=head2 CSS_is_configured

  Checks to see if the CSS component has already been configured.  May
  be run with the CSSD started in exclusive or clustered mode, but is
  most meaningful when run with the CSSD in exclusive mode, since the
  CSSD will not start when configuration has not completed and the CSSD
  is started in clustered mode.

=head3 Parameters

  None

=head3 Returns

  TRUE  - The CSS component is     configured
  FALSE - The CSS component is not configured

=cut

sub CSS_is_configured {
  my $CRSCTL = crs_exec_path("crsctl");
  my $rc = FALSE;

  trace ("Querying for existing CSS voting disks");
  my ($cmdrc, @out) = system_cmd_capture("$CRSCTL query css votedisk");

  # remove blank lines and headings
  my @vflist = grep(!/^ *$/ && !/^\#/ && !/^-/, @out);

  my $vfcount = scalar(@vflist) - 1;
  if ($vfcount > 0) {
    trace("Found $vfcount configured voting files");
    $rc = TRUE;
  }

  return $rc;
}

=head2 CSS_add_vfs

  Add voting file(s) to the CSSD configuration.

=head3 Parameters

  A list of voting file paths or a single ASM diskgroup name.  If the
  parameter is an ASM dikgroup name, it must be prefixed with a +

=head3 Returns

  TRUE  - The voting file(s) were     added
  FALSE - The voting file(s) were not added

=cut

sub CSS_add_vfs {
  my $cfg  = shift;
  my $rc   = TRUE;

  my @addvfcmd;
  if ($_[0] =~ /^\+/) {
    my $dg_parm = $_[0];
    my $diskgroup = $dg_parm;

    if ($cfg->platform_family ne "windows") { $dg_parm = "'$dg_parm'"; }

    $diskgroup =~ s/^\+//;
    trace("Creating voting files in ASM diskgroup $diskgroup");
    @addvfcmd = ("crsctl", "replace", "votedisk", $dg_parm);
  }
  else {
    trace("Adding voting files @_");
    @addvfcmd = ("crsctl", "add", "css", "votedisk", (@_));
  }

  trace("Executing @addvfcmd");

  my $status = run_crs_cmd(@addvfcmd);

  if ($status != 0) {
    error("Voting file add failed");
    $rc = FALSE;
  }

  return $rc;
}

=head2 CSS_add_vfs

  Delete voting file(s) from the CSSD configuration.

=head3 Parameters

  None! It gets GUIDs from "crsctl query css votedisk"

=head3 Returns

  TRUE  - The voting file(s) were     deleted
  FALSE - The voting file(s) were not deleted

=cut

sub CSS_delete_vfs
{
   my $rc     = TRUE;
   my $crsctl = catfile ($CFG->ORA_CRS_HOME, "bin", "crsctl");
   my $ASM_DISK_GROUP = $CFG->params('ASM_DISK_GROUP');

   # deleting votedisk
   my @cmd = ("crsctl", "delete", "css", "votedisk", "+$ASM_DISK_GROUP");
   my $status = run_crs_cmd(@cmd);
   if ($status == 0) {
     trace ("crsctl delete for vds in $ASM_DISK_GROUP ... success");
   }
   else {
     error ("crsctl delete for vds in $ASM_DISK_GROUP ... failed");
     $rc = FALSE;
   }

  return $rc;
}

=head2 CSS_downgrade_VFs

  Downgrades voting files when downgrading to pre-11.2 clusterware.

  This function queries for existing voting files, which cannot be on
  ASM, and deletes and readds them to force proper formatting of the
  voting files

=head3 Parameters

  None

=head3 Notes

  This function must only be executed in an environment where the
  clusterware is not active on any nodes, including the node this
  fucntion is running on.

  This function is intended for downgrading to pre-11.2 clusterware
  only.  It will return TRUE without performing any operations for
  11.2+ clusterware

  This function will fail if VFs are on ASM, as these files cannot
  be downgraded, as there will likely not be any voting files found
  in the OCR configuration

=head3 Returns

  TRUE  - Voting files have been downgraded
  FALSE - One or more voting files has not been downgraded

=cut

sub CSS_downgrade_VFs {
  my $cfg = shift;
  my $crsctl = catfile($cfg->oldconfig('ORA_CRS_HOME'), "bin", "crsctl");
  my $rc = TRUE;
  my ($tempfh, $tempfile);
  my ($cmdrc, @out);

  # For version 10.x and 11.1.x, we need to delete and readd VFs
  if ($cfg->isVersion10 || $cfg->isVersion111) {
    trace ("Querying for existing CSS voting disks for downgrade");
    ($cmdrc, @out) = system_cmd_capture("$crsctl query css votedisk");
    my @vfout = grep(/^ *\d/, @out);
    chomp @vfout;
    my @vflist;

    for my $vfend (@vfout) {
      push @vflist, (split(' ', $vfend))[2];
    }

    my $vfcount = scalar(@vflist);
    # If we only have 1 VF, we cannot delete it and re-add, so add a
    # temporary file
    if ($vfcount == 1) {
      ($tempfh, $tempfile) = tempfile();
      close($tempfh);
      $cmdrc = system_cmd("$crsctl add css votedisk $tempfile -force");
      if ($cmdrc != 0) { $rc = FALSE; }
    }

    while ($rc && scalar(@vflist) > 0) {
      my $vf = shift @vflist;
      $cmdrc = system_cmd("$crsctl delete css votedisk $vf -force");
      if ($cmdrc == 0) {
        $cmdrc = system_cmd("$crsctl add css votedisk $vf -force");
      }

      if ($cmdrc != 0) { $rc = FALSE; }
    }

    if ($tempfile) {
      $cmdrc = system_cmd("$crsctl delete css votedisk $tempfile -force");
      if ($cmdrc != 0) { $rc = FALSE; }
    }
  }

  return $rc;
}

# Private subroutines
sub CSS_start {
  my $ORA_CRS_HOME = $CFG->ORA_CRS_HOME;
  my $crsctl = catfile($ORA_CRS_HOME, "bin", "crsctl");
  my $rc = FALSE;
  my @startcss = ($crsctl, "start", "resource", "ora.cssd", "-init",
                 (@_));
  my @output = system_cmd_capture(@startcss);
  my $status = shift @output;

  if ($status == 0) { $rc = check_service ("css", 2); }
  # If we found a CRS-4402, we know that the startup failed, but no
  # need to report status
  elsif (scalar(grep(/CRS\-4402/, @output)) == 0) {
    push @output, "CSS startup failed with return code $status";
  }

  return ($rc, @output);
}
sub CSS_Local_Endpt {
  our $perms;
  my $name =  $File::Find::name;
  if ($name =~ /OCSSD_LL/ ||
      $name =~ /Oracle_CSS_LclLstnr/ ||
      $name =~ /DBG_CSSD/) {
    trace("Removing $name");
    unlink $name;
  }
}
1;
