MINI MINI MANI MO
# Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
#
# NAME
# asmcmdsys - ASM CoMmanD line interface (Sample Module)
#
# DESCRIPTION
# This module is a dummy/sample module that provides a working
# template for module additions.
#
# NOTES
# usage: asmcmdcore [-p] [command]
#
# MODIFIED (MM/DD/YY)
# emsu 07/07/17 - 26412596: Have lscc display storage access
# prabbala 05/25/17 - 25785813: log more detail if spbackup src has fn.finc
# anovelo 04/26/17 - 25950711: Added GI user check to lscc
# emsu 02/28/17 - 25497063: Add --guid to mkcc, add chcc
# emsu 10/15/16 - 24760407: Pass version to mgmtca in mkcc
# emsu 10/21/16 - 25108506: Add -legacy to 'mgmtca queryRepos' in lscc
# anovelo 10/24/16 - 24706236: Update call to asmcmdshare_get_subdirs
# emsu 08/12/16 - 24381765: Don't allow indirect access on AIX in mkcc
# emsu 07/23/16 - 24322507: Add equivalent PRGR errors in mkcc/rmcc
# anovelo 06/09/16 - 21360641: Create backup spfiles in server mode
# emsu 04/26/16 - 21537310: Add --file option to lscc
# diguzman 05/30/16 - 19654070: Little change at _no_instance_cmd routine
# diguzman 05/24/16 - 23061091: asmcmdshare_set_instance signature change
# emsu 03/03/16 - 22817301: DSC not supported on HPUX
# samjo 04/12/16 - 23052791: Use nohdr with KFOD
# emsu 04/03/16 - 22956099: Make rmcc error messages clearer
# emsu 12/02/15 - 22301967: Add ACFS to mkcc/rmcc/lscc
# diguzman 03/08/16 - 22752719: validate shutdown's target option
# emsu 02/22/16 - 22780177: Fix guid/version mismatch trace in lscc
# emsu 02/03/16 - 22652416: Fix order of RHP version/guid in lscc
# samjo 01/29/16 - 20690631: Start IOS when configuring ASMCC with
# indirect storage
# emsu 01/25/16 - 22375430: Fail mkcc if credfile dir nonexistent
# diguzman 01/21/16 - 22014252: add shutdown --target option
# emsu 11/23/15 - 21623472: Add RHP to lscc
# emsu 10/06/15 - 22088795: Add TFA to mkcc/rmcc/lscc
# emsu 10/22/15 - 21864926: Fix version in mkcc, fix error reporting
# in mk/rmcc, mk/rmcc must be executed by GI user
# emsu 07/28/15 - 20035825: Add RHP to mkcc/rmcc
# emsu 06/22/15 - 20732199: Add GIMR to lscc, support rmcc -f for GIMR
# emsu 05/12/15 - 21044453: Add GIMR to mkcc/rmcc
# siyarlag 05/11/15 - Bug/21116936: fix label set, disallow afd on exadata
# siyarlag 04/02/15 - Bug/19700132: make afd commands clusterwide
# diguzman 03/13/15 - 20678151: Pass array references to asmcmdshare_runcmd
# ssprasad 03/09/15 - Change afd.conf to oracleafd.conf
# prabbala 01/30/15 - 20101171: used asmcmdshare_runcmd to capture cmd op
# siyarlag 01/08/15 - Bug/20307737: print msg based on return code
# siyarlag 12/04/14 - Bug/18838998: display if no device to label
# prabbala 11/25/14 - 19512500: use asmcmdshare_runcmd to capture output
# of a shell command
# samjo 10/15/14 - Bug 19803231. Successful 'asmcmd lscc' should go to
# STDOUT
# ykatada 10/03/14 - #19617921: use bind variables to SELECTs
# somgupta 08/22/14 - Bug/18369237: add lscc
# siyarlag 07/30/14 - Bug/19326908: use HAS commands for SIHA
# alolau 06/10/14 - Add afd_di
# siyarlag 06/24/14 - Bug/19035573: use afdtool always to update afd conf
# siyarlag 05/29/14 - Bug/18865657: add afd_scan
# hppancha 03/26/14 - bug11634278: Add mkcc and rmcc
# pvenkatr 01/22/14 - Bug # 18136383 Added afd commands
# samjo 05/08/14 - Bug 18515716. Add checks in 'spcopy'
# siyarlag 05/21/14 - Bug/18812974: add afd_filter afd_lsdsk
# siyarlag 04/16/14 - Bug/18430406: add afd_configure afd_deconfigure
# afd_state
# pvenkatr 01/22/14 - Bug # 18136383 Added AFD commands
# afd_dsget/afd_dsset/afd_label/afd_unlabel
# pvenkatr 01/15/14 - Bug # 17998892 - Added check for SYSASM for
# spbackup, spcopy, spget, spmove, spset.
# pvenkatr 11/21/13 - #17526682 - Added a check for client cluster before
# calling gpnptsetspfile.
# pvenkatr 09/25/13 - 17398626 - using 'uname -n' & %COMPUTERNAME% instead
# of $HOSTNAME env var.
# pvenkatr 09/03/13 - # 17365414 - invoking SQLPLUS with appropriate
# privileges.
# pvenkatr 08/27/13 - Rearranged reconnect logic in dsset for FlexASM
# scenarios.
# manuegar 05/08/13 - Bug13951456 Support bind parameters
# pvenkatr 02/01/13 - Using asmcmdshare_filter_invisible_cmds
# pvenkatr 08/24/11 - Removed flags hash table - using from XML
# adileepk 06/20/11 - Connection Pooling.
# soye 05/13/11 - lrg5557538: spbackup accepts DB spfile type
# dfriedma 05/12/11 - v$asm_operation updates
# adileepk 11/08/10 - Adding changes to integrate parser module with
# asmcmd.
# moreddy 09/21/10 - 9846823 spcopy -u update gpnp when dstn is os file
# shmubeen 08/16/10 - fix for bug 7189804. lsop enhancement
# shmubeen 05/31/10 - change Dsk_Num header of lsop to Operation
# moreddy 05/06/10 - bug 8667038 NLS for error messages
# amitroy 04/21/10 - BUG 8933243 - USE DIFFERENT CHARACTER IN ASMCMD TO
# "HIDE COLUMN DETAILS" INSTEAD OF -H
# pvenkatr 03/31/10 - Syntax, description, example - all from XML
# moreddy 03/22/10 - Adding more tracing
# moreddy 01/19/10 - Adding tracing messages
# sanselva 12/16/09 - lrg 4067759 fix crsctl/srvctl:for NT
# sanselva 11/02/09 - NT paths interpreted wrongly for spcopy spmove
# pvenkatr 09/03/09 - Help message from xml file.
# amitroy 09/24/09 - adding 'mount' option for STARTUP; bug#8676198
# amitroy 09/22/09 - modifying the hashkey for 'normal'
# amitroy 09/15/09 - #8676198 add NORMAL option for SHUTDOWN
# canuprem 07/29/09 - update dsset and dsget to work in SIHA mode
# sanselva 07/27/09 - #8683273 asm_diskstring glob string issue
# lajain 07/23/09 - Display empty string in dsget.
# sanselva 06/24/09 - correct syntax for command startup
# canuprem 06/18/09 - options for dsset should be mutually exclusive.
# gayavenk 06/11/09 - Fix pathname issue in dsset
# shagarw 06/11/09 - add param to setds to ignore the input ds.
# canuprem 05/29/09 - modify dsset and dsget
# sanselva 05/14/09 - spbackup,spmove,spcopy issue when target directory
# heyuen 04/17/09 - add dsset dsget help
# heyuen 04/20/09 - add spbackup
# sanselva 04/12/09 - ASMCMD long options and consistency
# heyuen 04/06/09 - update spmove to use asmcmdsys_dualget
# heyuen 03/23/09 - remove iostat
# heyuen 01/30/09 - update offline commands
# heyuen 02/09/09 - fix asmcmd_spset
# heyuen 12/03/08 - add spmove, spset, spget
# heyuen 10/24/08 - change zones to regions
# heyuen 10/14/08 - use dynamic modules
# heyuen 09/19/08 - fix ctrl-c in iostat
# heyuen 09/17/08 - complete path for spcopy
# heyuen 08/02/08 - fix startup/shutdown msgs
# heyuen 07/28/08 - use command properties array
# heyuen 07/17/08 - fix messages, rewrite iostat
# heyuen 05/14/08 - add spcopy
# heyuen 05/19/08 - startup restricted
# heyuen 05/12/08 - enable iostat continuously
# heyuen 04/15/08 - bug 6935431, reorder help
# heyuen 03/26/08 - bug 6753856: fix shutdown hang
# heyuen 03/17/08 - enable -g for iostat
# heyuen 02/21/08 - disable iostat for non mounted disks
# heyuen 09/20/07 - creation
#
############################################################################
#
############################ Functions List #################################
#
#############################################################################
package asmcmdsys;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(asmcmdsys_init
%asmcmdsys_cmds
);
use strict;
use DBI qw(:sql_types);
use Getopt::Long qw(:config no_ignore_case bundling);
use asmcmdglobal;
use asmcmdshare;
use asmcmdparser;
use asmcmdbase;
use XML::Parser;
use asmcmdexceptions;
use asmcmdxmlexceptions;
use List::Util qw[min max];
use File::Find;
use File::Spec;
use File::Spec::Functions;
use File::Copy;
use Sys::Hostname;
use Cwd;
####################### ASMCMDSYS Global Constants ######################
our $ASMCMDSYS_SQLPLUS = "$ENV{'ORACLE_HOME'}/bin/sqlplus -S / as sysasm";
# SQLPLUS command with appropriate roles.
our $ASMCMDSYS_SQLPLUS_SYSASM =
"$ENV{'ORACLE_HOME'}/bin/sqlplus -S / as sysasm";
our $ASMCMDSYS_SQLPLUS_SYSDBA =
"$ENV{'ORACLE_HOME'}/bin/sqlplus -S / as sysdba";
our $ASMCMDSYS_SQLPLUS_SYSOPER =
"$ENV{'ORACLE_HOME'}/bin/sqlplus -S / as sysoper" ;
our $ASMCMDSYS_DSC_NAME = "Domain Services Cluster";
our $ASMCMDSYS_IOS_NAME = "Oracle ASM IOServer";
our $ASMCMDSYS_MIN_MC_VER = "12.2.0.1.0"; # minimum valid MC version
# cluster class
use constant CLUSTER_CLASS_STANDALONE => "Standalone";
use constant CLUSTER_CLASS_DOMAINSERVICES => "DomainServices";
use constant CLUSTER_CLASS_MEMBER => "Member";
####################### ASMCMDSYS Global Variables ######################
our (%asmcmdsys_lsop_header) = ('name' ,'Group_Name',
'pass' ,'Pass ',
'state' ,'State',
'power' ,'Power',
'est_work' ,'EST_WORK',
'est_rate' ,'EST_RATE',
'est_minutes' ,'EST_TIME',
);
our (%asmcmdsys_cmds) = (lsop => {},
shutdown => {},
spcopy => {},
spmove => {},
startup => {},
spset => {},
spget => {},
dsset => {},
dsget => {},
spbackup => {},
mkcc => {},
rmcc => {},
lscc => {},
chcc => {}
);
# PLSQL constants
my ($PLSQL_NUMBER) = 22;
# lscc --file component mapping
our (%lsccfile_comps);
sub is_asmcmd
{
return 1;
}
########
# NAME
# asmcmdsys_init
#
# DESCRIPTION
# This function initializes the asmcmdsys module. For now it simply
# registers its callbacks with the asmcmdglobal module.
#
# PARAMETERS
# None
#
# RETURNS
# Null
#
# NOTES
# Only asmcmdcore_main() calls this routine.
########
sub init
{
# All of the arrays defined in the asmcmdglobal module must be
# initialized here. Otherwise, an internal error will result.
push (@asmcmdglobal_command_callbacks, \&asmcmdsys_process_cmd);
push (@asmcmdglobal_help_callbacks, \&asmcmdsys_process_help);
push (@asmcmdglobal_command_list_callbacks, \&asmcmdsys_get_asmcmd_cmds);
push (@asmcmdglobal_is_command_callbacks, \&asmcmdsys_is_cmd);
push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdsys_is_wildcard_cmd);
push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdsys_syntax_error);
push (@asmcmdglobal_no_instance_callbacks, \&asmcmdsys_is_no_instance_cmd);
%asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdsys_cmds);
#Perform ASMCMD consistency check if enabled
if ($asmcmdglobal_hash{'consistchk'} eq 'y')
{
if (!asmcmdshare_check_option_consistency(%asmcmdsys_cmds))
{
exit 1;
}
}
}
########
# NAME
# asmcmdsys_process_cmd
#
# DESCRIPTION
# This routine calls the appropriate routine to process the command
# specified by $asmcmdglobal_hash{'cmd'}.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# 1 if command is found in the asmcmdsys module; 0 if not.
#
# NOTES
# Only asmcmdcore_shell() calls this routine.
########
sub asmcmdsys_process_cmd
{
my ($dbh) = @_;
my ($succ) = 0;
# Get current command from global value, which is set by
# asmcmdsys_parse_asmcmd_args()and by asmcmdcore_shell().
my ($cmd) = $asmcmdglobal_hash{'cmd'};
# Declare and initialize hash of function pointers, each designating a
# routine that processes an ASMCMDSYS command.
my (%cmdhash) = ( startup => \&asmcmdsys_process_startup ,
shutdown => \&asmcmdsys_process_shutdown ,
lsop => \&asmcmdsys_process_lsop,
spcopy => \&asmcmdsys_process_spcopy,
spmove => \&asmcmdsys_process_spmove,
spset => \&asmcmdsys_process_spset,
spget => \&asmcmdsys_process_spget,
dsset => \&asmcmdsys_process_dsset,
dsget => \&asmcmdsys_process_dsget,
spbackup => \&asmcmdsys_process_spbackup,
mkcc => \&asmcmdsys_process_mkcc,
rmcc => \&asmcmdsys_process_rmcc,
lscc => \&asmcmdsys_process_lscc,
chcc => \&asmcmdsys_process_chcc);
if (defined ( $cmdhash{ $cmd } ))
{
# If user specifies a known command, then call routine to process it. #
$cmdhash{ $cmd }->($dbh);
$succ = 1;
}
return $succ;
}
########
# NAME
# asmcmdsys_process_startup
#
# DESCRIPTION
# This function processes the asmcmd command startup.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_startup
{
my (%args);
my ($ret);
my ($qry);
my ($pfile);
my ($sqlplus_stmt);
my ($tempoutputfile) = $asmcmdglobal_hash{'tempdir'} . "/startuptemp.out";
my ($tempfilehandle);
my (@lines) = ();
# Get option parameters, if any.
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#bug 6994980
delete $ENV{'ENV'} if (defined($ENV{'ENV'}));
if ($asmcmdglobal_hash{'contyp'} eq "sysasm")
{
$sqlplus_stmt = "$ASMCMDSYS_SQLPLUS_SYSASM" ;
}
if ($asmcmdglobal_hash{'contyp'} eq "sysdba")
{
$sqlplus_stmt = "$ASMCMDSYS_SQLPLUS_SYSDBA" ;
}
if ($asmcmdglobal_hash{'contyp'} eq "sysoper")
{
$sqlplus_stmt = "$ASMCMDSYS_SQLPLUS_SYSOPER" ;
}
$qry = "startup";
#nomount
if (defined($args{'nomount'}))
{
$qry .= " nomount ";
}
if (defined($args{'restrict'}))
{
$qry .= " restrict ";
}
if (defined($args{'pfile'}))
{
$qry .= " pfile=" . $args{'pfile'};
}
#mount
if (defined($args{'mount'}))
{
$qry .= " mount ";
}
# untaint sqlplus
$sqlplus_stmt =~ m/(.*)/;
$sqlplus_stmt = $1;
unlink $tempoutputfile if(-e $tempoutputfile);
open SQLPLUS, "| $sqlplus_stmt > $tempoutputfile";
print SQLPLUS $qry;
close SQLPLUS;
eval
{
open($tempfilehandle, "<$tempoutputfile") or
die "Could not open the temporary file $tempoutputfile";
my @lines = <$tempfilehandle>;
close $tempfilehandle;
unlink $tempoutputfile;
foreach (@lines)
{
asmcmdshare_print $_;
if($_ =~ m/[A-Z]+-[0-9]+\s*:/)
{
$asmcmdglobal_hash{'e'} = -1;
}
}
};
asmcmdshare_trace(3, "$qry", 'y', 'n');
return;
}
########
# NAME
# asmcmdsys_process_shutdown
#
# DESCRIPTION
# This function processes the asmcmd command shutdown.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_shutdown
{
my ($dbh) = shift;
my ($tgt) = undef; # Target instance. #
my ($sid) = undef; # Target's SID. #
my ($osid) = undef;
my ($inst) = undef;
my (%args); # Argument hash used by getopts(). #
my ($ret); # asmcmdsys_parse_int_args() return value. #
my ($qry); # SQL query statement. #
my ($sqlplus_stmt);
my ($tempoutputfile) = $asmcmdglobal_hash{'tempdir'} . "/shutdowntemp.out";
my ($tempfilehandle);
my (@lines) = ();
my (@what);
my (@from);
my ($sth);
my ($rc);
# Get option parameters, if any.
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
if (defined($args{'target'}))
{
$tgt = $args{'target'};
$sid = asmcmdshare_set_instance($tgt);
if (!defined($sid))
{
my (@eargs) = ("target", $tgt);
asmcmdshare_error_msg(8608, \@eargs);
return 0;
}
return 0 if ($sid eq '');
}
#bug 6994980
delete $ENV{'ENV'} if (defined($ENV{'ENV'}));
if ($asmcmdglobal_hash{'contyp'} eq "sysasm")
{
$sqlplus_stmt = "$ASMCMDSYS_SQLPLUS_SYSASM" ;
}
if ($asmcmdglobal_hash{'contyp'} eq "sysdba")
{
$sqlplus_stmt = "$ASMCMDSYS_SQLPLUS_SYSDBA" ;
}
if ($asmcmdglobal_hash{'contyp'} eq "sysoper")
{
$sqlplus_stmt = "$ASMCMDSYS_SQLPLUS_SYSOPER" ;
}
$qry = "SHUTDOWN ";
if (defined($args{'immediate'}))
{
$qry .= "IMMEDIATE ";
}
elsif (defined($args{'abort'}))
{
$qry .= "ABORT ";
}
else
{
$qry .= "NORMAL ";
}
# untaint sqlplus
$sqlplus_stmt =~ m/(.*)/;
$sqlplus_stmt = $1;
if (defined($dbh))
{
asmcmdbase_disconnect($dbh);
}
if (defined($sid))
{
$osid = $ENV{'ORACLE_SID'};
$ENV{'ORACLE_SID'} = $sid;
}
unlink $tempoutputfile if(-e $tempoutputfile);
open SQLPLUS, "| $sqlplus_stmt > $tempoutputfile";
print SQLPLUS $qry;
close SQLPLUS;
eval
{
open($tempfilehandle, "<$tempoutputfile") or
die "Could not open the temporary file $tempoutputfile";
my @lines = <$tempfilehandle>;
close $tempfilehandle;
unlink $tempoutputfile;
foreach (@lines)
{
asmcmdshare_print $_;
if ($_ =~ m/[A-Z]+-[0-9]+\s*:/)
{
$asmcmdglobal_hash{'e'} = -1;
}
}
};
asmcmdshare_trace(3, "$qry", 'y', 'n');
# Restore original value of $ORACLE_SID and reconnect to ASM instance
if (defined($osid))
{
$ENV{'ORACLE_SID'} = $osid;
$dbh = asmcmdbase_connect(undef);
}
return;
}
########
# NAME
# asmcmdsys_process_lsop
#
# DESCRIPTION
# This function processes the asmcmd command lsop.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_lsop
{
my ($dbh) = shift;
my (@what , @from, $sth, @where, @order, @binds);
my (%min_col_wid, $print_format, $print_string, @what_print);
my ($k, $v, @op_list, $h);
my ($row);
my (%args);
my (@tmp_cols);
my $gnum;
my $ret;
my @eargs;
my $dgname;
# Get option parameters, if any.
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
asmcmdshare_trace(3, "ASMCMD (PID = $$) - LSOP Command\n", 'y', 'n');
push (@what, 'v$asm_diskgroup_stat.name as name');
push (@what, 'v$asm_operation.pass as pass');
push (@what, 'v$asm_operation.state as state');
push (@what, 'v$asm_operation.power as power');
push (@what, 'v$asm_operation.est_work as est_work');
push (@what, 'v$asm_operation.est_rate as est_rate');
push (@what, 'v$asm_operation.est_minutes as est_minutes');
push (@from, 'v$asm_operation');
push (@from, 'v$asm_diskgroup_stat');
push (@where, 'v$asm_operation.group_number = v$asm_diskgroup_stat.group_number');
if( defined($args{'G'}) )
{
$dgname = $args{'G'};
$gnum = asmcmdshare_get_gnum_from_gname($dbh, $dgname);
if( $gnum )
{
# Use the name, rather than the number. Doing so preserves the order
# of v$asm_operation
push (@where, 'v$asm_diskgroup_stat.name = ?');
push (@binds, [uc($dgname), SQL_VARCHAR]);
}
else
{
@eargs = ($dgname);
asmcmdshare_error_msg(8001, \@eargs);
return;
}
}
$sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where,
\@order, \@binds);
@tmp_cols = @{$sth->{NAME}};
@what = ();
foreach (@tmp_cols)
{
push (@what, "\L$_");
}
#initialize the min_col_wid array
foreach(@what)
{
$min_col_wid{$_} = 0;
}
while (defined($row = asmcmdshare_fetch($sth)))
{
my(%op_info) = ();
while(($k, $v) = each(%{$row}))
{
$k =~ tr/[A-Z]/[a-z]/;
if(!defined($v))
{
$v="";
}
$op_info{$k} = $v;
$min_col_wid{$k} = max($min_col_wid{$k}, length($v));
}
push (@op_list, \%op_info);
}
#get header length
foreach (@what)
{
$min_col_wid{$_} = max($min_col_wid{$_},
length($asmcmdsys_lsop_header{$_}));
}
$print_format = '';
foreach (@what)
{
$print_format .= "%-" . $min_col_wid{$_} . "s ";
}
$print_format .= "\n";
#print header
if (!defined ($args{'suppressheader'}) )
{
@what_print = ();
foreach (@what)
{
push (@what_print, $asmcmdsys_lsop_header{$_});
}
$print_string = sprintf($print_format, @what_print);
asmcmdshare_trace(3, "ASMCMD (PID = $$) - LSOP Command - $print_string\n", 'y', 'n');
asmcmdshare_print($print_string);
}
#print rows
foreach $h (@op_list)
{
@what_print = ();
foreach (@what)
{
push (@what_print, $h->{$_});
}
$print_string = sprintf($print_format, @what_print);
asmcmdshare_trace(3, "ASMCMD (PID = $$) - LSOP Command - $print_string\n", 'y', 'n');
asmcmdshare_print($print_string);
}
asmcmdshare_trace(3, "ASMCMD (PID = $$) - LSOP Command Finished\n", 'y', 'n');
return;
}
########
# NAME
# asmcmdsys_process_spcopy
#
# DESCRIPTION
# This function processes the asmcmd command spcopy.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
# The source file type can be ASM SP FILE or ASM BACKUP FILE
# The destination file type is always ASM SP FILE
# When the source file is on a file system, one cannot distinguish between
# various SP file types and has to accept any one of them
########
sub asmcmdsys_process_spcopy
{
my ($dbh) = shift;
my ($src_path, $dst_path, $src_name, $dst_name);
my ($sth);
my ($hdl, $pblk, $fname, $ftyp, $blksz, $openmode);
my (%norm, $ret);
my ($update_gpnp) = 0;
my ($pl_sql_number) = 22;
my ($spfile_number) = 253;
my ($spfile_type) = 31; # KSFD_ASMINIT
my ($spbackup_type) = 33; # KSFD_ASMINITBAK
my ($db_spfile_type) = 13; # KSFD_INIT
my (%args);
my ($fileType, $blkSz, $fileSz);
my ($client_mode) = 0;
my (@eargs);
# Get option parameters, if any.
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# SPCOPY operation for ASM SPFILE requires SYSASM
if ($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
asmcmdshare_error_msg (9485, undef);
return;
}
$src_path = shift(@{$args{'spcopy'}});
$src_path =~ /(.*)/;
$src_path = $1;
$src_name = $src_path;
#Match the last part in the src_path which is source file name
#seperator is '/' for linux path and '\' for NT
$src_name =~ s,(.*/|.*\\)(.*)$,$2,;
$dst_path = shift(@{$args{'spcopy'}});
# complete relative paths
# paths begining with "/"(linux) or containing ":\" (NT) are OS paths
if ($src_path !~ m':\\' and $src_path !~ m'^/')
{
$src_path = asmcmdshare_make_absolute($src_path);
}
if ($dst_path !~ m':\\' and $dst_path !~ m'^/')
{
# Target is ASM
asmcmdsys_get_target_name($dbh,$src_name,$dst_path,\$dst_name);
}
else
{
# target is OS
# if path exists it must be a directory
if ( -d $dst_path )
{
$dst_name = $dst_path . '/' . $src_name;
}
else
{
$dst_name = $dst_path;
}
}
if (!defined $src_path || !defined $dst_name)
{
asmcmdshare_trace(3, "NOTE: Either src_path or dst_name is not defined",
'n', 'n');
return;
}
$update_gpnp = 1 if defined($args{'u'});
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.getfileattr(:src_path, :fileType, :fileSz, :blkSz);
end;
});
# bind input params #
$sth->bind_param( ":src_path", $src_path);
# bind output params #
$sth->bind_param_inout( ":fileType", \$fileType, $PLSQL_NUMBER);
$sth->bind_param_inout( ":fileSz", \$fileSz, $PLSQL_NUMBER);
$sth->bind_param_inout( ":blkSz", \$blkSz, $PLSQL_NUMBER);
$ret = $sth->execute();
if (!defined ($ret) && ($asmcmdglobal_hash{'mode'} eq 'n'))
{
$asmcmdglobal_hash{'e'} = -1;
}
if (!defined $fileType || !defined $fileSz || !defined $blkSz
|| (($fileType != $spfile_type) &&
($fileType != $spbackup_type) &&
($fileType != $db_spfile_type)))
{
@eargs = ( $src_path );
asmcmdshare_error_msg(8303, \@eargs);
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y') if(defined($DBI::errstr));
return;
}
# reconnect
asmcmdbase_disconnect($dbh) if defined ($dbh);
#undef since we connect to local asm instance
$dbh = asmcmdbase_connect(undef);
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.asmcopy(:src_path, :dst_name, :spfile_number,
:fileType, :blkSz, :spfile_number2,
:dst_ftype, :client_mode);
exception when others then
raise;
end;
});
$sth->bind_param( ":src_path", $src_path);
$sth->bind_param( ":dst_name", $dst_name);
$sth->bind_param( ":spfile_number", $spfile_number);
$sth->bind_param( ":fileType", $fileType);
$sth->bind_param( ":blkSz", $blkSz);
$sth->bind_param( ":spfile_number2", $spfile_number);
$sth->bind_param( ":dst_ftype", $spfile_type);
$sth->bind_param( ":client_mode", $client_mode);
$ret = $sth->execute();
if (!defined($ret))
{
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
return;
}
if ($update_gpnp)
{
$ret = asmcmdsys_spset($dbh, $dst_name);
if (!defined($ret))
{
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
return;
}
asmcmdshare_trace(3, "NOTE: spfile $dst_name location has been set in"
." gpnp profile ", 'n', 'n');
}
return;
}
########
# NAME
# asmcmdsys_process_spmove
#
# DESCRIPTION
# This function processes the asmcmd command spmove.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
# The source file type can be ASM SP FILE
# When the source file is on a file system, one cannot distinguish between
# various SP file types and has to accept any one of them
########
sub asmcmdsys_process_spmove
{
my ($dbh) = shift;
my ($attr, $val);
my ($sth);
my ($src_path, $dst_path, $src_name, $dst_name,$spfile);
my (%norm, $ret);
my ($update_gpnp) = 0;
my ($pl_sql_number) = 22;
my ($spfile_number) = 253;
my ($spfile_type) = 31; # KSFD_ASMINIT
my ($db_spfile_type) = 13; # KSFD_INIT
my (%args);
my ($fileType, $blkSz, $fileSz);
my ($client_mode) = 0;
my ($gname);
my (@eargs);
# Get option parameters, if any.
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# SPMOVE operation for ASM SPFILE requires SYSASM privilege
if($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
asmcmdshare_error_msg (9485, undef);
return;
}
$src_path = shift(@{$args{'spmove'}});
$src_path =~ /(.*)/;
$src_path = $1;
$src_name = $src_path;
#Match the last part in the src_path which is source file name
#seperator is '/' for linux path and '\' for NT
$src_name =~ s,(.*/|.*\\)(.*)$,$2,;
$dst_path = shift(@{$args{'spmove'}});
# complete relative paths
# paths begining with "/"(linux) or containing ":\" (NT) are OS paths
if($src_path !~ m':\\' and $src_path !~ m'^/')
{
$src_path = asmcmdshare_make_absolute($src_path);
}
if($dst_path !~ m':\\' and $dst_path !~ m'^/')
{
# Target is ASM
asmcmdsys_get_target_name($dbh,$src_name,$dst_path,\$dst_name);
}
else
{
# target is OS
# if path exists it must be a directory
if ( -d $dst_path )
{
$dst_name = $dst_path . '/' . $src_name;
}
else
{
$dst_name = $dst_path;
}
}
return if (!defined $src_path || !defined $dst_name);
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.getfileattr(:src_path, :fileType, :fileSz, :blkSz);
end;
});
# bind input params #
$sth->bind_param( ":src_path", $src_path);
# bind output params #
$sth->bind_param_inout( ":fileType", \$fileType, $PLSQL_NUMBER);
$sth->bind_param_inout( ":fileSz", \$fileSz, $PLSQL_NUMBER);
$sth->bind_param_inout( ":blkSz", \$blkSz, $PLSQL_NUMBER);
$ret = $sth->execute();
if ((!defined ($ret)) && $asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
if (!defined $fileType || !defined $fileSz || !defined $blkSz
|| (($fileType != $spfile_type) &&
($fileType != $db_spfile_type)))
{
@eargs = ( $src_path );
asmcmdshare_error_msg(8303, \@eargs);
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y') if(defined($DBI::errstr));
return;
}
# reconnect
asmcmdbase_disconnect($dbh) if defined ($dbh);
#undef since we connect to local asm instance
$dbh = asmcmdbase_connect(undef);
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.asmcopy(:src_path, :dst_name, :spfile_number,
:fileType, :blkSz, :spfile_number2,
:dst_ftype, :client_mode);
exception when others then
raise;
end;
});
$sth->bind_param( ":src_path", $src_path);
$sth->bind_param( ":dst_name", $dst_name);
$sth->bind_param( ":spfile_number", $spfile_number);
$sth->bind_param( ":fileType", $fileType);
$sth->bind_param( ":blkSz", $blkSz);
$sth->bind_param( ":spfile_number2", $spfile_number);
$sth->bind_param( ":dst_ftype", $spfile_type);
$sth->bind_param( ":client_mode", $client_mode);
$ret = $sth->execute();
if (!defined($ret))
{
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
return;
}
# if the current spfile is the active one, update gpnp
$spfile = asmcmdsys_dualget($dbh, 'asm_spfile');
if (!defined($spfile) or $spfile ne $dst_name)
{
asmcmdshare_trace(3, "NOTE: The current spfile is the active one."
." Updating gpnp profile to $dst_name", 'n', 'n');
$ret = asmcmdsys_spset($dbh, $dst_name);
if (!defined($ret))
{
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
# UNDO the move if GPnP update is not successful
if($dst_name =~ m'^\+')
{
%norm = asmcmdshare_normalize_path($dbh, $src_path, 0, \$ret);
$gname = asmcmdshare_get_gname_from_ gnum ($dbh, $norm{'gnum'}->[0]);
asmcmdbase_rm_sql($dbh, $gname, $norm{'path'}->[0], 0);
}
else
{
#os File
#Untaint
$dst_name =~ m/(.*)/;
$dst_name = $1;
unlink("$dst_name");
}
return;
}
}
asmcmdbase_disconnect($dbh) if defined ($dbh);
#undef since we connect to local asm instance
$dbh = asmcmdbase_connect(undef);
#delete the source file
asmcmdshare_trace(3, "NOTE: Deleting the source file ", 'n', 'n');
if ($src_path =~ m'^\+')
{
# asm spfile
%norm = asmcmdshare_normalize_path($dbh, $src_path, 0, \$ret);
$gname = asmcmdshare_get_gname_from_gnum ($dbh, $norm{'gnum'}->[0]);
asmcmdbase_rm_sql($dbh, $gname, $norm{'path'}->[0], 0);
}
else
{
#os spfile
unlink($src_path);
}
}
########
# NAME
# asmcmdsys_process_spbackup
#
# DESCRIPTION
# This function processes the asmcmd command spbackup.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_spbackup
{
my ($dbh) = shift;
my (%args, $ret);
my ($src_path, $dst_path,$src_name,$dst_name);
my ($sth, $fileType, $fileSz, $blkSz);
my ($spfile_type) = 31;
my ($db_spfile_type) = 13;
my ($spbackup_type) = 33;
my ($client_mode) = 0;
my (@eargs);
# Get option parameters, if any.
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# SPBACKUP operation for ASM SPFILE requires SYSASM privilege
if($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
asmcmdshare_error_msg (9485, undef);
return;
}
$src_path = shift(@{$args{'spbackup'}});
$src_path =~ /(.*)/;
$src_path = $1;
$src_name = $src_path;
#Match the last part in the src_path which is source file name
#seperator is '/' for linux path and '\' for NT
$src_name =~ s,(.*/|.*\\)(.*)$,$2,;
$dst_path = shift(@{$args{'spbackup'}});
# complete relative paths
# paths begining with "/"(linux) or containing ":\" (NT) are OS paths
if($src_path !~ m':\\' and $src_path !~ m'^/')
{
$src_path = asmcmdshare_make_absolute($src_path);
}
if($dst_path !~ m':\\' and $dst_path !~ m'^/')
{
asmcmdsys_get_target_name($dbh,$src_name,$dst_path,\$dst_name);
}
else
{
# target is OS
# if path exists it must be a directory
if ( -d $dst_path )
{
$dst_name = $dst_path . '/' . $src_name;
}
else
{
$dst_name = $dst_path;
}
}
if (!defined $src_path || !defined $dst_name)
{
asmcmdshare_trace(3, "NOTE: Either src_path or dst_name is not defined",
'n', 'n');
return;
}
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.getfileattr(:src_path, :fileType, :fileSz, :blkSz);
end;
});
# bind input params #
$sth->bind_param( ":src_path", $src_path);
# bind output params #
$sth->bind_param_inout( ":fileType", \$fileType, $PLSQL_NUMBER);
$sth->bind_param_inout( ":fileSz", \$fileSz, $PLSQL_NUMBER);
$sth->bind_param_inout( ":blkSz", \$blkSz, $PLSQL_NUMBER);
$ret = $sth->execute();
if ((!defined ($ret)) && $asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
if (!defined $fileType || !defined $fileSz || !defined $blkSz
|| ($fileType != $spfile_type && $fileType != $db_spfile_type))
{
@eargs = ( $src_path );
asmcmdshare_error_msg(8303, \@eargs);
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y') if(defined($DBI::errstr));
return;
}
# reconnect
asmcmdbase_disconnect($dbh) if defined ($dbh);
#undef since we connect to local asm instance
$dbh = asmcmdbase_connect(undef);
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.asmcopy(:src_path, :dst_name, :spfile_number,
:fileType, :blkSz, :dst_fnum, :dst_ftype,
:client_mode);
exception when others then
raise;
end;
});
$sth->bind_param(":src_path", $src_path);
$sth->bind_param(":dst_name", $dst_name);
$sth->bind_param(":spfile_number", 0);
$sth->bind_param(":fileType", $fileType);
$sth->bind_param(":blkSz", $blkSz);
$sth->bind_param(":dst_fnum", 0);
$sth->bind_param(":dst_ftype", $spbackup_type);
$sth->bind_param(":client_mode", $client_mode);
$ret = $sth->execute();
if (!defined($ret))
{
if($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
return;
}
asmcmdbase_disconnect($dbh) if defined ($dbh);
#undef since we connect to local asm instance
$dbh = asmcmdbase_connect(undef);
}
########
# NAME
# asmcmdsys_get_target_name
#
# DESCRIPTION
# This function gets the complete target file name when the
# destination is a directory or diskgroup for spcopy/spmove/spbackup.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# src_name (IN) - spfile name
# dst_path (IN) - destination path
# dst_name (IN/OUT) - destination pathname
#
# RETURNS
# Null.
#
# NOTES
#
########
sub asmcmdsys_get_target_name
{
my ($dbh,$src_name,$dst_path,$dst_name) = @_;
my (@paths, @ref_ids, @par_ids, @dg_nums, @entry_list, $name ,$ret);
my (%norm);
my (@eargs);
# target is ASM
# if target is a relative path, complete it to be a valid ASM path
%norm = asmcmdshare_normalize_path($dbh, $dst_path, 1, \$ret);
# path exists, must be a directory, and need to complete it with
# the source name
if ( $ret == 0 )
{
# complete the path
$dst_path = $norm{'path'}->[0];
@paths = @{ $norm{'path'} };
@ref_ids = @{ $norm{'ref_id'} };
@par_ids = @{ $norm{'par_id'} };
@dg_nums = @{ $norm{'gnum'} };
$name = $paths[0];
$name =~ s,.*/(.*)$,$1,;
if($ref_ids[0] == -1)
{
@eargs = ($dst_path);
asmcmdshare_error_msg(8014, \@eargs);
return;
}
asmcmdshare_get_subdirs($dbh, \@entry_list, undef, $dg_nums[0],
$ref_ids[0], $par_ids[0], $name, undef, 0, 1);
# if the directory is a diskgroup or a directory
if ( ($ref_ids[0] != -1) &&
(($ref_ids[0] == $dg_nums[0] << 24 ) ||
($entry_list[0]->{'alias_directory'} eq 'Y')))
{
if ($src_name =~ /^\S+\.\d+\.\d+$/)
{
asmcmdshare_trace(1, "ERROR: Unable to form destination file name.",
'y', 'y');
asmcmdshare_trace(1, "ERROR: Source file name has file number and " .
"incarnation in it. Provide destination file name.",
'y', 'y');
@eargs = ( $src_name );
asmcmdshare_error_msg(8303, \@eargs);
return ;
}
$$dst_name = $dst_path . '/' . $src_name;
}
elsif($entry_list[0]->{'alias_directory'} eq 'N')
{
$$dst_name = $dst_path;
}
else
{
@eargs = ($dst_path);
asmcmdshare_error_msg(8014, \@eargs);
return;
}
}
else
{
$$dst_name = $dst_path;
}
}
########
# NAME
# asmcmdsys_process_spget
#
# DESCRIPTION
# This function processes the asmcmd command spget.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_spget
{
my ($dbh) = shift;
my ($spfile);
# SPGET operation for ASM SPFILE requires SYSASM privilege
if($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
asmcmdshare_error_msg (9485, undef);
return;
}
# If any options are added to the command,
# asmcmdsys_parse_int_args() should be called.
#
# Get option parameters, if any.
#$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
#return unless defined ($ret);
$spfile = asmcmdsys_dualget($dbh, 'asm_spfile');
if (defined($spfile))
{
asmcmdshare_print($spfile ."\n");
}
}
########
# NAME
# asmcmdsys_process_dsset
#
# DESCRIPTION
# This function processes the asmcmd command dsset.
#
# USAGE
# dsset [--normal] [--parameter] [--profile [-f]] pathname
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# second arg to gpnpsetds is false so that user supplied diskstring
# gets pushed to gpnp profile.
#########
sub asmcmdsys_process_dsset
{
my ($dbh) = @_;
my ($ret, %args, $sth);
my ($forced);
my ($qry);
my ($gpnptool);
my ($gpnp_sign);
my ($gpnp_edit);
my ($gpnp_seq);
my ($prof_path);
my ($peer_path);
my ($seq_num);
my ($buf) = "";
my ($normal);
my ($num);
my ($test);
my ($status) = "";
my ($crs_home);
my ($crsctl);
my ($srvctl);
my ($update);
my ($path) = 0;
my (@buf_arr);
my ($check_siha);
my (@eargs);
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
$forced = 0;
$normal = 0;
if (defined($args{'normal'}) ||
(!defined($args{'parameter'}) && !defined($args{'profile'})))
{
$normal = 1;
}
if (defined($args{'f'}))
{
$forced = 1;
}
$path = join(',', @{$args{'dsset'}});
# asm_diskstring should be of the format 'path1','path2','path3',.....
$path =~ s/,/','/g;
#ASM instance is required for all the options other than '--profile -f'
if (!defined($dbh))
{
if (!(defined($args{'profile'}) && $forced == 1))
{
asmcmdshare_error_msg(8102, undef);
return;
}
}
# Problem: If dsset is used multiple times in a asmcmd session
# pathname gets messed up. Reason being, the buffer cache used for
# storing pathname in SQL is not cleared on every call.
# This workaround could be removed if the PL/SQL buffer problem is
# fixed.
if (defined($dbh))
{
if (!(defined($args{'profile'}) && $forced == 1 ))
{
# If connection to ASM exists, and "--profile -f" not specified, then
# disconnect and connect.
asmcmdbase_disconnect ($dbh);
$dbh = asmcmdbase_connect (undef)
}
}
if ($normal == 1)
{
$qry = "alter system set asm_diskstring='" . $path . "' SCOPE=BOTH";
$ret = asmcmdshare_do_stmt($dbh,$qry);
return;
}
if (defined($args{'profile'}))
{
if ($forced == 1)
{
$crsctl = "$ENV{'ORACLE_HOME'}/bin/crsctl";
#The path in WIN is ORACLE_HOME/bin/crsctl.exe
$crsctl .= ".exe" if ($^O =~ /win/i);
# Make sure the crsctl binary exists and can be executed.
if (! -x $crsctl)
{
#If not, try at a second locaction.
$crsctl = "$ENV{'ORACLE_HOME'}/rdbms/bin/crsctl";
$crsctl .= ".exe" if ($^O =~ /win/i);
if (! -x $crsctl)
{
@eargs = ($crsctl);
asmcmdshare_error_msg(8313, \@eargs);
return;
}
}
#untaint crsctl
$crsctl =~ /([^\n^\r^\t]+)/;
$crsctl = $1;
asmcmdshare_trace(3, "NOTE: Checking the status of cluster.. ", 'y', 'n');
if (asmcmdshare_runcmd("$crsctl check css", \@buf_arr, 1, 0))
{
asmcmdshare_error_msg(8309, undef);
return;
}
# Check for the number at CRS-xxxx: in the output.
foreach (@buf_arr)
{
$status = $2 if (/([^\d]+)([^:]+)/);
last if (defined($status) and $status);
}
if (defined($status) and $status)
{
if ($status eq 4529)
{
#output is CRS-4529: Cluster Synchronization Services is online
asmcmdshare_error_msg(8308, undef);
return;
}
}
else
{
asmcmdshare_error_msg(8309, undef);
return;
}
$check_siha = "$crsctl status resource ora.crsd -init -g";
if (asmcmdshare_runcmd($check_siha, \@buf_arr, 1, 0))
{
asmcmdshare_error_msg(8315, undef);
return;
}
# Get the error code form the o/p
foreach (@buf_arr)
{
if (/([^\d]+)([^:]+)/)
{
$status = $2;
last;
}
}
if (!defined($status) or !$status)
{
asmcmdshare_error_msg(8315, undef);
return;
}
# siha mode, crsd is not registered
# CRS-212: Resource 'ora.crsd' is not registered.
if ($status eq 212)
{
$srvctl = "$ENV{'ORACLE_HOME'}/bin/srvctl";
#path in Win is ORACLE_HOME/bin/srvctl.bat
$srvctl .= ".bat" if ($^O =~ /win/i);
# Make sure the srvctl binary exists and can be executed.
if (! -x $srvctl)
{
#If not, try at a second locaction.
$srvctl = "$ENV{'ORACLE_HOME'}/srvm/bin/srvctl";
#path in Win is ORACLE_HOME/bin/srvctl.bat
$srvctl .= ".bat" if ($^O =~ /win/i);
if (! -x $srvctl)
{
@eargs = ($srvctl);
asmcmdshare_error_msg(8316, \@eargs);
return;
}
}
$update = " $srvctl modify asm -d " . $path;
$update =~ /([^\n^\r^\t]+)/;
$update = $1;
if (asmcmdshare_runcmd($update, \@buf_arr, 1, 1))
{
asmcmdshare_error_msg(8314, undef);
}
return;
}
# cluster mode
# CRS-4003 :Resource 'ora.crsd' is registered.
elsif ($status eq 4003)
{
#HOSTNAME should be defined
$buf = asmcmdshare_get_host_name();
if (!defined($buf))
{
$buf = "HOSTNAME";
@eargs = ($buf);
asmcmdshare_error_msg(8318, \@eargs);
return;
}
$gpnptool = "$ENV{'ORACLE_HOME'}/bin/gpnptool";
# Adjust path for win
$gpnptool .= ".exe" if ($^O =~ /win/i);
# Make sure the gpnptool binary exists and can be executed.
if (! -x $gpnptool)
{
#If not, try at a second locaction.
$gpnptool = "$ENV{'ORACLE_HOME'}/has/bin/gpnptool";
# Adjust path for win
$gpnptool .= ".exe" if($^O =~ /win/i);
if (! -x $gpnptool)
{
@eargs = ($gpnptool);
asmcmdshare_error_msg(8305, \@eargs);
return;
}
}
# The location of profile.xml is different for production and
# dev environment now. The current environment is being determined by
# checking whethe ADE_VIEW_ROOT is set or not. Bug 8579922 is filed
# for this issue.
$test = "$ENV{'ORACLE_HOME'}/gpnp/".
asmcmdshare_get_host_name().
"/profiles/peer/profile.xml";
if ( -f $test) #production environment
{
$crs_home = "$ENV{'ORACLE_HOME'}";
}
else # dev env
{
$test = "$ENV{'ORACLE_HOME'}/has_work/gpnp/".
asmcmdshare_get_host_name().
"/profiles/peer/profile.xml";
if (-f $test)
{
$crs_home = "$ENV{'ORACLE_HOME'}/has_work";
}
else
{
asmcmdshare_error_msg(8319,undef);
return;
}
}
#if(!defined($ENV{'ADE_VIEW_ROOT'}))
#{
# $crs_home = "$ENV{'ORACLE_HOME'}";
#}
#else
#{
# $crs_home = "$ENV{'ORACLE_HOME'}/has_work";
#}
$prof_path = "$crs_home/gpnp/".
asmcmdshare_get_host_name ().
"/profiles/peer/profile.xml";
#untaint prof_path
$prof_path =~ /([^\n^\r^\t]+)/;
$prof_path = $1;
$peer_path = "$crs_home/gpnp/".
asmcmdshare_get_host_name().
"/wallets/peer";
#untaint peer_path
$peer_path =~ /([^\n^\r^\t]+)/;
$peer_path = $1;
# get the sequence number of the profile.
$gpnp_seq = "$gpnptool getpval -p=$prof_path -prf_sq -o-";
#untaint gpnp_seq
$gpnp_seq =~ /([^\n^\r^\t]+)/;
$gpnp_seq = $1;
asmcmdshare_trace(3, "NOTE: Setting the gpnp profile sequence "
."number.. ", 'n', 'n');
if (asmcmdshare_runcmd($gpnp_seq, \@buf_arr, 0, 0))
{
asmcmdshare_error_msg(8310, undef);
}
$seq_num = $buf_arr[0] + 3;
$gpnp_edit = "$gpnptool edit -p=$prof_path -o=$prof_path -ovr " .
"-asm_dis=$path -prf_sq=$seq_num";
#untaint gpnp_edit
$gpnp_edit =~ /([^\n^\r^\t]+)/;
$gpnp_edit = $1;
asmcmdshare_trace(3, "NOTE: Editing the gpnp profile.. ", 'n', 'n');
if (asmcmdshare_runcmd($gpnp_edit, \@buf_arr, 1, 1))
{
asmcmdshare_error_msg(8306, undef);
return;
}
$gpnp_sign = "$gpnptool sign -p=$prof_path -o=$prof_path -ovr " .
"-w=file:$peer_path";
#untaint gpnp_sign
$gpnp_sign =~ /([^\n^\r^\t]+)/;
$gpnp_sign = $1;
asmcmdshare_trace(3, "NOTE: Signing the gpnp profile.. ", 'n', 'n');
# sign the gpnp profile once its edited.
if (asmcmdshare_runcmd($gpnp_sign, \@buf_arr, 0, 1))
{
asmcmdshare_error_msg(8307, undef);
return;
}
}
# could not determine mode
else
{
asmcmdshare_error_msg(8315, undef);
return;
}
}
else
{
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.gpnpsetds(:ds_path,0);
exception when others then
raise;
end;
});
$sth->bind_param(":ds_path", $path);
$ret = $sth->execute();
if (!defined ($ret))
{
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
}
}
return;
}
#parameter option.
if (defined($args{'parameter'}))
{
$qry = "alter system set asm_diskstring='" . $path . "' SCOPE=MEMORY";
$ret = asmcmdshare_do_stmt($dbh,$qry);
return;
}
}
########
# NAME
# asmcmdsys_process_dsget
#
# DESCRIPTION
# This function processes the asmcmd command dsget.
#
# USAGE
# dsget [[--normal] [--profile [-f]] [--parameter]]
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
#########
sub asmcmdsys_process_dsget
{
my ($dbh) = shift;
my ($diskstring, $sth, $ret, $row, $val);
my (@what, @from, @where, @order, @binds);
my ($num);
my ($buf);
my (@buf_arr);
my ($test);
my ($parameter);
my ($profile);
my ($forced);
my ($status);
my (%args);
my ($gpnptool);
my ($gpnp_exec);
my ($prof_path);
my ($crs_home);
my ($crsctl);
my ($srvctl);
my ($get_dis);
my (@eargs);
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
$num = keys(%args);
$forced = 0;
$parameter = 0;
$profile = 0;
if (defined($args{'f'}))
{
$forced = 1;
}
# normal mode should return both profile and parameter settings.
# normal is the default option
if (defined($args{'normal'}) ||
(!defined($args{'profile'}) && !defined($args{'parameter'})))
{
$parameter = 1;
$profile = 1;
}
elsif (defined($args{'profile'}))
{
$profile = 1;
}
elsif (defined($args{'parameter'}))
{
$parameter = 1;
}
#ASM instance is required for all the options other than '--profile -f'
if (!defined($dbh))
{
if (!(defined($args{'profile'}) && $forced == 1))
{
asmcmdshare_error_msg(8102, undef);
return;
}
}
if ($parameter == 1)
{
push(@what, 'value');
push(@from, 'v$parameter');
push(@where, 'name = ?');
push(@binds, ['asm_diskstring', SQL_VARCHAR]);
$sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where,
\@order, \@binds);
$row = asmcmdshare_fetch($sth);
asmcmdshare_finish($sth);
$val = $row->{VALUE};
#A row is always returned because asm_diskstring always exists.
#If the value of row is NULL, then DBI returns undefined.
#If the value of row is "", then DBI returns "".
#It looks like empty string("") is being stored as NULL and hence DBI
#returns undefined value.
#Adding code below to display empty string in dsget.
$val = "" if !defined($val);
if (defined($val))
{
asmcmdshare_print("parameter:" . $val . "\n");
}
}
if ($profile == 1)
{
# force with profile option.
if ($forced == 1)
{
$crsctl = "$ENV{'ORACLE_HOME'}/bin/crsctl";
#The path in WIN is ORACLE_HOME/bin/crsctl.exe
$crsctl .= ".exe" if ($^O =~ /win/i);
# Make sure the crsctl binary exists and can be executed.
if (! -x $crsctl)
{
#If not, try at a second locaction.
$crsctl = "$ENV{'ORACLE_HOME'}/rdbms/bin/crsctl";
#The path in WIN is ORACLE_HOME/bin/crsctl.exe
$crsctl .= ".exe" if ($^O =~ /win/i);
if (! -x $crsctl)
{
@eargs = ($crsctl);
asmcmdshare_error_msg(8313, \@eargs);
return;
}
}
#untaint crsctl
$crsctl =~ /([^\n^\r^\t]+)/;
$crsctl = $1;
asmcmdshare_trace(3, "NOTE: Checking the status of cluster.. ", 'y', 'n');
if (asmcmdshare_runcmd("$crsctl check css", \@buf_arr, 1, 0))
{
asmcmdshare_error_msg(8309, undef);
return;
}
# Check for the number at CRS-xxxx in the output.
# Could not check cluster status if anything fails.
foreach (@buf_arr)
{
if (/([^\d]+)([^:]+)/)
{
$status = $2;
last;
}
}
if (defined($status) and $status)
{
if($status eq 4529)
{
# CRS-4529: Cluster Synchronization Services is online
asmcmdshare_error_msg(8308, undef);
return;
}
}
else
{
asmcmdshare_error_msg(8309, undef);
return;
}
# gpnpd does not come up in SIHA mode
if (asmcmdshare_runcmd("$crsctl status resource ora.crsd -init -g",
\@buf_arr, 1, 0))
{
asmcmdshare_error_msg(8315, undef);
return;
}
#get the error code
foreach (@buf_arr)
{
if (/([^\d]+)([^:]+)/)
{
$status = $2;
last;
}
}
if (!defined($status) or !$status)
{
asmcmdshare_error_msg(8315, undef);
return;
}
# siha mode, crsd is not registered
if($status eq 212)
{
$srvctl = "$ENV{'ORACLE_HOME'}/bin/srvctl";
#path in Win is ORACLE_HOME/bin/srvctl.bat
$srvctl .= ".bat" if ($^O =~ /win/i);
# Make sure the srvctl binary exists and can be executed.
if (! -x $srvctl)
{
#If not, try at a second locaction.
$srvctl = "$ENV{'ORACLE_HOME'}/srvm/bin/srvctl";
#path in Win is ORACLE_HOME/bin/srvctl.bat
$srvctl .= ".bat" if ($^O =~ /win/i);
if (! -x $srvctl)
{
@eargs = ($srvctl);
asmcmdshare_error_msg(8316, \@eargs);
return;
}
}
#untaint srvctl
$srvctl =~ /([^\n^\r^\t]+)/;
$srvctl = $1;
if (asmcmdshare_runcmd("$srvctl config asm", \@buf_arr, 1, 1))
{
asmcmdshare_error_msg(8317, undef);
return;
}
foreach (@buf_arr)
{
if (/discovery string:([^\n]+)/)
{
$buf = $1;
last;
}
}
# Extract the diskstring from the output
if (defined($buf) and $buf)
{
asmcmdshare_print("profile:" . $buf . "\n");
}
else
{
asmcmdshare_error_msg(8317, undef);
}
return;
}
#cluster mode
elsif ($status eq 4003)
{
#HOSTNAME should be defined
$buf = asmcmdshare_get_host_name ();
if (!defined($buf))
{
$buf = "HOSTNAME";
@eargs = ($buf);
asmcmdshare_error_msg(8318, \@eargs);
return;
}
$gpnptool = "$ENV{'ORACLE_HOME'}/bin/gpnptool";
# Adjust path for win
$gpnptool .= ".exe" if ($^O =~ /win/i);
# Make sure the gpnptool binary exists and can be executed.
if (! -x $gpnptool)
{
#If not, try at a second locaction.
$gpnptool = "$ENV{'ORACLE_HOME'}/has/bin/gpnptool";
# Adjust path for win
$gpnptool .= ".exe" if ($^O =~ /win/i);
if (! -x $gpnptool)
{
@eargs = ($gpnptool);
asmcmdshare_error_msg(8305, \@eargs);
return;
}
}
# The location of profile.xml is different for production and
# dev environment now. The current environment is being determined by
# checking whethe ADE_VIEW_ROOT is set or not. Bug 8579922 is filed
# for this issue.
$test = "$ENV{'ORACLE_HOME'}/gpnp/".
asmcmdshare_get_host_name ().
"/profiles/peer/profile.xml";
if ( -f $test) #production env
{
$crs_home = "$ENV{'ORACLE_HOME'}";
}
else #dev env
{
$test = "$ENV{'ORACLE_HOME'}/has_work/gpnp/".
asmcmdshare_get_host_name().
"/profiles/peer/profile.xml";
if (-f $test)
{
$crs_home = "$ENV{'ORACLE_HOME'}/has_work";
}
else
{
asmcmdshare_error_msg(8319, undef);
return;
}
}
#if(!defined($ENV{'ADE_VIEW_ROOT'}))
#{
# $crs_home = "$ENV{'ORACLE_HOME'}";
#}
#else
#{
# $crs_home = "$ENV{'ORACLE_HOME'}/has_work";
#}
#untaint gpnptool
$gpnptool =~ /([^\n^\r^\t]+)/;
$gpnptool = $1;
$prof_path = "$crs_home/gpnp/".asmcmdshare_get_host_name().
"/profiles/peer/profile.xml";
#untaint prof_path
$prof_path =~ /([^\n^\r^\t]+)/;
$prof_path = $1;
$gpnp_exec = "$gpnptool getpval -p=$prof_path -asm_dis -o-";
$gpnp_exec =~ /([^\n^\r^\t]+)/;
$gpnp_exec = $1;
if (asmcmdshare_runcmd($gpnp_exec, \@buf_arr, 0, 0))
{
asmcmdshare_error_msg(8307, undef);
return;
}
if (defined($buf_arr[0]) and $buf_arr[0])
{
asmcmdshare_print("profile:" . $buf_arr[0] . "\n");
}
}
else
{
asmcmdshare_error_msg(8315, undef);
return;
}
}
else
{
$diskstring = asmcmdsys_dualget($dbh, 'asm_diskstring');
#Adding code below to display empty string in dsget.
$diskstring = "" if !defined($diskstring);
if (defined($diskstring))
{
asmcmdshare_print("profile:" . $diskstring . "\n");
}
}
return;
}
}
########
# NAME
# asmcmdsys_dualget
#
# DESCRIPTION
# This function returns the requested valure from sys_cluster_properties.
#
# USAGE
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# $what (IN) - property/value requested
# $val (OUT) -
#
# RETURNS
# Requested value querying from sys_context
#
#########
sub asmcmdsys_dualget
{
my ($dbh, $what) = @_;
my ($stmt, $ret, $val, $row);
=pod
We use 4000 because asm_diskstring can be more than the default max(256)
The default maximum size can be overriden by specifying the optional length
parameter, which must be a NUMBER or a value that can be implicitly
converted to NUMBER.The valid range of values is 1 to 4000 bytes. If you
specify an invalid value, then Oracle Database ignores it and uses the
default.For more details see SYS_CONTEXT in SQL Reference.
=cut
$stmt = "select sys_context('sys_cluster_properties','" . $what . "',4000) ".
"as val from dual";
$ret = asmcmdshare_do_select($dbh, $stmt);
$row = asmcmdshare_fetch($ret);
asmcmdshare_finish($ret);
$val = $row->{VAL};
return $val;
}
########
# NAME
# asmcmdsys_spset
#
# DESCRIPTION
# This function sets the spfile location in the gPnP profile.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# spfile(IN) - spfile path.
#
# RETURNS
# Path of the spfile.
#
# NOTES
# The caller is responsible to check that the file exists.
#
########
sub asmcmdsys_spset
{
my ($dbh, $path) = @_;
my ($ret, $sth);
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.gpnpsetsp(:spfile_path);
exception when others then
raise;
end;
});
$sth->bind_param( ":spfile_path", $path);
$ret = $sth->execute();
return $ret;
}
########
# NAME
# asmcmdsys_process_spset
#
# DESCRIPTION
# This function processes the asmcmd command spset.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_spset
{
my ($dbh) = shift;
my ($attr, $val);
my ($ret, %args, $sth, $row, $dst_path);
my ($spfile);
my (%result);
my (@eargs);
my (@what , @from, @where, @order);
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
($spfile) = @{$args{'spset'}};
# SPSET operation for ASM SPFILE requires SYSASM privilege
if($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
asmcmdshare_error_msg (9485, undef);
return;
}
#Normalize the path - if it is an OS path (starting with '/')
#it has to be absolute, otherwise it is either an absolute ASM path
#or a relative one
if ( (($spfile !~ /^[a-z]:/i) && ($spfile !~ m'^[/\\\\]')) || ($spfile !~ m'^/'))
{
#It must be an ASM path. Normalize the ASM path
%result = asmcmdshare_normalize_path ($dbh, $spfile, 0, \$ret);
if ($ret != 0)
{
#Check if file exists
@eargs = ($spfile);
asmcmdshare_error_msg(8014, \@eargs);
return;
}
($spfile) = @{$result{'path'}};
}
else
{
#It should be an absolute OS path. Check if file exists
if ( !(-e $spfile) )
{
@eargs = ($spfile);
asmcmdshare_error_msg(8014, \@eargs);
return;
}
#$spfile =~ s/\\/\//g;
}
$ret = asmcmdsys_spset($dbh, $spfile);
if (!defined($ret))
{
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
return;
}
return;
}
########
# NAME
# asmcmdsys_get_asmdiskstr_fromprofile
#
# DESCRIPTION
# This function returns asm_diskstring value from gpnp profile.
#
# PARAMETERS
# NONE
#
# RETURNS
# asm_diskstring value if available ; "" otherwise.
########
sub asmcmdsys_get_asmdiskstr_fromprofile
{
my ($kfod);
my (@buf);
my (@eargs);
# if unable to query, return empty string
my $asmdiskstr = "";
my ($asm_discstr) = "";
$kfod = catfile($ENV{'ORACLE_HOME'}, 'bin', 'kfod');
$kfod .= ".exe" if($^O =~ /win/i);
# Make sure the kfod binary exists and can be executed.
if (! -x $kfod)
{
@eargs = ($kfod);
asmcmdshare_error_msg(9515, \@eargs);
return;
}
asmcmdshare_runcmd("$kfod op=GPNPDSTR nohdr=true", \@buf, 0, 0);
foreach my $line (@buf)
{
$asmdiskstr = $line;
}
# kfod returns one line o/p for asm_diskstring.
# Last line contains the asm_diskstring.
#
# kfod may return 0 but asm_diskstring may
# be 'Not Set' or 'Not Available'.
#
# If asm_diskstring was never setup at profile creation
# time or later, the default value in profile.xml is
# "++no-value-at-profile-creation--never-updated-through-ASM++"
# This special string also needs to be handled well.
# For those, return empty string.
#
# Empty string is interpreted as default discovery string
# by afdboot/afdtool.
#
chomp($asmdiskstr);
if( ($asmdiskstr eq "Not Set") ||
($asmdiskstr eq "Not Available") ||
($asmdiskstr eq "++no-value-at-profile-creation--never-updated-through-ASM++") )
{
$asmdiskstr = "";
}
asmcmdshare_trace(3, "Retrieved asm disk string from gpnp : \'$asmdiskstr\'",
'y', 'n');
return $asmdiskstr;
} # end asmcmdsys_get_asmdiskstr_fromprofile
########
# NAME
# asmcmdsys_is_help
#
# DESCRIPTION
# This function is the help function for the ASMCMDSYS module.
#
# PARAMETERS
# command (IN) - display the help message for this command.
#
# RETURNS
# 1 if command found; 0 otherwise.
########
sub asmcmdsys_process_help
{
my ($command) = shift; # User-specified argument; show help on $cmd. #
my ($desc); # Command description for $cmd. #
my ($succ) = 0; # 1 if command found, 0 otherwise. #
if (asmcmdsys_is_cmd ($command))
{ # User specified a command name to look up. #
$desc = asmcmdshare_get_help_desc($command);
asmcmdshare_print "$desc\n";
$succ = 1;
}
return $succ;
}
########
# NAME
# asmcmdsys_is_cmd
#
# DESCRIPTION
# This routine checks if a user-entered command is one of the known
# ASMCMD internal commands that belong to the ASMCMDSYS module.
#
# PARAMETERS
# arg (IN) - user-entered command name string.
#
# RETURNS
# True if $arg is one of the known commands, false otherwise.
########
sub asmcmdsys_is_cmd
{
my ($arg) = shift;
return defined ( $asmcmdsys_cmds{ $arg } );
}
########
# NAME
# asmcmdsys_is_wildcard_cmd
#
# DESCRIPTION
# This routine determines if an ASMCMDSYS command allows the use
# of wild cards.
#
# PARAMETERS
# arg (IN) - user-entered command name string.
#
# RETURNS
# True if $arg is a command that can take wildcards as part of its argument,
# false otherwise.
########
sub asmcmdsys_is_wildcard_cmd
{
my ($arg) = shift;
my (%cmdhash); # Empty hash; no ASMCMDSYS command supports wildcards. #
return (asmcmdshare_get_cmd_wildcard($arg) eq "true");
}
########
# NAME
# asmcmdsys_is_no_instance_cmd
#
# DESCRIPTION
# This routine determines if a command can run without an ASM instance.
#
# PARAMETERS
# arg (IN) - user-entered command name string.
#
# RETURNS
# 1 if $arg is a command that can run without an ASM instance or it does not
# belong to this module
# 0 if $arg is a command that needs to connect to an ASM instance
# -1 if $arg is a command that may use an ASM instance.
#
# NOTES
# The asmcmdsys module currently supports no command that can run
# without an ASM instance.
########
sub asmcmdsys_is_no_instance_cmd
{
my ($arg) = shift;
my ($rc);
return 1 unless defined($asmcmdsys_cmds{$arg});
$rc = asmcmdshare_get_cmd_noinst($arg);
if ($rc eq "true")
{
return 1;
}
elsif ($rc eq "undef")
{
return -1;
}
return 0;
}
########
# NAME
# asmcmdsys_parse_int_args
#
# DESCRIPTION
# This routine parses the arguments for flag options for ASMCMDSYS
# internal commands.
#
# PARAMETERS
# cmd (IN) - user-entered command name string.
# args_ref (OUT) - hash of user-specified flag options for a command,
# populated by getopts().
#
# RETURNS
# Zero on success; undefined on error.
#
# NOTES
# $cmd must already be verified as a valid ASMCMDSYS internal command.
########
sub asmcmdsys_parse_int_args
{
my ($cmd, $args_ref) = @_;
my ($key);
my (@string);
# Use Gsmcmdparser_parse_issued_command() from the asmcmdparser package to parse arguments for
# internal commands. These arguments are stored in @ARGV.
if(!asmcmdparser_parse_issued_command($cmd, $args_ref, \@string))
{
# Print correct command format if syntax error. #
asmcmdsys_syntax_error($cmd);
return undef;
}
return 0;
}
########
# NAME
# asmcmdsys_syntax_error
#
# DESCRIPTION
# This function prints the correct syntax for a command to STDERR, used
# when there is a syntax error. This function is responsible for
# only ASMCMDSYS commands.
#
# PARAMETERS
# cmd (IN) - user-entered command name string.
#
# RETURNS
# 1 if the command belongs to this module; 0 if command not found.
#
# NOTES
# These errors are user-errors and not internal errors. They are of type
# record, not signal.
#
# N.B. Functions in this module can call this function directly, without
# calling the asmcmdshare::asmcmdshare_syntax_error equivalent. The
# latter is used only by the asmcmdcore module.
########
sub asmcmdsys_syntax_error
{
my ($cmd) = shift;
my ($cmd_syntax); # Correct syntax for $cmd. #
my ($succ) = 0;
#display syntax only if the command belongs to this module
if (asmcmdsys_is_cmd($cmd))
{
$cmd_syntax = asmcmdshare_get_help_syntax($cmd); # Get syntax for $cmd. #
$cmd_syntax = asmcmdshare_trim_str ($cmd_syntax); # Trim blank spaces #
if (defined ($cmd_syntax))
{
asmcmdshare_printstderr 'usage: ' . $cmd_syntax . "\n";
asmcmdshare_printstderr 'help: help ' . $cmd . "\n";
$succ = 1;
}
}
return $succ;
}
########
# NAME
# asmcmdsys_get_asmcmd_cmds
#
# DESCRIPTION
# This routine constructs a string that contains a list of the names of all
# ASMCMD internal commands and returns this string.
#
# PARAMETERS
# None.
#
# RETURNS
# A string contain a list of the names of all ASMCMD internal commands.
#
# NOTES
# Used by the help command and by the error command when the user enters
# an invalid internal command.
#
# IMPORTANT: the commands names must be preceded by eight (8) spaces of
# indention! This formatting is mandatory.
########
sub asmcmdsys_get_asmcmd_cmds
{
return asmcmdshare_filter_invisible_cmds(%asmcmdsys_cmds);
}
########
# NAME
# asmcmdsys_is_dsc_supported
#
# DESCRIPTION
# This function determines whether it is being run on an operating system
# on which Domain Services Clusters (DSCs) are supported.
#
# PARAMETERS
# None
#
# RETURNS
# 1 if the operating system supports DSCs, 0 otherwise
#
# NOTES
# As of 12.2, DSCs are not supported on Windows.
#
# 22817301: DSCs aren't supported on HPUX either for 12.2.0.1.
#
# $$$ This function needs to eventually check whether it is being run in an
# environment that has ASM/GIMR/TFA (mandatory DSC components) configured.
# But the APIs needed to do this don't yet exist.
########
sub asmcmdsys_is_dsc_supported
{
my $supported = 0;
if (($^O =~ /linux/i) || ($^O =~ /solaris/i) || ($^O =~ /aix/i))
{
$supported = 1;
}
return $supported;
}
########
# NAME
# asmcmdsys_is_ios_supported
#
# DESCRIPTION
# This function determines whether it is being run on an operating system
# on which IOServer is supported.
#
# PARAMETERS
# None
#
# RETURNS
# 1 if the operating system supports IOServer, 0 otherwise
#
# NOTES
# As of 12.2.0.1, IOServer is not supported on AIX. It follows that IOServer
# is also not supported on the platforms on which DSC is not supported
# (Windows and HPUX).
########
sub asmcmdsys_is_ios_supported
{
my $supported = 0;
if (($^O =~ /linux/i) || ($^O =~ /solaris/i))
{
$supported = 1;
}
return $supported;
}
########
# NAME
# asmcmdsys_get_cluster_class
#
# DESCRIPTION
# This function retrieves the cluster class by calling
# 'crsctl get cluster class'
#
# PARAMETERS
# None
#
# RETURNS
# The cluster class on success; undefined on error
#
# NOTES
########
sub asmcmdsys_get_cluster_class
{
my $class;
my $crsctlop = "get cluster class";
# directories in which the utilties are found
my @patharr = ("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
asmcmdshare_trace(3, "Getting cluster class", 'y', 'n');
my @result = asmcmdshare_execute_tool("crsctl", ".bat", $crsctlop,
\@patharr);
my $resultstr = join("", @result);
asmcmdshare_trace(3, $resultstr, 'y', 'n');
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
asmcmdshare_trace(1, $resultstr, 'y', 'y');
}
else
{
if (scalar(grep(/Standalone/, @result)) > 0)
{
$class = CLUSTER_CLASS_STANDALONE;
}
elsif (scalar(grep(/Domain/, @result)) > 0)
{
$class = CLUSTER_CLASS_DOMAINSERVICES;
}
elsif (scalar(grep(/Member/, @result)) > 0)
{
$class = CLUSTER_CLASS_MEMBER;
}
}
# Will be undef if the crsctl call failed
return $class;
}
########
# NAME
# asmcmdsys_is_gi_user
#
# DESCRIPTION
# This function determines whether the user issuing the command is the Grid
# Infrastructure (GI) user.
#
# PARAMETERS
# None
#
# RETURNS
# 1 if the user is the GI user, 0 otherwise
#
# NOTES
# Previously, we checked the owner of 'olsnodes' binary to retrieve the
# GI user.
# # path to the olsnodes binary
# my ($olsnodesbin) = "$ENV{'ORACLE_HOME'}/bin/olsnodes";
# my ($giuser) = getpwuid((stat($olsnodesbin))[4]);
#
# We check the olsnodes binary because it exists only in a GI home. This is
# how we can be sure that $ORACLE_HOME is a GI home and not a DB home.
#
########
sub asmcmdsys_is_gi_user
{
my $isgiuser = 0;
my ($rc, $giuser, $gigroup);
my $curruser = getpwuid($<);
undef &ASMCMDGetGIUser;
my $cdet = DynaLoader::dl_find_symbol($asmcmdglobal_hash{'asmperl'},
"XS_ASMCMDCLNT_GetGIUser");
DynaLoader::dl_install_xsub("ASMCMDGetGIUser", $cdet);
# Get GI user and group from ASMCMDGetGIUser using PerlToC API
($rc, $giuser, $gigroup) = ASMCMDGetGIUser();
# In case of error, record the $rc, and return as not a GI user
if ($rc != 0)
{
asmcmdshare_trace (1, "Error $rc in ASMCMDGetGIUser().\n", 'y', 'n');
return $isgiuser;
}
asmcmdshare_trace(3, "GI user $giuser and GI group $gigroup\n", 'y', 'n');
if ($curruser eq $giuser)
{
$isgiuser = 1;
}
return $isgiuser;
}
########
# NAME
# asmcmdsys_lsccfile_start
#
# DESCRIPTION
# This function processes the start of XML tags in lscc --file.
#
# PARAMETERS
# expat (IN) - expat parser object
# element (IN) - tag element name
# attrs (IN) - tag element attributes
#
# RETURNS
# NULL
#
# NOTES
# Only asmcmdsys_list_cmf_comps calls this function.
########
sub asmcmdsys_lsccfile_start
{
my ($expat, $element, %attrs) = @_;
my $attrkey;
my $attrval;
# the tags we're interested in look like
# <clsxwrap:clsxSecNode id="ASM">
if ($element eq "clsxwrap:clsxSecNode")
{
for $attrkey (keys %attrs)
{
# some component names show up as lowercase in the Cluster Manifest File,
# so uppercase everything to be consistent
$attrval = uc($attrs{$attrkey});
if (($attrkey eq "id") && defined $lsccfile_comps{$attrval})
{
$lsccfile_comps{$attrval} = "YES";
}
}
}
# we are also interested in the payload of the ASM_attributes section in
# order to find out the storage access
elsif ($element eq "clsxwrap:clsxWrapPayload")
{
for $attrkey (keys %attrs)
{
if ($attrkey eq "ASM_ACCESS_MODE")
{
$attrval = $attrs{$attrkey};
$attrval = ($attrval eq "1") ? "Indirect" : "Direct";
$lsccfile_comps{"STORAGE ACCESS"} = $attrval;
}
}
}
}
########
# NAME
# asmcmdsys_list_cmf_comps
#
# DESCRIPTION
# Parses a Cluster Manifest File and displays the components it contains
#
# PARAMETERS
# $cmf (IN) - path to Cluster Manifest File
# $printhdr (IN) - flag indicating to print header
#
# RETURNS
# NULL
#
# NOTES
#
########
sub asmcmdsys_list_cmf_comps
{
my ($cmf, $printhdr) = @_;
my $rc;
my $handlers;
my $parser;
my @eargs;
my $cmffilehandle;
my $output = "";
my $iscmf = 0;
# valid components in a Cluster Manifest File
my $asmname = "ASM"; # ASM component name #
my $gimrname = "GIMR"; # GIMR component name #
my $tfaname = "TFA"; # TFA component name #
my $acfsname = "ACFS"; # ACFS component name #
my $rhpname = "RHP"; # RHP component name #
my $gnsname = "GNS"; # GNS component name #
# storage access
my $storageaccess = "STORAGE ACCESS"; # ASM access mode (direct/indirect) #
# format strings, header, and trailer
my $fmtstr = "%3s %4s %3s %4s %3s %3s %14s";
my $header = '-'x80 ."\n" .
sprintf($fmtstr, $asmname, $gimrname, $tfaname, $acfsname,
$rhpname, $gnsname, $storageaccess) . "\n" .
'='x80 . "\n";
my $trailer = '='x80 . "\n";
# initialize to all NO (and storage access to "unknown")
%lsccfile_comps = (
$asmname => "NO",
$gimrname => "NO",
$tfaname => "NO",
$acfsname => "NO",
$rhpname => "NO",
$gnsname => "NO",
$storageaccess => "-",
);
# bail out if the user supplied a nonexistent file or a file on which there
# are no read permissions
$rc = asmcmdshare_xml_exists($cmf);
return unless defined ($rc);
# check that the file at least vaguely resembles a Cluster Manifest File by
# seeing if it contains the text "clsxwrap:clsxWrap"
open($cmffilehandle, "< $cmf");
my @lines = <$cmffilehandle>;
close $cmffilehandle;
foreach (@lines)
{
if ($_ =~ m/clsxwrap:clsxWrap/)
{
$iscmf = 1;
}
}
if (!$iscmf)
{
@eargs = ($cmf);
# ASMCMD-9497: "File '%s' is not a Cluster Manifest File."
asmcmdshare_error_msg(9497, \@eargs);
return;
}
# $$$ Perl-C call to the cred API to determine if the XML the user supplied
# is a valid credentials file
# specify the handler callback and instantiate the parser: the information we
# look for in the XML file is specifically located in the start tags and the
# rest of the information may be ignored
$handlers = {Start => \&asmcmdsys_lsccfile_start};
$parser = XML::Parser->new(Handlers => $handlers,
ErrorContext => 5);
# parse!
eval
{
$parser->parsefile($cmf);
};
# bail out if there were any parsing errors
if (asmcmdxmlexceptions::catch())
{
my (@msg, $err);
$err = asmcmdxmlexceptions::getErrmsg();
@msg = split(/\n/, $err);
if ($msg[$#msg] =~ m/Parser\.pm/)
{
pop @msg;
}
$err = join("\n", @msg) ."\n";
@eargs = ($err);
asmcmdshare_error_msg(9395, \@eargs);
return;
}
# see if there are any components configured and add info to the "no header"
# output string if there are
if ($lsccfile_comps{$asmname} eq "YES")
{
$output .= "$asmname ($lsccfile_comps{$storageaccess} Storage Access),";
}
if ($lsccfile_comps{$gimrname} eq "YES")
{
$output .= "$gimrname,";
}
if ($lsccfile_comps{$tfaname} eq "YES")
{
$output .= "$tfaname,";
}
if ($lsccfile_comps{$acfsname} eq "YES")
{
$output .= "$acfsname,";
}
if ($lsccfile_comps{$rhpname} eq "YES")
{
$output .= "$rhpname,";
}
if ($lsccfile_comps{$gnsname} eq "YES")
{
$output .= "$gnsname,";
}
# remove the trailing comma
$output =~ s/,$//;
# error out if Cluster Manifest File doesn't contain any components
if (!$output)
{
@eargs = ($cmf);
# ASMCMD-9496: "The Cluster Manifest File '%s' does not contain any
# components."
asmcmdshare_error_msg(9496, \@eargs);
return;
}
# output the result
if ($printhdr)
{
asmcmdshare_print($header);
asmcmdshare_print(sprintf($fmtstr, $lsccfile_comps{$asmname},
$lsccfile_comps{$gimrname}, $lsccfile_comps{$tfaname},
$lsccfile_comps{$acfsname}, $lsccfile_comps{$rhpname},
$lsccfile_comps{$gnsname},
$lsccfile_comps{$storageaccess}) . "\n");
asmcmdshare_print($trailer);
}
else
{
asmcmdshare_print("$output\n");
}
}
########
# NAME
# asmcmdsys_process_mkcc
#
# DESCRIPTION
# To create a client cluster for ASM, GIMR, TFA, ACFS, and RHP
#
# PARAMETERS
# $dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# NULL
#
# NOTES
# Requires SYSASM and GI user privileges to execute this operation
########
sub asmcmdsys_process_mkcc
{
my $dbh = shift; # get db handle #
my %args; # arguments passed with the command #
my $ret; # asmcmdbase_parse_int_args() return value #
my $sth;
# common to all components
my $clustername; # client cluster name #
my $wrap; # credential file name #
my $version = ''; # client cluster version #
my $guid = ''; # client cluster guid #
# directories in which the utilties are found
my @patharr = ("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
my $clusterclass; # cluster class #
my $clusterstate; # cluster state #
# components to configure
my $asmname = "asm"; # ASM component name #
my $gimrname = "gimr"; # GIMR component name #
my $tfaname = "tfa"; # TFA component name #
my $acfsname = "acfs"; # ACFS component name #
my $rhpname = "rhp"; # RHP component name #
my %components = ( # hash indicating which components to configure #
$asmname => "true",
$gimrname => "true",
$tfaname => "true",
$acfsname => "true",
$rhpname => "true",
);
# tfactl and acfsutil error output by default goes to stderr; redirect it to
# stdout in order to look for specific error messages
my $redirect = 1;
# ASM-specific
my $directacc; # direct access allowed or not (ASM) #
my $kfodop; # arguments to kfod (ASM) #
# ORA-15365 "Client Cluster '%s' already configured"
my $asmccexists = "ORA-15365"; # CC already configured error (ASM) #
my $srvctlop; # arguments to srvctl (ASM) #
# GIMR-specific
# token seen in output of 'srvctl config mgmtdb' if run on GIMR client
my $gimrisclient = "server_cluster_name";
my $mgmtcaop; # arguments to mgmtca (GIMR) #
# MGTCA-1149 "Client cluster '%s' already exists."
my $gimrccexists = "MGTCA-1149"; # CC already configured error (GIMR) #
# TFA-specific
my $tfactlop; # arguments to tfactl (TFA) #
# TFA-00516 "This client is already registered in receiver"
my $tfaccexists = "TFA-00516"; # CC already configured error (TFA) #
# TFA-00519 "Oracle Trace File Analyzer (TFA) is not installed."
my $tfanoconfig = "TFA-00519"; # tfa not installed in an ADE view #
# ACFS-specific
my $acfsutilop; # arguments to acfsutil (ACFS) #
# prefix to all ACFS errors thrown by acfsutil
my $acfserrprefix = "ACFS-";
# ACFS-09818: "Unable to add an already existing cluster '%s'. Exporting the
# existing credentials."
my $acfsccexists = "ACFS-09818"; # CC already configured error (ACFS) #
# acfsutil is found in /sbin on unix platforms
my @acfsutildir = ("/sbin/");
# RHP-specific
my $rhpctlop; # arguments to rhpctl (RHP) #
# PRGR-128 "Site "%s" already exists."
my $rhpccexists1 = "PRGR-128"; # (old) CC already configured error (RHP) #
# PRGR-152 "Client "%s" already exists."
my $rhpccexists2 = "PRGR-152"; # (new) CC already configured error (RHP) #
# PRCR-1001 "Resource %s does not exist"
my $rhpnoconfig = "PRCR-1001"; # rhpserver not configured error #
# get option parameters
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# options to specify which components to configure (if no components are
# specified, the default is to generate credentials for all components for
# which a service is configured)
if (defined ($args{$asmname}) || defined ($args{$gimrname}) ||
defined ($args{$tfaname}) || defined ($args{$acfsname}) ||
defined ($args{$rhpname}))
{
if (!defined ($args{$asmname}))
{
$components{$asmname} = "false";
}
if (!defined ($args{$gimrname}))
{
$components{$gimrname} = "false";
}
if (!defined ($args{$rhpname}))
{
$components{$rhpname} = "false";
}
if (!defined ($args{$acfsname}))
{
$components{$acfsname} = "false";
}
if (!defined ($args{$tfaname}))
{
$components{$tfaname} = "false";
}
}
# get the cluster name and credentials file name
($clustername, $wrap) = @{$args{'mkcc'}};
# check if SYSASM privileges exist
if ($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
# ASMCMD-9488: "operations on Client Cluster require SYSASM privilege"
asmcmdshare_error_msg(9488, undef);
return;
}
# check if Domain Services Cluster is supported on this OS
if (!asmcmdsys_is_dsc_supported())
{
my @eargs = ($ASMCMDSYS_DSC_NAME);
# ASMCMD-9493: "The '%s' feature is not supported on this operating
# system."
asmcmdshare_error_msg(9493, \@eargs);
return;
}
# check if the user executing the command is the GI user
if (!asmcmdsys_is_gi_user())
{
# ASMCMD-9546: "Insufficient permission to execute the command. The command
# requires an Oracle Grid Infrastructure user."
asmcmdshare_error_msg(9546, undef);
return;
}
# check that the cluster is not in rolling upgrade or rolling patch
$clusterstate = asmcmdbase_get_cluster_state($dbh);
if (lc($clusterstate) ne "normal")
{
my @eargs = ($clustername, $clusterstate);
# ASMCMD-9478: "Create or delete member cluster '%s' failed because the
# Domain Services Cluster is in '%s' state."
asmcmdshare_error_msg(9478, \@eargs);
return;
}
# make additional checks for cluster class in production but in a development
# environment, skip these checks so that tests continue to work
if (!asmcmdshare_is_dev_env())
{
$clusterclass = asmcmdsys_get_cluster_class();
# error out if we failed to get the cluster class
if (!defined ($clusterclass))
{
return;
}
# error out if either or both of the following are true:
# 1) configuring ASM + cluster class is Member
# 2) configuring TFA or configuring GIMR + cluster class is not DSC
if (($components{$asmname} eq "true" &&
$clusterclass eq CLUSTER_CLASS_MEMBER) ||
(($components{$gimrname} eq "true" ||
$components{$tfaname} eq "true") &&
$clusterclass ne CLUSTER_CLASS_DOMAINSERVICES))
{
# ASMCMD-9498: "The command is only supported on the Domain Services
# Cluster."
asmcmdshare_error_msg(9498, undef);
return;
}
}
# 21864926: version is an argument to the CLI for multiple components
# optional parameter client cluster version
if (defined ($args{'version'}))
{
$version = ($args{'version'}); # user specified client cluster version #
# validate that the version is in a.b.c.d.e format where each of the five
# digits are between 0 and 99
if ($version !~ /^(\d{1,2}\.){4}\d{1,2}$/)
{
# ASMCMD-9499: "invalid member cluster version '%s'"
my @eargs = ($version);
asmcmdshare_error_msg(9499, \@eargs);
return;
}
# now that we have a valid version, check that it is not lower than the
# minimum member cluster version
if (asmcmdshare_version_cmp($version, $ASMCMDSYS_MIN_MC_VER) < 0)
{
# the ASM version is equivalent to the DSC version (release version);
# since we disallow this command during rolling migration, the
# release/software/active versions must all be the same if we get here
my $dsc_version = asmcmdshare_get_asm_version($dbh);
my @eargs = ($version, $dsc_version);
# ASMCMD-9500: "member cluster version '%s' is incompatible with Domain
# Services Cluster version '%s'"
asmcmdshare_error_msg(9500, \@eargs);
return;
}
}
if (defined ($args{'guid'}))
{
$guid = ($args{'guid'}); # user specified client cluster guid #
# validate that the GUID is a 32-digit hexadecimal string
if ($guid !~ /^[0-9a-fA-F]{32}$/)
{
# ASMCMD-9479: "invalid GUID '%s' specified"
my @eargs = ($guid);
asmcmdshare_error_msg(9479, \@eargs);
return;
}
}
# 22375430: check if the directory in the path to the credentials file
# exists; this doesn't need to work on Windows because we would have already
# errored out above on Windows
my @splitwrapdir = split(/\//, $wrap, -1);
pop(@splitwrapdir);
my $wrapdir = join('/', @splitwrapdir);
# if no directory was specified, then check the current working directory
if (!$wrapdir)
{
$wrapdir = getcwd();
}
if (! -d $wrapdir)
{
my @eargs = ($wrapdir);
# ASMCMD-9495: "Directory '%s' does not exist."
asmcmdshare_error_msg(9495, \@eargs);
return;
}
# 20675462: error out if we don't have write permissions on the above
# directory; also error out if the credentials file already exists, and we
# don't have write permissions on it
if (! -w $wrapdir || (-e $wrap && (! -w $wrap)))
{
# ASMCMD-9463: "operation failed due to lack of write permissions"
asmcmdshare_error_msg(9463, undef);
return;
}
# ASM
if ($components{$asmname} eq "true")
{
asmcmdshare_trace(3, "Configuring ASM Client Cluster", 'y', 'n');
$directacc = 0; # assume indirect access #
# optional parameter direct (or indirect)
if (defined ($args{'direct'}))
{
$directacc = 1; # if specified direct access or indirect #
}
# Error out if the user wants indirect access and we are on an OS that
# doesn't support IOServer
if (!$directacc && !asmcmdsys_is_ios_supported())
{
my @eargs = ($ASMCMDSYS_IOS_NAME);
# ASMCMD-9493: "The '%s' feature is not supported on this operating
# system."
asmcmdshare_error_msg(9493, \@eargs);
return;
}
# query to create client cluster
if (defined ($args{'version'}) && defined ($args{'guid'}))
{
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.createclientcluster2 (:clname, :direct_access, :clver,
:clguid);
end;
});
# bind input parameters
$sth->bind_param(":clname", $clustername);
$sth->bind_param(":direct_access", $directacc);
$sth->bind_param(":clver", $version);
$sth->bind_param(":clguid", $guid);
}
elsif (defined ($args{'version'}))
{
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.createclientcluster2 (:clname, :direct_access, :clver,
'');
end;
});
# bind input parameters
$sth->bind_param(":clname", $clustername);
$sth->bind_param(":direct_access", $directacc);
$sth->bind_param(":clver", $version);
}
elsif (defined ($args{'guid'}))
{
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.createclientcluster2 (:clname, :direct_access, '',
:clguid);
end;
});
# bind input parameters
$sth->bind_param(":clname", $clustername);
$sth->bind_param(":direct_access", $directacc);
$sth->bind_param(":clguid", $guid);
}
else
{
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.createclientcluster2 (:clname, :direct_access, '',
'');
end;
});
# bind input parameters
$sth->bind_param(":clname", $clustername);
$sth->bind_param(":direct_access", $directacc);
}
$ret = $sth->execute();
if (!defined($ret))
{
# if we get an error saying the client cluster is already configured, we
# want to continue and export the credentials
if ($DBI::errstr =~ m,$asmccexists,)
{
# 21982652: don't let extraneous error messages go to stderr
my @spliterr = split("\n", $DBI::errstr);
my $row;
foreach $row (@spliterr)
{
if ($row =~ m,$asmccexists,)
{
# stderr and trace file
asmcmdshare_trace(1, $row, 'y', 'y');
}
else
{
# only trace file
asmcmdshare_trace(1, $row, 'y', 'n');
}
}
}
# otherwise fail
else
{
my @eargs = ("ASM", $clustername);
# ASMCMD-9494: "failed to create component '%s' credentials for Member
# Cluster '%s'"
asmcmdshare_error_msg(9494, \@eargs);
@eargs = ($clustername);
# ASMCMD-9476: "failed to create the Cluster Manifest File for member
# cluster '%s'"
asmcmdshare_error_msg(9476, \@eargs);
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
$kfodop = "op=credexport client_cluster=$clustername wrap=$wrap nohdr=TRUE";
# Environment variable 'ORACLE_HOME' will be set, otherwise
# ASMCMD init code will bail out.
my @result = asmcmdshare_execute_tool("kfod", ".exe", $kfodop, \@patharr);
# check if kfod succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
my @eargs = ("ASM", $clustername);
# ASMCMD-9494: "failed to create component '%s' credentials for Member
# Cluster '%s'"
asmcmdshare_error_msg(9494, \@eargs);
@eargs = ($clustername);
# ASMCMD-9476: "failed to create the Cluster Manifest File for member
# cluster '%s'"
asmcmdshare_error_msg(9476, \@eargs);
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Start IOS if indirect access
if ($directacc == 0)
{
$srvctlop = "start ioserver";
my @result = asmcmdshare_execute_tool("srvctl", ".bat", $srvctlop,
\@patharr);
# check if srvctl succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# ignore error. write only to trace file
asmcmdshare_trace(1, join("", @result), 'y', 'n');
}
}
}
# GIMR
# $$$ if --gimrdir is specified, pass its value to the appropriate mgmtca
# command.
if ($components{$gimrname} eq "true")
{
# check if mgmtdb server is configured
my @result = asmcmdshare_execute_tool("srvctl", ".bat",
"config mgmtdb -inner 1", \@patharr);
my $resultstr = join("", @result);
asmcmdshare_trace(3, $resultstr, 'y', 'n');
# $$$ This check can be removed once is_dsc_supported works properly.
# srvctl will also exit with return code 0 if executed on an mgmtdb client;
# also skip this step if we are on a mgmtdb client
if ($asmcmdglobal_hash{'utilsucc'} eq 'true' &&
$resultstr !~ m,$gimrisclient,)
{
asmcmdshare_trace(3, "Configuring GIMR Client Cluster", 'y', 'n');
$mgmtcaop = "createRepos -wrap $wrap -client $clustername";
# 24760407: support the use case where MC version != DSC version for GIMR
if (defined ($args{'version'}))
{
$mgmtcaop .= " -version $version";
}
my @result = asmcmdshare_execute_tool("mgmtca", ".bat", $mgmtcaop,
\@patharr);
# if we get an error saying the client cluster is already configured, we
# want to export the credentials because mgmtca internally doesn't do so
if (join("", @result) =~ m,$gimrccexists,)
{
# 21982652: don't let Cause/Action go to stderr
my $row;
foreach $row (@result)
{
$row =~ s/\s+$//;
if ($row =~ m,$gimrccexists,)
{
# stderr and trace file
asmcmdshare_trace(1, $row, 'y', 'y');
}
else
{
# only trace file
asmcmdshare_trace(1, $row, 'y', 'n');
}
}
$mgmtcaop = "configRepos exportCred -wrap $wrap -client $clustername";
@result = asmcmdshare_execute_tool("mgmtca", ".bat", $mgmtcaop,
\@patharr);
}
# check if mgmtca succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
my @eargs = ("GIMR", $clustername);
# ASMCMD-9494: "failed to create component '%s' credentials for Member
# Cluster '%s'"
asmcmdshare_error_msg(9494, \@eargs);
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
}
# TFA
if ($components{$tfaname} eq "true")
{
# We will have already errored out above if the OS is Windows; tfa is not
# supported on Windows, and currently the tfactl executable doesn't even
# exist on Windows
#
# check if tfa is configured (i.e. running)
$tfactlop = "print status";
my @result = asmcmdshare_execute_tool("tfactl", "", $tfactlop, \@patharr,
$redirect);
my $resultstr = join("", @result);
asmcmdshare_trace(3, $resultstr, 'y', 'n');
# skip this step only if ALL of the following are true:
# (1) tfa is not installed
# (2) we're in a development environment (ADE view)
# (3) the user didn't specify the --tfa flag
if (!($resultstr =~ m,$tfanoconfig, && asmcmdshare_is_dev_env() &&
!defined $args{$tfaname}))
{
asmcmdshare_trace(3, "Configuring TFA Client Cluster", 'y', 'n');
$tfactlop = "client add $clustername -export $wrap";
# optional version parameter
if (defined($args{'version'}))
{
$tfactlop .= " -version $version";
}
@result = asmcmdshare_execute_tool("tfactl", "", $tfactlop, \@patharr,
$redirect);
# if we get an error saying the client cluster is already configured, we
# want to continue; tfactl internally already re-exports the credentials
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
if (join("", @result) =~ m,$tfaccexists,)
{
# Suppress extraneous outputs
my $row;
foreach $row (@result)
{
$row =~ s/\s+$//;
if ($row =~ m,$tfaccexists,)
{
# stderr and trace file
asmcmdshare_trace(1, $row, 'y', 'y');
}
else
{
# only trace file
asmcmdshare_trace(1, $row, 'y', 'n');
}
}
}
# otherwise fail
else
{
my @eargs = ("TFA", $clustername);
# ASMCMD-9494: "failed to create component '%s' credentials for
# Member Cluster '%s'"
asmcmdshare_error_msg(9494, \@eargs);
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
}
}
# ACFS
if ($components{$acfsname} eq "true")
{
# check if ACFS is loaded
my @result = asmcmdshare_execute_tool("acfsdriverstate", ".bat", "loaded",
\@patharr);
asmcmdshare_trace(3, join("", @result), 'y', 'n');
if ($asmcmdglobal_hash{'utilsucc'} eq 'true')
{
asmcmdshare_trace(3, "Configuring ACFS Client Cluster", 'y', 'n');
$acfsutilop = "cluster credential -g $clustername -o $wrap";
# optional version parameter
if (defined($args{'version'}))
{
$acfsutilop .= " -c $version";
}
@result = asmcmdshare_execute_tool("acfsutil", ".exe", $acfsutilop,
\@acfsutildir, $redirect);
my $resultstr = join("", @result);
# Trim the command name prefix that acfsutil outputs
$resultstr =~ s/.*$acfserrprefix/$acfserrprefix/g;
@result = split("\n", $resultstr);
# if we get an error saying the client cluster is already configured, we
# want to continue; acfsutil internally already re-exports the
# credentials
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
if (join("", @result) =~ m,$acfsccexists,)
{
# Suppress extraneous outputs
my $row;
foreach $row (@result)
{
$row =~ s/\s+$//;
if ($row =~ m,$acfsccexists,)
{
# stderr and trace file
asmcmdshare_trace(1, $row, 'y', 'y');
}
else
{
# only trace file
asmcmdshare_trace(1, $row, 'y', 'n');
}
}
}
# otherwise fail
else
{
my @eargs = ("ACFS", $clustername);
# ASMCMD-9494: "failed to create component '%s' credentials for Member
# Cluster '%s'"
asmcmdshare_error_msg(9494, \@eargs);
asmcmdshare_trace(1, $resultstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
}
}
# RHP
if ($components{$rhpname} eq "true")
{
# check if rhpserver is configured, since it isn't supported on some ports
my @result = asmcmdshare_execute_tool("srvctl", ".bat", "config rhpserver",
\@patharr);
# we're done if rhpserver isn't configured
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# suppress the error message if the user didn't specify the --rhp flag
# and the error tells that "rhpserver is not configured"
if (!defined $args{$rhpname} && join("", @result) =~ m,$rhpnoconfig,)
{
asmcmdshare_trace(3, join("", @result), 'y', 'n');
}
# error out
else
{
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1 ;
}
return;
}
asmcmdshare_trace(3, "Configuring RHP Client Cluster", 'y', 'n');
$rhpctlop = "add client -client $clustername -internalcred $wrap";
# optional version parameter
if (defined($args{'version'}))
{
$rhpctlop .= " -version $version";
}
# on Windows, rhpctl doesn't exist because rhpserver isn't supported
@result = asmcmdshare_execute_tool("rhpctl", "", $rhpctlop,
\@patharr);
# if we get an error saying the client cluster is already configured, we
# want to export the credentials because rhpctl internally won't do so in
# this case
if (join("", @result) =~ m,$rhpccexists1, ||
join("", @result) =~ m,$rhpccexists2,)
{
# 21982652: rhpctl doesn't output extraneous messages when the rhp client
# already exists, so we don't have to do any extra work here
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$rhpctlop = "export client -client $clustername -internalcred $wrap";
@result = asmcmdshare_execute_tool("rhpctl", "", $rhpctlop,
\@patharr);
}
# check if rhpctl succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
my @eargs = ("RHP", $clustername);
# ASMCMD-9494: "failed to create component '%s' credentials for Member
# Cluster '%s'"
asmcmdshare_error_msg(9494, \@eargs);
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
# at the end of a successful mkcc, output the components in the Cluster
# Manifest File that was generated/updated
asmcmdsys_list_cmf_comps($wrap, 1);
}
########
# NAME
# asmcmdsys_process_rmcc
#
# DESCRIPTION
# To delete a client cluster for ASM, GIMR, TFA, ACFS, and RHP
#
# PARAMETERS
# NONE
#
# RETURNS
# NULL
#
# NOTES
# Requires SYSASM and GI user privileges to execute this operation
#
# Unlike mkcc, where the user can specify which components to configure, rmcc
# will just try to deconfigure all the components for which there is a server
# configured
########
sub asmcmdsys_process_rmcc
{
my $dbh = shift; # get db handle #
my %args; # arguments passed with the command #
my $ret; # asmcmdbase_parse_int_args() return value #
# common to all components
my $clustername; # client cluster name #
my $clusterclass; # cluster class #
my $clusterstate; # cluster state #
# directories in which the utilties are found
my @patharr = ("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
# ASMCMD-9490 "member cluster '%s' is not configured"
my $nothingexists = "ASMCMD-9490"; # no components configured error #
# tfactl and acfsutil error output by default goes to stderr; redirect it to
# stdout in order to look for specific error messages
my $redirect = 1;
# ASM-specific
my $kfodop; # arguments to kfod (ASM) #
# ORA-15367 "Client Cluster '%s' not configured"
my $asmccnotexists = "ORA-15367"; # CC not configured error (ASM) #
# GIMR-specific
# token seen in 'srvctl config mgmtdb' output if run on a GIMR client
my $gimrisclient = "server_cluster_name";
my $mgmtcaop; # arguments to mgmtca (GIMR) #
# MGTCA-1150 "Client cluster '%s' does not exist."
my $gimrccnotexists = "MGTCA-1150"; # CC not configured error (GIMR) #
# TFA-specific
my $tfactlop; # arguments to tfactl (TFA) #
# prefix to all TFA errors thrown by tfactl
my $tfaerrprefix = "TFA-00";
# TFA-00517 "This client does not exist"
my $tfaccnotexists = "TFA-00517"; # CC not configured error (TFA) #
# ACFS-specific
my $acfsutilop; # arguments to acfsutil (ACFS) #
# prefix to all ACFS errors thrown by acfsutil
my $acfserrprefix = "ACFS-";
# ACFS-09819: "Unable to remove the '%s' cluster. It does not exist."
my $acfsccnotexists = "ACFS-09819"; # CC not configured error (ACFS) #
# acfsutil is found in /sbin on unix platforms
my @acfsutildir = ("/sbin/");
# RHP-specific
my $rhpctlop; # arguments to rhpctl (RHP) #
# PRGR-119 "Site "%s" does not exist."
my $rhpccnotexists1 = "PRGR-119"; # (old) CC not configured error (RHP) #
# PRGR-170 "Client "%s" does not exist."
my $rhpccnotexists2 = "PRGR-170"; # (new) CC not configured error (RHP) #
# get command parameters
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# get cluster name
$clustername = shift @{$args{'rmcc'}};
# check if SYSASM privileges exist
if ($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
# ASMCMD-9488: "operations on Client Cluster require SYSASM privilege"
asmcmdshare_error_msg(9488, undef);
return;
}
# check if Domain Services Cluster is supported on this OS
if (!asmcmdsys_is_dsc_supported())
{
my @eargs = ($ASMCMDSYS_DSC_NAME);
# ASMCMD-9493: "The '%s' feature is not supported on this operating
# system."
asmcmdshare_error_msg(9493, \@eargs);
return;
}
# check if the user executing the command is the GI user
if (!asmcmdsys_is_gi_user())
{
# ASMCMD-9546: "Insufficient permission to execute the command. The command
# requires an Oracle Grid Infrastructure user."
asmcmdshare_error_msg(9546, undef);
return;
}
# check that the cluster is not in rolling upgrade or rolling patch
$clusterstate = asmcmdbase_get_cluster_state($dbh);
if (lc($clusterstate) ne "normal")
{
my @eargs = ($clustername, $clusterstate);
# ASMCMD-9478: "Create or delete member cluster '%s' failed because the
# Domain Services Cluster is in '%s' state."
asmcmdshare_error_msg(9478, \@eargs);
return;
}
# make additional checks for cluster class in production but in a development
# environment, skip these checks so that tests continue to work
if (!asmcmdshare_is_dev_env())
{
$clusterclass = asmcmdsys_get_cluster_class();
# error out if we failed to get the cluster class
if (!defined ($clusterclass))
{
return;
}
# error out if we are on a member cluster
if ($clusterclass eq CLUSTER_CLASS_MEMBER)
{
# ASMCMD-9498: "The command is only supported on the Domain Services
# Cluster."
asmcmdshare_error_msg(9498, undef);
return;
}
}
# 22956099: check if any of the components exist for the cluster we are
# attempting to remove and error out if not
my @result = asmcmdshare_execute_tool("asmcmd", ".bat", "lscc $clustername",
\@patharr, $redirect);
if (join("", @result) =~ m,$nothingexists,)
{
my @eargs = ($clustername);
# ASMCMD-9490: "member cluster '%s' is not configured"
asmcmdshare_error_msg(9490, \@eargs);
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
# ASM
asmcmdshare_trace(3, "Deconfiguring ASM Client Cluster", 'y', 'n');
# construct argument to kfod
$kfodop = "op=creddelclus client_cluster=$clustername nohdr=TRUE";
# adding optional parameters
if (defined($args{'f'}))
{
$kfodop .= " force=TRUE";
}
@result = asmcmdshare_execute_tool("kfod", ".exe", $kfodop, \@patharr);
# check if kfod succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# if we get an error saying the client cluster is already deconfigured, or
# if the force flag is set, we want to continue
if (defined($args{'f'}) || join("", @result) =~ m,$asmccnotexists,)
{
# if the error is that the client cluster is already deconfigured, just
# trace that message
if (join("", @result) =~ m,$asmccnotexists,)
{
asmcmdshare_trace(1, join("", @result), 'y', 'n');
}
# output the entire error stack for all other failures
else
{
asmcmdshare_trace(1, join("", @result), 'y', 'y');
}
}
# otherwise fail
else
{
my @eargs = ($clustername);
# ASMCMD-9477: "delete Client Cluster '%s' failed"
asmcmdshare_error_msg(9477, \@eargs);
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
}
# GIMR
# check if mgmtdb server is configured; if not, there can be no client to
# deconfigure, so skip this step
@result = asmcmdshare_execute_tool("srvctl", ".bat",
"config mgmtdb -inner 1", \@patharr);
my $resultstr = join("", @result);
asmcmdshare_trace(3, $resultstr, 'y', 'n');
# $$$ This check can be removed once is_dsc_supported works properly. srvctl
# will also exit with return code 0 if executed on an mgmtdb client; also
# skip this step if we are on a mgmtdb client
if ($asmcmdglobal_hash{'utilsucc'} eq 'true' &&
$resultstr !~ m,$gimrisclient,)
{
asmcmdshare_trace(3, "Deconfiguring GIMR Client Cluster", 'y', 'n');
$mgmtcaop = "removeRepos -client $clustername";
# optional force parameter
if (defined($args{'f'}))
{
$mgmtcaop .= " -force";
}
my @result = asmcmdshare_execute_tool("mgmtca", ".bat", $mgmtcaop,
\@patharr);
# check if mgmtca succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# if we get an error saying the client cluster is already deconfigured,
# or if the force flag is set, we want to continue
if (defined($args{'f'}) || join("", @result) =~ m,$gimrccnotexists,)
{
# if the error is that the client cluster is already deconfigured, just
# trace that message
if (join("", @result) =~ m,$gimrccnotexists,)
{
asmcmdshare_trace(1, join("", @result), 'y', 'n');
}
# output the entire error stack for all other failures
else
{
asmcmdshare_trace(1, join("", @result), 'y', 'y');
}
}
# otherwise fail
else
{
my @eargs = ($clustername);
# ASMCMD-9477: "delete Client Cluster '%s' failed"
asmcmdshare_error_msg(9477, \@eargs);
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
}
}
# TFA
# We will have already errored out above if the OS is Windows; tfa is not
# supported on Windows, and currently the tfactl executable doesn't even
# exist on Windows
#
# check if tfa is configured (i.e. running); if not, there can be no tfa
# clients, so skip this step
$tfactlop = "print status";
@result = asmcmdshare_execute_tool("tfactl", "", $tfactlop, \@patharr,
$redirect);
$resultstr = join("", @result);
asmcmdshare_trace(3, $resultstr, 'y', 'n');
if ($resultstr !~ m,$tfaerrprefix,)
{
asmcmdshare_trace(3, "Deconfiguring TFA Client Cluster", 'y', 'n');
$tfactlop = "client remove $clustername";
# optional force parameter
if (defined($args{'f'}))
{
$tfactlop .= " -f";
}
my @result = asmcmdshare_execute_tool("tfactl", "", $tfactlop, \@patharr,
$redirect);
# check if tfactl succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# if we get an error saying the client cluster is already deconfigured,
# or if the force flag is set, we want to continue
if (defined($args{'f'}) || join("", @result) =~ m,$tfaccnotexists,)
{
# if the error is that the client cluster is already deconfigured, just
# trace that message
if (join("", @result) =~ m,$tfaccnotexists,)
{
asmcmdshare_trace(1, join("", @result), 'y', 'n');
}
# output the entire error stack for all other failures
else
{
asmcmdshare_trace(1, join("", @result), 'y', 'y');
}
}
# otherwise fail
else
{
my @eargs = ($clustername);
# ASMCMD-9477: "delete Client Cluster '%s' failed"
asmcmdshare_error_msg(9477, \@eargs);
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
}
}
# check if ACFS is loaded; if not, there can be no client to deconfigure, so
# skip this step
@result = asmcmdshare_execute_tool("acfsdriverstate", ".bat", "loaded",
\@patharr);
asmcmdshare_trace(3, join("", @result), 'y', 'n');
if ($asmcmdglobal_hash{'utilsucc'} eq 'true')
{
asmcmdshare_trace(3, "Deconfiguring ACFS Client Cluster", 'y', 'n');
$acfsutilop = "cluster credential -r $clustername";
# optional force parameter
if (defined($args{'f'}))
{
$acfsutilop .= " -f";
}
my @result = asmcmdshare_execute_tool("acfsutil", ".exe", $acfsutilop,
\@acfsutildir, $redirect);
my $resultstr = join("", @result);
# Trim the command name prefix that acfsutil outputs
$resultstr =~ s/.*$acfserrprefix/$acfserrprefix/g;
# check if acfsutil succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# if we get an error saying the client cluster is already deconfigured,
# or if the force flag is set, we want to continue
if (defined($args{'f'}) || $resultstr =~ m,$acfsccnotexists,)
{
# if the error is that the client cluster is already deconfigured,
# we just want to trace that message
if (join("", @result) =~ m,$acfsccnotexists,)
{
my $row;
foreach $row (@result)
{
$row =~ s/\s+$//;
# trim the command name prefix that acfsutil outputs
$row =~ s/.*$acfserrprefix/$acfserrprefix/;
# only trace file
asmcmdshare_trace(1, $row, 'y', 'n');
}
}
# output the entire error stack for all other failures
else
{
asmcmdshare_trace(1, $resultstr, 'y', 'y');
}
}
# otherwise fail
else
{
my @eargs = ($clustername);
# ASMCMD-9477: "delete member cluster '%s' failed"
asmcmdshare_error_msg(9477, \@eargs);
asmcmdshare_trace(1, $resultstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
}
}
# RHP
# check if rhpserver is configured; if not, there can be no client to
# deconfigure, so skip this step
@result = asmcmdshare_execute_tool("srvctl", ".bat", "config rhpserver",
\@patharr);
asmcmdshare_trace(3, join("", @result), 'y', 'n');
if ($asmcmdglobal_hash{'utilsucc'} eq 'true')
{
asmcmdshare_trace(3, "Deconfiguring RHP Client Cluster", 'y', 'n');
$rhpctlop = "delete client -client $clustername";
# optional force parameter
if (defined($args{'f'}))
{
$rhpctlop .= " -force";
}
# on Windows, rhpctl doesn't exist because rhpserver isn't supported
my @result = asmcmdshare_execute_tool("rhpctl", "", $rhpctlop,
\@patharr);
# check if rhpctl succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# if we get an error saying the client cluster is already deconfigured,
# we just want to trace that message
if (join("", @result) =~ m,$rhpccnotexists1, ||
join("", @result) =~ m,$rhpccnotexists2,)
{
asmcmdshare_trace(1, join("", @result), 'y', 'n');
}
else
{
my @eargs = ($clustername);
# ASMCMD-9477: "delete Client Cluster '%s' failed"
asmcmdshare_error_msg(9477, \@eargs);
# 21982652: rhpctl doesn't output extraneous messages when the rhp
# client does not exist, so we don't have to do any extra work here
asmcmdshare_trace(1, join("", @result), 'y', 'y');
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
}
}
}
########
# NAME
# asmcmdsys_process_lscc
#
# DESCRIPTION
# To list the configured client clusters for ASM, GIMR, TFA, ACFS, and RHP
#
# PARAMETERS
# NONE
#
# RETURNS
# NULL
#
# NOTES
# Requires SYSASM and GI user privileges to execute this operation
########
sub asmcmdsys_process_lscc
{
my %args; # Arguments passed with the command #
my $ret; # asmcmdbase_parse_int_args() return value #
# common to all components
my $clustername; # client cluster name #
my $clusterclass; # cluster class #
# directories in which the utilities are found
my @patharr =("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
# hash storing the parsed results returned by the utilities
# keys are lowercased cluster names
# values are references to hashes, each of which is structured like the
# following example, where the component names will only be defined if they
# are configured
# "name" => "Cc1" (case-preserved name)
# "version" => "12.2.0.0.0"
# "guid" => "19fc25ffe567cf0abf6a9f1562f7976e"
# "asm" => "YES" (whether ASM Client Cluster is configured)
# "gimr" => "YES" (whether GIMR Client Cluster is configured)
# "tfa" => "YES" (whether TFA Client Cluster is configured)
# "rhp" => "NO" (whether RHP Client Cluster is configured)
# "acfs" => "NO" (whether ACFS Client Cluster is configured)
# "metacomp" => "ASM" (component from which we obtained the "master"
# metadata, i.e. what gets stored in this hash, used
# when reporting inconsistent configurations)
my %output = ();
# component-specific
my $asmname = "ASM"; # ASM component name #
my $gimrname = "GIMR"; # GIMR component name #
my $tfaname = "TFA"; # TFA component name #
my $acfsname = "ACFS"; # ACFS component name #
my $rhpname = "RHP"; # RHP component name #
my $kfodop; # arguments to kfod (ASM) #
my $mgmtcaop; # arguments to mgmtca (GIMR) #
my $tfactlop; # arguments to tfactl (TFA) #
my $acfsutilop; # arguments to acfsutil (ACFS) #
my $rhpctlop; # arguments to rhpctl (RHP) #
# tfactl and acfsutil error output by default goes to stderr; redirect it to
# stdout in order to look for specific error messages
my $redirect = 1;
# error codes
# ASM-specific
# KFOD-00332: "no Client Clusters configured"
my $noasmcc = "KFOD-00332";
# KFOD-00333: "Client Cluster '%s' is not configured"
my $nothisasmcc = "KFOD-00333";
# GIMR-specific
# token seen in 'srvctl config mgmtdb' output if run on a GIMR client
my $gimrisclient = "server_cluster_name";
# MGTCA-1151: "There are no clients for the GIMR Server Cluster"
my $nogimrcc = "MGTCA-1151";
# there is no mgmtca option to list only a specific client cluster
# TFA-specific
# prefix to all TFA errors thrown by tfactl
my $tfaerrprefix = "TFA-00";
# tfactl doesn't output anything (i.e. there is no error) when there are no
# client clusters configured
# TFA-00517: "This client does not exist"
my $nothistfacc = "TFA-00517";
# ACFS-specific
# prefix to all ACFS errors thrown by acfsutil
my $acfserrprefix = "ACFS-";
# ACFS-09809: "No Member Clusters have been registered."
my $noacfscc = "ACFS-09809";
# ACFS-09810: "No credentials were found for the '%s' cluster name."
my $nothisacfscc = "ACFS-09810";
# acfsutil is found in /sbin on unix platforms
my @acfsutildir = ("/sbin/");
# RHP-specific
# PRGR-119: "Site "%s" does not exist."
my $nothisrhpcc = "PRGR-119";
# format strings, headers, and trailer
# the output format generally matches that of kfod
my $hdrwidth = 100;
my $fmtstr = "%15s %14s %-32s";
my $longfmtstr = "%15s %14s %-32s %3s %4s %3s %4s %3s %14s";
my $header = '-'x$hdrwidth . "\n" .
sprintf($fmtstr, "NAME", "VERSION", "GUID") . "\n" .
'='x$hdrwidth . "\n";
my $longheader = '-'x$hdrwidth . "\n" .
sprintf($longfmtstr, "NAME", "VERSION", "GUID", $asmname,
$gimrname, $tfaname, $acfsname, $rhpname,
"STORAGE ACCESS") . "\n" .
'='x$hdrwidth . "\n";
my $trailer = '='x$hdrwidth . "\n";
my $configtrc = "%s: version=%s guid=%s; %s: version=%s guid=%s";
# versions normalized to 4 digits, e.g. 12.2.0.0
# 22780177: we normalize versions to 4 digits before comparing them for
# mismatches because:
# -> rhpctl masks the fifth digit of the version number in their output
# -> in development, the fifth digit of the version number may not be the
# same across labels at some given point in time
my $compvsnnorm; # version of component currently being listed
my $metavsnnorm; # version of "metacomp"
# get option parameters
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# --file option: it needs to work on a member cluster / without an ASM
# instance
if (defined($args{'file'}))
{
asmcmdsys_list_cmf_comps($args{'file'}, !defined($args{'suppressheader'}));
return;
}
# check if SYSASM privileges exist
if ($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
# ASMCMD-9488: "operations on Client Cluster require SYSASM privilege"
asmcmdshare_error_msg(9488, undef);
return;
}
# check if Domain Services Cluster is supported on this OS
if (!asmcmdsys_is_dsc_supported())
{
my @eargs = ($ASMCMDSYS_DSC_NAME);
# ASMCMD-9493: "The '%s' feature is not supported on this operating
# system."
asmcmdshare_error_msg(9493, \@eargs);
return;
}
# check if the user executing the command is the GI user
if (!asmcmdsys_is_gi_user())
{
# ASMCMD-9546: "Insufficient permission to execute the command. The command
# requires an Oracle Grid Infrastructure user."
asmcmdshare_error_msg(9546, undef);
return;
}
# make additional checks for cluster class in production but in a development
# environment, skip these checks so that tests continue to work
if (!asmcmdshare_is_dev_env())
{
$clusterclass = asmcmdsys_get_cluster_class();
# error out if we failed to get the cluster class
if (!defined ($clusterclass))
{
return;
}
# error out if we are on a member cluster
if ($clusterclass eq CLUSTER_CLASS_MEMBER)
{
# ASMCMD-9498: "The command is only supported on the Domain Services
# Cluster."
asmcmdshare_error_msg(9498, undef);
return;
}
}
# get cluster name
$clustername = shift @{$args{'lscc'}};
# ASM
asmcmdshare_trace(3, "Querying for ASM Client Clusters", 'y', 'n');
$kfodop = "op=lscc nohdr=TRUE";
if (defined ($clustername))
{
$kfodop .= " client_cluster=$clustername";
}
# Environment variable 'ORACLE_HOME' will be set, otherwise
# ASMCMD init code will bail out.
my @result = asmcmdshare_execute_tool ("kfod", ".exe", $kfodop, \@patharr);
my $resultstr = join("", @result);
# check if kfod succeeded
# continue if the reason for "failure" is trying to list a client cluster
# that isn't configured
if ($asmcmdglobal_hash{'utilsucc'} ne 'true' &&
$resultstr !~ m,$nothisasmcc,)
{
# ASMCMD-9491: "failed to list the configured Client Clusters"
asmcmdshare_error_msg(9491, undef);
asmcmdshare_trace(1, $resultstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return 1;
}
# parse and store the kfod output, if there are (relevant) ASM client
# clusters configured
if ($resultstr !~ m,$noasmcc, && $resultstr !~ m,$nothisasmcc,)
{
my $row;
foreach $row (@result)
{
my @splitrow = split(" ", $row);
my %rowinfo = ();
# normalize the case of the key, but preserve the case of the name
my $key = lc($splitrow[0]);
$rowinfo{"name"} = $splitrow[0];
$rowinfo{"version"} = $splitrow[1];
$rowinfo{"guid"} = $splitrow[2];
$rowinfo{"accmode"} = $splitrow[3];
$rowinfo{"asm"} = "YES";
$rowinfo{"gimr"} = "NO";
$rowinfo{"rhp"} = "NO";
$rowinfo{"acfs"} = "NO";
$rowinfo{"tfa"} = "NO";
$rowinfo{"metacomp"} = $asmname;
$output{$key} = \%rowinfo;
}
}
# GIMR
# check if mgmtdb is configured; if not, there can be no mgmtdb clients, so
# skip this step
@result = asmcmdshare_execute_tool("srvctl", ".bat",
"config mgmtdb -inner 1", \@patharr);
$resultstr = join("", @result);
asmcmdshare_trace(3, $resultstr, 'y', 'n');
# $$$ This check can be removed once is_dsc_supported works properly. srvctl
# will also exit with return code 0 if executed on an mgmtdb client; also
# skip this step if we are on a mgmtdb client
if ($asmcmdglobal_hash{'utilsucc'} eq 'true' &&
$resultstr !~ m,$gimrisclient,)
{
asmcmdshare_trace(3, "Querying for GIMR Client Clusters", 'y', 'n');
# $$$ mgmtca queryRepos is changing their output to JSON format.
# Temporarily pass the -legacy flag to continue getting the current output
# so this doesn't break. We will switch to parsing their new output when it
# is available.
$mgmtcaop = "queryRepos -legacy";
@result = asmcmdshare_execute_tool ("mgmtca", ".bat", $mgmtcaop,
\@patharr);
$resultstr = join("", @result);
# check if mgmtca succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# ASMCMD-9491: "failed to list the configured Client Clusters"
asmcmdshare_error_msg(9491, undef);
asmcmdshare_trace(1, $resultstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return 1;
}
# parse and store the mgmtca output, if there are GIMR client clusters
# configured
if ($resultstr !~ m,$nogimrcc,)
{
# remove the header, i.e. the first two rows that say
# The client clusters for the Server Cluster <server cluster name> are:
# CLUSTER NAME VERSION GUID
@result = @result[2..$#result];
my $row;
foreach $row (@result)
{
my (@splitrow) = split(" ", $row);
# normalize the case of the key, but preserve the case of the name
my $key = lc($splitrow[0]);
# if client cluster name is specified, only extract the row for this
# particular cluster
next if (defined ($clustername) && $key ne lc($clustername));
# parse the row
my %rowinfo = ();
$rowinfo{"name"} = $splitrow[0];
$rowinfo{"version"} = $splitrow[1];
$rowinfo{"guid"} = $splitrow[2];
# if we already have some info for this client cluster
if (defined ($output{$key}))
{
# get the four-digit version numbers to compare
$compvsnnorm = $rowinfo{"version"};
$compvsnnorm =~ s/\.\d+$//;
$metavsnnorm = $output{$key}{"version"};
$metavsnnorm =~ s/\.\d+$//;
# error out if the version doesn't match
if (($compvsnnorm ne $metavsnnorm) ||
($rowinfo{"guid"} ne $output{$key}{"guid"}))
{
asmcmdshare_trace(3, sprintf($configtrc, $gimrname,
$rowinfo{"version"},
$rowinfo{"guid"},
$output{$key}{"metacomp"},
$output{$key}{"version"},
$output{$key}{"guid"}),
'y', 'n');
my @eargs = ($rowinfo{"name"}, $output{$key}{"metacomp"},
$gimrname);
# ASMCMD-9492: "The configuration for Client Cluster '%s' is
# inconsistent between components '%s' and '%s'."
asmcmdshare_error_msg(9492, \@eargs);
return 1;
}
# just indicate that GIMR is configured
$output{$key}{"gimr"} = "YES";
}
# otherwise add a new entry
else
{
$rowinfo{"asm"} = "NO";
$rowinfo{"accmode"} = "-";
$rowinfo{"gimr"} = "YES";
$rowinfo{"tfa"} = "NO";
$rowinfo{"acfs"} = "NO";
$rowinfo{"rhp"} = "NO";
$rowinfo{"metacomp"} = $gimrname;
$output{$key} = \%rowinfo;
}
}
}
}
# TFA
# We will have already errored out above if the OS is Windows; tfa is not
# supported on Windows, and currently the tfactl executable doesn't even
# exist on Windows
#
# check if tfa is configured (i.e. running); if not, there can be no tfa
# clients, so skip this step
$tfactlop = "print status";
@result = asmcmdshare_execute_tool("tfactl", "", $tfactlop, \@patharr,
$redirect);
$resultstr = join("", @result);
asmcmdshare_trace(3, $resultstr, 'y', 'n');
if ($resultstr !~ m,$tfaerrprefix,)
{
$tfactlop = "client list -details";
if (defined ($clustername))
{
$tfactlop .= " -cluster $clustername";
}
@result = asmcmdshare_execute_tool("tfactl", "", $tfactlop, \@patharr,
$redirect);
$resultstr = join("", @result);
# check if tfactl succeeded
# continue if the reason for "failure" is trying to list a client cluster
# that isn't configured
if ($asmcmdglobal_hash{'utilsucc'} ne 'true' &&
$resultstr !~ m,$nothistfacc,)
{
# ASMCMD-9491: "failed to list the configured Client Clusters"
asmcmdshare_error_msg(9491, undef);
asmcmdshare_trace(1, $resultstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return 1;
}
# parse and store the tfactl output, if there are (relevant) TFA client
# clusters configured
if ($resultstr !~ m,$nothistfacc,)
{
my $row;
foreach $row (@result)
{
# skip rows that only have whitespace
next if ($row =~ m/^\s*$/);
my @splitrow = split(" ", $row);
my %rowinfo = ();
# normalize the case of the key, but preserve the case of the name
my $key = lc($splitrow[0]);
$rowinfo{"name"} = $splitrow[0];
$rowinfo{"version"} = $splitrow[1];
# $$$ currently, tfactl does not yet output a valid GUID, so don't
# parse a GUID from its output
$rowinfo{"guid"} = "";
# if we already have some info for this client cluster
if (defined ($output{$key}))
{
# get the four-digit version numbers to compare
$compvsnnorm = $rowinfo{"version"};
$compvsnnorm =~ s/\.\d+$//;
$metavsnnorm = $output{$key}{"version"};
$metavsnnorm =~ s/\.\d+$//;
# error out if the version doesn't match
# $$$ (eventually, we also want to check that the GUID matches)
if ($compvsnnorm ne $metavsnnorm)
{
asmcmdshare_trace(3, sprintf($configtrc, $tfaname,
$rowinfo{"version"},
$rowinfo{"guid"},
$output{$key}{"metacomp"},
$output{$key}{"version"},
$output{$key}{"guid"}),
'y', 'n');
my @eargs = ($rowinfo{"name"}, $output{$key}{"metacomp"},
$tfaname);
# ASMCMD-9492: "The configuration for Client Cluster '%s' is
# inconsistent between components '%s' and '%s'."
asmcmdshare_error_msg(9492, \@eargs);
return 1;
}
# just indicate that TFA is configured
$output{$key}{"tfa"} = "YES";
}
# otherwise add a new entry
else
{
$rowinfo{"asm"} = "NO";
$rowinfo{"accmode"} = "-";
$rowinfo{"gimr"} = "NO";
$rowinfo{"tfa"} = "YES";
$rowinfo{"acfs"} = "NO";
$rowinfo{"rhp"} = "NO";
$rowinfo{"metacomp"} = $tfaname;
$output{$key} = \%rowinfo;
}
}
}
}
# ACFS
# check if ACFS is loaded; if not, there can be no clients, so skip this step
@result = asmcmdshare_execute_tool("acfsdriverstate", ".bat", "loaded",
\@patharr);
asmcmdshare_trace(3, join("", @result), 'y', 'n');
if ($asmcmdglobal_hash{'utilsucc'} eq 'true')
{
asmcmdshare_trace(3, "Querying for ACFS Client Clusters", 'y', 'n');
$acfsutilop = "cluster credential -n -l";
if (defined ($clustername))
{
$acfsutilop .= " $clustername";
}
my @result = asmcmdshare_execute_tool("acfsutil", ".exe", $acfsutilop,
\@acfsutildir, $redirect);
my $resultstr = join("", @result);
# check if acfsutil succeeded
# continue if the reason for "failure" is trying to list a client cluster
# that isn't configured
if ($asmcmdglobal_hash{'utilsucc'} ne 'true' &&
$resultstr !~ m,$nothisacfscc,)
{
# trim the command name prefix that acfsutil outputs
$resultstr =~ s/.*$acfserrprefix/$acfserrprefix/g;
# ASMCMD-9491: "failed to list the configured member clusters"
asmcmdshare_error_msg(9491, undef);
asmcmdshare_trace(1, $resultstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return 1;
}
# parse and store the acfsutil output, if there are (relevant) ACFS client
# clusters configured
if ($resultstr !~ m,$noacfscc, && $resultstr !~ m,$nothisacfscc,)
{
my $row;
foreach $row (@result)
{
my @splitrow = split(" ", $row);
my %rowinfo = ();
# normalize the case of the key, but preserve the case of the name
my $key = lc($splitrow[0]);
$rowinfo{"name"} = $splitrow[0];
$rowinfo{"version"} = $splitrow[1];
$rowinfo{"guid"} = $splitrow[2];
# if we already have some info for this client cluster
if (defined ($output{$key}))
{
# get the four-digit version numbers to compare
$compvsnnorm = $rowinfo{"version"};
if ($compvsnnorm)
{
$compvsnnorm =~ s/\.\d+$//;
}
$metavsnnorm = $output{$key}{"version"};
$metavsnnorm =~ s/\.\d+$//;
# error out if the version doesn't match or if the GUID doesn't match
if (($rowinfo{"guid"} &&
($rowinfo{"guid"} ne $output{$key}{"guid"})) ||
($compvsnnorm &&
($compvsnnorm ne $metavsnnorm)))
{
asmcmdshare_trace(3, sprintf($configtrc, $acfsname,
$compvsnnorm,
$rowinfo{"guid"},
$output{$key}{"metacomp"},
$metavsnnorm,
$output{$key}{"guid"}),
'y', 'n');
my @eargs = ($rowinfo{"name"}, $output{$key}{"metacomp"},
$acfsname);
# ASMCMD-9492: "The configuration for member cluster '%s' is
# inconsistent between components '%s' and '%s'."
asmcmdshare_error_msg(9492, \@eargs);
return 1;
}
# just indicate that ACFS is configured
$output{$key}{"acfs"} = "YES";
}
# otherwise add a new entry
else
{
$rowinfo{"asm"} = "NO";
$rowinfo{"accmode"} = "-";
$rowinfo{"gimr"} = "NO";
$rowinfo{"rhp"} = "NO";
$rowinfo{"acfs"} = "YES";
$rowinfo{"tfa"} = "NO";
$rowinfo{"metacomp"} = $acfsname;
$output{$key} = \%rowinfo;
}
}
}
}
# RHP
# check if rhpserver is configured; if not, there can be no clients, so skip
# this step
@result = asmcmdshare_execute_tool("srvctl", ".bat", "config rhpserver",
\@patharr);
asmcmdshare_trace(3, (join("", @result)), 'y', 'n');
if ($asmcmdglobal_hash{'utilsucc'} eq 'true')
{
asmcmdshare_trace(3, "Querying for RHP Client Clusters", 'y', 'n');
$rhpctlop = "query client -internal 1";
if (defined ($clustername))
{
$rhpctlop .= " -client $clustername";
}
# on Windows, rhpctl doesn't exist because rhpserver isn't supported
my @result = asmcmdshare_execute_tool("rhpctl", "", $rhpctlop,
\@patharr);
my $resultstr = join("", @result);
# check if rhpctl succeeded
# continue if the reason for "failure" is trying to list a client cluster
# that isn't configured
if ($asmcmdglobal_hash{'utilsucc'} ne 'true' &&
$resultstr !~ m,$nothisrhpcc,)
{
# ASMCMD-9491: "failed to list the configured Client Clusters"
asmcmdshare_error_msg(9491, undef);
asmcmdshare_trace(1, $resultstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
# parse and store the rhpctl output, if there are (relevant) RHP client
# clusters configured
if ($resultstr !~ m,$nothisrhpcc,)
{
my $row;
foreach $row (@result)
{
# guid and version might not be populated (they aren't populated in the
# case where the rhpclient hasn't been started)
#
# Output format:
# $rhpctl query client -inner 1
# #@=result[0]: site={...} guid={...} version={...}
# #@=result[1]: site={...} guid={...} version={...}
# #@=result[2]: site={...} guid={...} version={...}
#
# $rhpctl query client -client myclient -inner 1
# #@=result[0]: site={...} guid={...} version={...} ...
if ($row =~ /site=\{(\S+)\} guid=\{(\S*)\} version=\{(\S*)\}/)
{
my %rowinfo = ();
# normalize the case of the key, but preserve the case of the name
my $key = lc($1);
$rowinfo{"name"} = $1;
$rowinfo{"guid"} = $2;
$rowinfo{"version"} = $3;
# if we already have some info for this client cluster
if (defined ($output{$key}))
{
# get the four-digit version numbers to compare
$compvsnnorm = $rowinfo{"version"};
if ($compvsnnorm)
{
$compvsnnorm =~ s/\.\d+$//;
}
$metavsnnorm = $output{$key}{"version"};
$metavsnnorm =~ s/\.\d+$//;
# error out if the version is defined and doesn't match or if the
# guid is defined and doesn't match (version will not be defined if
# the user didn't specify it and rhpclient hasn't been started;
# guid will not be defined if rhpclient hasn't been started)
if (($rowinfo{"guid"} &&
($rowinfo{"guid"} ne $output{$key}{"guid"})) ||
($compvsnnorm &&
($compvsnnorm ne $metavsnnorm)))
{
asmcmdshare_trace(3, sprintf($configtrc, $rhpname,
$rowinfo{"version"},
$rowinfo{"guid"},
$output{$key}{"metacomp"},
$output{$key}{"version"},
$output{$key}{"guid"}),
'y', 'n');
my @eargs = ($rowinfo{"name"}, $output{$key}{"metacomp"},
$rhpname);
asmcmdshare_error_msg(9492, \@eargs);
return 1;
}
# just indicate that RHP is configured
$output{$key}{"rhp"} = "YES";
}
# otherwise add a new entry
else
{
$rowinfo{"asm"} = "NO";
$rowinfo{"accmode"} = "-";
$rowinfo{"gimr"} = "NO";
$rowinfo{"tfa"} = "NO";
$rowinfo{"acfs"} = "NO";
$rowinfo{"rhp"} = "YES";
$rowinfo{"metacomp"} = $rhpname;
$output{$key} = \%rowinfo;
}
}
}
}
}
# cases where there are no client clusters to list
if (!%output)
{
# the specified client cluster is not configured
if (defined ($clustername))
{
my @eargs = ($clustername);
# ASMCMD-9490: "Client Cluster '%s' is not configured"
asmcmdshare_error_msg(9490, \@eargs);
return 1;
}
# no client clusters are configured
else
{
# ASMCMD-9489: "no Client Clusters configured"
asmcmdshare_error_msg(9489, undef);
return 0;
}
}
# print the header if it is not suppressed
if (!defined ($args{"suppressheader"}))
{
if (defined ($args{'l'}))
{
asmcmdshare_print($longheader);
}
else
{
asmcmdshare_print($header);
}
}
# print the rows of output; the output rows are in case-insensitive
# lexicographic order
my $key;
foreach $key (sort(keys %output))
{
my $cluster;
$cluster = $output{$key};
my $accmode = $cluster->{"accmode"};
my $outputrow = sprintf($fmtstr, $cluster->{"name"},
$cluster->{"version"},
$cluster->{"guid"});
# include details
if (defined ($args{'l'}))
{
if (defined ($args{'suppressheader'}))
{
# add a space
$outputrow .= " ";
# add info about which components are configured
if ($cluster->{"asm"} eq "YES")
{
$outputrow .= "$asmname ($accmode Storage Access),";
}
if ($cluster->{"gimr"} eq "YES")
{
$outputrow .= "$gimrname,";
}
if ($cluster->{"tfa"} eq "YES")
{
$outputrow .= "$tfaname,";
}
if ($cluster->{"acfs"} eq "YES")
{
$outputrow .= "$acfsname,";
}
if ($cluster->{"rhp"} eq "YES")
{
$outputrow .= "$rhpname,";
}
# remove the trailing comma
$outputrow =~ s/,$//;
}
else
{
# use the format string with the component columns instead; the storage
# access mode should be displayed as the last column (i.e. after the
# per-component information)
$outputrow = sprintf($longfmtstr, $cluster->{"name"},
$cluster->{"version"},
$cluster->{"guid"},
$cluster->{"asm"},
$cluster->{"gimr"},
$cluster->{"tfa"},
$cluster->{"acfs"},
$cluster->{"rhp"},
$cluster->{"accmode"});
}
}
asmcmdshare_print($outputrow . "\n");
}
# print the trailer if the header is not suppressed
if (!defined ($args{"suppressheader"}))
{
asmcmdshare_print($trailer);
}
return 0;
}
########
# NAME
# asmcmdsys_process_chcc
#
# DESCRIPTION
# To modify a client cluster for ASM, GIMR, TFA, ACFS, and RHP
#
# PARAMETERS
# $dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# NULL
#
# NOTES
# Requires SYSASM and GI user privileges to execute this operation
########
sub asmcmdsys_process_chcc
{
my $dbh = shift; # get db handle #
my %args; # arguments passed with the command #
my $ret; # asmcmdbase_parse_int_args() return value #
my $sth;
# common to all components
my $clustername; # client cluster name #
my $clusterclass;
# ASM-specific
my $directacc; # direct access to disks or not (ASM) #
my $srvctlop; # arguments to srvctl for starting IOS (ASM) #
# directories in which the utilties are found
my @patharr = ("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
# ASMCMD-9490 "member cluster '%s' is not configured"
my $nothingexists = "ASMCMD-9490"; # no components configured error #
# for redirecting stderr to stdout in order to look for specific error
# messages that go to stderr
my $redirect = 1;
# get command parameters
$ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# get cluster name
$clustername = shift @{$args{'chcc'}};
# check if SYSASM privileges exist
if ($asmcmdglobal_hash{'contyp'} ne "sysasm")
{
# ASMCMD-9488: "operations on Client Cluster require SYSASM privilege"
asmcmdshare_error_msg(9488, undef);
return;
}
# check if Domain Services Cluster is supported on this OS
if (!asmcmdsys_is_dsc_supported())
{
my @eargs = ($ASMCMDSYS_DSC_NAME);
# ASMCMD-9493: "The '%s' feature is not supported on this operating
# system."
asmcmdshare_error_msg(9493, \@eargs);
return;
}
# check if the user executing the command is the GI user
if (!asmcmdsys_is_gi_user())
{
# ASMCMD-9546: "Insufficient permission to execute the command. The command
# requires an Oracle Grid Infrastructure user."
asmcmdshare_error_msg(9546, undef);
return;
}
# make additional checks for cluster class in production but in a development
# environment, skip these checks so that tests continue to work
if (!asmcmdshare_is_dev_env())
{
$clusterclass = asmcmdsys_get_cluster_class();
# error out if we failed to get the cluster class
if (!defined ($clusterclass))
{
return;
}
# error out if we are on a member cluster
if ($clusterclass eq CLUSTER_CLASS_MEMBER)
{
# ASMCMD-9498: "The command is only supported on the Domain Services
# Cluster."
asmcmdshare_error_msg(9498, undef);
return;
}
}
# check if the cluster we are attempting to modify exists and error out if
# not
my @result = asmcmdshare_execute_tool("asmcmd", ".bat", "lscc $clustername",
\@patharr, $redirect);
if (join("", @result) =~ m,$nothingexists,)
{
my @eargs = ($clustername);
# ASMCMD-9490: "member cluster '%s' is not configured"
asmcmdshare_error_msg(9490, \@eargs);
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
# determine the requested disk access mode - the command-line usage will
# enforce that the user specifies either direct or indirect, so assume
# indirect unless the user specifies direct
$directacc = 0;
if (defined ($args{'direct'}))
{
$directacc = 1;
}
# Start IOS if indirect access
if ($directacc == 0)
{
$srvctlop = "start ioserver";
my @result = asmcmdshare_execute_tool("srvctl", ".bat", $srvctlop,
\@patharr);
# check if srvctl succeeded
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# ignore error. write only to trace file
asmcmdshare_trace(1, join("", @result), 'y', 'n');
}
}
# modify the configuration - if the user asked for a no-op, the API will
# error out
$sth = $dbh->prepare(q{
begin
dbms_diskgroup.changeclientcluster (:clname, :direct_access);
end;
});
# bind input parameters
$sth->bind_param(":clname", $clustername);
$sth->bind_param(":direct_access", $directacc);
$ret = $sth->execute();
if (!defined($ret))
{
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
1;
OHA YOOOO