MINI MINI MANI MO
# Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
#
# NAME
# asmcmddisk - ASM CoMmanD line interface DISK operations
#
# DESCRIPTION
# This module contains the code for ASMCMD/ASM disk-related
# operations, such as bad block remap and listing the
# contents of v$asm_disk.
#
# NOTES
# usage: asmcmdcore [-p] [command]
#
# MODIFIED (MM/DD/YY)
# diguzman 09/12/16 - 24570890: handle sites in non extended dg
# anovelo 08/18/16 - 24469683: Improve mkdg message if file unreadable
# emsu 06/08/16 - Add site_guid to lsdsk
# diguzman 06/22/16 - 23623646: _is_new_redundancy_ok uses v$ view not gv$
# anovelo 05/05/16 - Add options WITH/WITHOUT and MODIFY for rebalance
# diguzman 05/30/16 - 19654070: Little change at _no_instance_cmd routine
# emsu 05/17/16 - 23266809: Get rid of temporary file in stamp
# soye 04/12/16 - return check status from asmcmddisk_process_chkdg
# saklaksh 03/02/16 - 21934604: Fix asmcmd mkdg to combine disks correctly
# saklaksh 03/02/16 - 22880348: Fix asmcmd mkdg to process <dsk> correctly
# jesugonz 02/16/15 - 22660914: add site_status column to lsdsk
# emsu 01/22/16 - rti18960899: Disallow stamping disk label on
# AFD/ASMLIB disks
# emsu 01/21/16 - 22579643: Fix lsdsk -I for disks with invalid header
# jesugonz 12/01/15 - 22330738: Add rename disk command to chdg
# cgraybl 09/24/15 - 21699408: fixed kfed output in asmcmddisk_scan_dsk
# emsu 08/31/15 - 21827297: Add site info to lsdsk
# jesugonz 09/07/15 - 21129410: Disallow stamping mounted disks
# jesugonz 08/18/15 - 21465980: If site provided, failgroup is mandatory
# diguzman 06/02/15 - 21137622: chdg. Fix case issue w/new redundancy type
# hppancha 11/07/14 - 19879934: add wait clause to chdg for rebalance
# diguzman 02/27/15 - 20655494: Add dg redundancy migration support to chdg
# ykatada 12/04/14 - 15949549: Move asmcmddisk_init_disk_attr and
# asmcmddisk_get_dsk to asmcmdshare
# hpikkili 10/10/14 - 19878169: add asmcmd migrate
# ykatada 10/03/14 - #19617921: use bind variables to SELECTs
# hppancha 03/28/14 - change spawnutil to utilsucc
# manuegar 01/21/14 - Bug 18106915 - When calculating $io_delta_info
# subtract previous from current value
# pvenkatr 05/13/13 - LRG 8953219 - resetting $asmcmdglobal{'running'}
# state after iostat completed.
# manuegar 05/08/13 - Bug13951456 Support bind parameters
# pvenkatr 02/01/13 - using asmcmdshare_execute_tool.
# arpsriva 27/11/12 - Fix for bug 1591518.
# shmubeen 09/26/12 - lrgfix 7273508 - kfod command's disks option typo
# cemiramo 06/16/12 - lrg 7054808 use full asm_diskstring param name in
# kfod
# pvenkatr 02/22/12 - Bug 12990216 - ASMCMD extensions for project 33011.
# pvenkatr 01/18/12 - Bug 13556583 - LSOD RETURNING NO RESULT WITH
# NON-WILDCARD PROC NAME
# adileepk 11/25/11 - Fix for bug-12661648. ASMCMD should have exit status
# as -1 when a command fails in non-interactive mode.
# s0shanka 08/29/11 - Removing redundant error message #9392 with #9391
# pvenkatr 08/24/11 - Removed flags hash table - using from XML
# adileepk 06/20/11 - Connection Pooling.
# adileepk 03/28/11 - Adding column for failgroup type for lsdsk command.
# canuprem 02/03/11 - Bug 11056684 change the options for kfod
# adileepk 01/31/11 - Fix for bug 9873674.
# adileepk 10/20/10 - Changes made to integrate the parser module with
# asmcmd.
# pvenkatr 06/08/10 - Bug # 8586570 support for Quorum failure
# diskgroups/disks.
# 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
# mchimang 02/22/10 - Fixed the bug 9364411
# moreddy 02/04/10 - bug9262634 fixed printing of error messages
# moreddy 01/19/10 - Adding tracing messages
# moreddy 12/14/09 - bug-9135531 broken xml files should not succeed
# mproddut 10/09/09 - #8753738 mount does not mount more than one diskgroup
# amitroy 10/09/09 - #BUG 8829999:iostat returning uninitialized value
# into console with offline disk
# pvenkatr 09/03/09 - Help message from xml file
# danagara 08/26/09 - bug-8554208 Added success message for few asm cmds
# amitroy 09/22/09 - modifying 'offline' time arguement; bug #8711671.
# sanselva 07/30/09 - #8707883 lsod disk pattern was not parsed
# sanselva 06/24/09 - fix help msgs for iostat
# soye 06/10/09 - move mapau and mapextent to diagnostic package
# pbagal 05/13/09 - Add OS pid to lsod, and remove type
# sanselva 04/24/09 - remove invalid -a option from 'help offline'
# heyuen 04/17/09 - fix lsdsk -M
# sanselva 04/06/09 - ASMCMD long options and consistency
# heyuen 03/23/09 - rename dgdrop to dropdg/ add iostat
# gmengel 02/13/09 - add option to list missing disks.
# heyuen 01/08/09 - change secsize to get from x$kfkid, x$kfdsk
# heyuen 01/05/09 - lsod
# heyuen 12/05/08 - add checks for sector_size
# heyuen 10/14/08 - use dynamic modules
# heyuen 10/02/08 - enable variable block size in disks
# heyuen 08/26/08 - show voting file, fix mkdg/chdg to accept inline
# args
# heyuen 07/28/08 - use command properties array
# heyuen 07/08/08 - mkdg better error handling, msgs
# heyuen 06/17/08 - fix chdg help
# heyuen 06/16/08 - update help
# heyuen 05/22/08 - fix parser_state
# heyuen 04/30/08 - fix mount restricted
# soye 04/24/08 - add missing diskgroup in rebal's help
# heyuen 04/15/08 - add membership to lsdsk, reorder help messages
# heyuen 03/14/08 - fix chdg parser
# heyuen 03/31/08 - mkdg does not error propperly
# heyuen 03/20/08 - fix disk name during diskgroup creation
# heyuen 03/14/08 - fix create diskgroup syntax
# soye 12/17/07 - add ASM file mapping support
# heyuen 02/12/08 - change help messages
# ykatada 01/28/08 - #6769870 correct the wrong word 'BYTES_WRITEEN'
# heyuen 12/04/07 - use kfod instead of regular ls for listing disks
# heyuen 08/08/07 - do not scan for disk groups in lsdsk offline mode
# hqian 06/24/07 - #6137843: reimplement lsdsk using kfed
# hqian 05/31/07 - Fix uninitialized value in lsdsk
# heyuen 05/25/07 - add return codes for errors
# hqian 05/24/07 - remap: error out for external redundancy disk group
# heyuen 05/03/07 - fix asmcmd lsdsk return codes
# hqian 03/06/07 - Rename command "repair" to "remap"
# hqian 02/23/07 - #5893911: use new dbms_diskgroup.blockrepair
# interface for repair;
# hqian 01/02/07 - Align IO buffer for disk header read
# hqian 11/30/06 - asmcmd lsdsk -I supports only UNIX and no character
# devices
# heyuen 10/18/06 - Add return codes for lsdsk
# hqian 07/20/06 - #5397026: new asmcmdglobal_no_instance_callbacks
# hqian 06/19/06 - 11gR1 proj-18435: implement asmcmd repair
# hqian 06/14/06 - 11gR1 proj-18435: implement asmcmd lsdsk
# hqian 06/14/06 - Creation
#
#############################################################################
#
############################ Functions List #################################
# asmcmddisk_init
# asmcmddisk_process_cmd
# asmcmddisk_process_lsdsk
# asmcmddisk_scan_dsk
# asmcmddisk_convert_date
# asmcmddisk_validate_header
# asmcmddisk_validate_stmt
# asmcmddisk_verify_endian
# asmcmddisk_verify_hard
# asmcmddisk_verify_btype
# asmcmddisk_verify_dformat
# asmcmddisk_path_forward
# asmcmddisk_lsdsk_init_col_wid
# asmcmddisk_process_remap
# asmcmddisk_get_dnum_from_dname
# asmcmddisk_process_help
# asmcmddisk_is_cmd
# asmcmddisk_is_wildcard_cmd
# asmcmddisk_is_no_instance_cmd
# asmcmddisk_parse_int_args
# asmcmddisk_syntax_error
# asmcmddisk_get_cmd_syntax
# asmcmddisk_get_asmcmd_cmds
# asmcmddisk_is_new_redundancy_ok
# asmcmddisk_process_iostat
# asmcmddisk_process_lsod
# asmcmddisk_missing
# asmcmddisk_mkdg_start
# asmcmddisk_mkdg_end
# asmcmddisk_mkdg_char
# asmcmddisk_chdg_start
# asmcmddisk_chdg_end
# asmcmddisk_chdg_char
# asmcmddisk_process_migrate
# asmcmddisk_get_fname_from_fnum
# asmcmddisk_get_gnum_from_gname
# asmcmddisk_get_dg_ausize
# asmcmddisk_get_dsk_secsize
# asmcmddisk_get_inst
# asmcmddisk_process_mkdg
# asmcmddisk_process_dropdg
# asmcmddisk_process_chkdg
# asmcmddisk_process_chdg
# asmcmddisk_process_mount
# asmcmddisk_process_umount
# asmcmddisk_process_online
# asmcmddisk_process_offline
# asmcmddisk_process_rebal
# asmcmddisk_process_stamp
# asmcmddisk_process_stamplist
# asmcmddisk_validate_label
#
#############################################################################
package asmcmddisk;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(asmcmddisk_init
);
use strict;
use DBI qw(:sql_types);
use Getopt::Long qw(:config no_ignore_case bundling);
use Data::Dumper;
use asmcmdglobal;
use asmcmdshare;
use asmcmdparser;
use XML::Parser;
use Data::Dumper;
use List::Util qw[min max];
use asmcmdexceptions;
use asmcmdxmlexceptions;
####################### ASMCMDDISK Global Constants ######################
# ASMCMD Column Header Names:
# Below are the names of the column headers for lsdsk. These headers
# are the ASMCMD equivalent of the columns of v$asm_disk.
our ($ASMCMDDISK_LSDSK_INST_ID) = 'Inst_ID';
our ($ASMCMDDISK_LSDSK_GNUM) = 'Group_Num';
our ($ASMCMDDISK_LSDSK_DNUM) = 'Disk_Num';
our ($ASMCMDDISK_LSDSK_INCARN) = 'Incarn';
our ($ASMCMDDISK_LSDSK_DISKGROUP) = 'Disk_group';
our ($ASMCMDDISK_LSDSK_MOUNTSTAT) = 'Mount_Stat';
our ($ASMCMDDISK_LSDSK_HEADERSTAT) = 'Header_Stat';
our ($ASMCMDDISK_LSDSK_MODESTAT) = 'Mode_Stat';
our ($ASMCMDDISK_LSDSK_STATE) = 'State';
our ($ASMCMDDISK_LSDSK_REDUND) = 'Redund';
our ($ASMCMDDISK_LSDSK_LIBRARY) = 'Library';
our ($ASMCMDDISK_LSDSK_OS_MB) = 'OS_MB';
our ($ASMCMDDISK_LSDSK_TOTAL_MB) = 'Total_MB';
our ($ASMCMDDISK_LSDSK_FREE_MB) = 'Free_MB';
our ($ASMCMDDISK_LSDSK_NAME) = 'Name';
our ($ASMCMDDISK_LSDSK_FGROUP) = 'Failgroup';
our ($ASMCMDDISK_LSDSK_SITENAME) = 'Site_Name';
our ($ASMCMDDISK_LSDSK_SITEGUID) = 'Site_GUID';
our ($ASMCMDDISK_LSDSK_SITESTATUS) = 'Site_Status';
our ($ASMCMDDISK_LSDSK_FGROUP_TYPE) = 'Failgroup_Type';
our ($ASMCMDDISK_LSDSK_LABEL) = 'Label';
our ($ASMCMDDISK_LSDSK_FGROUPLABEL) = 'Failgroup_Label';
our ($ASMCMDDISK_LSDSK_SITELABEL) = 'Site_Label';
our ($ASMCMDDISK_LSDSK_PATH) = 'Path';
our ($ASMCMDDISK_LSDSK_UDID) = 'UDID';
our ($ASMCMDDISK_LSDSK_PRODUCT) = 'Product';
our ($ASMCMDDISK_LSDSK_CREATE_DATE) = 'Create_Date';
our ($ASMCMDDISK_LSDSK_MOUNT_DATE) = 'Mount_Date';
our ($ASMCMDDISK_LSDSK_REPAIR_TIMER) = 'Repair_Timer';
our ($ASMCMDDISK_LSDSK_READS) = 'Reads';
our ($ASMCMDDISK_LSDSK_WRITES) = 'Write';
our ($ASMCMDDISK_LSDSK_READ_ERRS) = 'Read_Errs';
our ($ASMCMDDISK_LSDSK_WRITE_ERRS) = 'Write_Errs';
our ($ASMCMDDISK_LSDSK_READ_TIME) = 'Read_time';
our ($ASMCMDDISK_LSDSK_WRITE_TIME) = 'Write_Time';
our ($ASMCMDDISK_LSDSK_BYTES_READ) = 'Bytes_Read';
our ($ASMCMDDISK_LSDSK_BYTES_WRITTEN) = 'Bytes_Written';
our ($ASMCMDDISK_LSDSK_VOTING_FILE) = 'Voting_File';
my ($ASMCMDDISK_METADATA_BLOCKSIZE) = 4096;
my ($ASMCMDDISK_1MB) = 1048576; # 1MB. #
# Other constants
# Discovery string for Linux.
my ($ASMCMDDISK_LINUX_DISCOVER) = '/dev/raw/raw*';
# Header constants
my ($ASMCMDDISK_HARD_4K) = 130;
my ($ASMCMDDISK_BTYPE_DISKHEAD) = 1;
# Appliance migration (assume default is in migration)
use constant APPLMIGR_NULL => 0;
use constant APPLMIGR_TRUE => 1;
use constant APPLMIGR_FALSE => 2;
my ($asmcmddisk_migrate) = APPLMIGR_NULL;
my ($asmcmddisk_migrate_dg);
my ($asmcmddisk_migrate_ndisks) = 0;
my ($asmcmddisk_migrate_power);
my ($asmcmddisk_migrate_wait);
our (@asmcmddisk_parser_state) = ('dummy');
our ($dgstmt) = 'dummy';
our (@asmcmddisk_parser_disks) = ('dummy');
our (@asmcmddisk_parser_attrs) = ('dummy');
our (%asmcmddisk_parser_fg) = ('dummy','dummy');
our ($xml_error) = 'dummy';
our ($asmcmddisk_parser_text) = ('dummy') ; # to parse for invalid text
# to hold data for chdg-replace
our (%asmcmddisk_user_names) = ('dummy', 'dummy'); # replace user by name
our (%asmcmddisk_user_ids) = (0,0); # replace user by id
our (%asmcmddisk_replace_disks) = ('dummy', 'dummy' ) ; # replace disk
our ($asmcmddisk_replace_power); # POWER clause
our ($asmcmddisk_replace_wait); # WAIT or NOWAIT
our ($asmcmddisk_chdg_power); # POWER clause for add, drop, resize
our ($asmcmddisk_chdg_wait); # WAIT clause for add, drop, resize
# to hold data for mkdg redundancy type
my ($asmcmddisk_mkdg_isNotExternal);
# <site> tag is present in XML configuration
my ($asmcmddisk_chdg_hasSites);
# to hold data for chdg redundancy type change
my ($asmcmddisk_new_redundancy); # New REDUNDANCY type
my ($asmcmddisk_cur_redundancy); # CURrent REDUNDANCY type
my ($asmcmddisk_dgname); # Disk group name
####################### ASMCMDDISK Global Variables ######################
my (%asmcmddisk_cmds) = (lsdsk => {},
lsod => {},
remap => {},
dropdg => {},
mkdg => {},
chkdg => {},
chdg => {},
mount => {},
umount => {},
online => {},
offline => {},
rebal => {},
iostat => {},
stamp => {},
stamplist => {},
);
my (%asmcmddisk_lsod_header) = ('program' , 'Process',
'status' , 'Status',
'inst_id' , 'Instance',
'type' , 'Type',
'ospid' , 'OSPID',
'path' , 'Path');
# Below are the names of the column headers for iostat.
our (%asmcmddisk_iostat_header) = ('group_number' ,'Group_Num',
'group_name' ,'Group_Name',
'disk_number' ,'Dsk_Num',
'name' ,'Dsk_Name',
'reads' ,'Reads',
'writes' ,'Writes',
'read_errs' ,'Read_Err',
'write_errs' ,'Write_Err',
'read_time' ,'Read_Time',
'write_time' ,'Write_Time',
'bytes_read' ,'Reads',
'bytes_written' ,'Writes',
'cold_reads' ,'Cold_Reads',
'cold_writes' ,'Cold_Writes',
'cold_bytes_read' ,'Cold_Reads',
'cold_bytes_written','Cold_Writes',
'hot_reads' ,'Hot_Reads',
'hot_writes' ,'Hot_Writes',
'hot_bytes_read' ,'Hot_Reads',
'hot_bytes_written' ,'Hot_Writes',
);
sub is_asmcmd
{
return 1;
}
########
# NAME
# asmcmddisk_init
#
# DESCRIPTION
# This function initializes the asmcmddisk 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, \&asmcmddisk_process_cmd);
push (@asmcmdglobal_help_callbacks, \&asmcmddisk_process_help);
push (@asmcmdglobal_command_list_callbacks, \&asmcmddisk_get_asmcmd_cmds);
push (@asmcmdglobal_is_command_callbacks, \&asmcmddisk_is_cmd);
push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmddisk_is_wildcard_cmd);
push (@asmcmdglobal_syntax_error_callbacks, \&asmcmddisk_syntax_error);
push (@asmcmdglobal_no_instance_callbacks, \&asmcmddisk_is_no_instance_cmd);
%asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmddisk_cmds);
#Perform ASMCMD consistency check if enabled
if($asmcmdglobal_hash{'consistchk'} eq 'y')
{
if(!asmcmdshare_check_option_consistency(%asmcmddisk_cmds))
{
exit 1;
}
}
}
########
# NAME
# asmcmddisk_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 asmcmddisk module; 0 if not.
#
# NOTES
# Only asmcmdcore_shell() calls this routine.
########
sub asmcmddisk_process_cmd
{
my ($dbh) = @_;
my ($succ) = 0;
my ($result);
# Get current command from global value, which is set by
# asmcmddisk_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 ASMCMDDISK command.
my (%cmdhash) = ( lsdsk => \&asmcmddisk_process_lsdsk,
lsod => \&asmcmddisk_process_lsod,
remap => \&asmcmddisk_process_remap,
dropdg => \&asmcmddisk_process_dropdg,
mkdg => \&asmcmddisk_process_mkdg,
chkdg => \&asmcmddisk_process_chkdg,
chdg => \&asmcmddisk_process_chdg,
mount => \&asmcmddisk_process_mount,
umount => \&asmcmddisk_process_umount,
online => \&asmcmddisk_process_online,
offline => \&asmcmddisk_process_offline,
rebal => \&asmcmddisk_process_rebal,
iostat => \&asmcmddisk_process_iostat,
stamp => \&asmcmddisk_process_stamp,
stamplist => \&asmcmddisk_process_stamplist);
if (defined ( $cmdhash{ $cmd } ))
{ # If user specifies a known command, then call routine to process it. #
$result=$cmdhash{ $cmd }->($dbh);
$succ = 1;
}
return $succ;
}
########
# NAME
# asmcmddisk_process_iostat
#
# DESCRIPTION
# This function processes the asmcmd command iostat.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmddisk_process_iostat
{
my ($dbh) = @_;
my (%args); # Argument hash used by getopts(). #
my ($ret);
my ($qry);
my ($gnum, $row);
my (@what , @from, $sth, @where, @order, @binds);
my (%min_col_wid, $print_format, $print_string, @what_print);
my (%what_delta);
my ($k, $v, @io_list, @io_list_prev, @delta, $h, $p);
my ($dt);
my (@eargs);
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
if (defined($args{'G'}))
{
my ($gname) = $args{'G'};
$gnum = asmcmdshare_get_gnum_from_gname($dbh, $gname);
if (!defined($gnum))
{
@eargs = ($gname);
asmcmdshare_error_msg(8001, \@eargs);
return;
}
$gname =~ tr/a-z/A-Z/;
push (@where, 'v$asm_diskgroup_stat.name = ?');
push (@binds, [$gname, SQL_VARCHAR]);
}
push (@what, 'v$asm_diskgroup_stat.name as group_name');
push (@what, 'v$asm_disk_stat.name');
if (defined($args{'io'})) #display IOs instead of bytes
{
push (@what, 'v$asm_disk_stat.reads');
push (@what, 'v$asm_disk_stat.writes');
$what_delta{'reads'} = 1;
$what_delta{'writes'} = 1;
}
else
{
push (@what, 'v$asm_disk_stat.bytes_read');
push (@what, 'v$asm_disk_stat.bytes_written');
$what_delta{'bytes_read'} = 1;
$what_delta{'bytes_written'} = 1;
}
if (defined($args{'region'}))
{
if (defined($args{'io'}))
{
push (@what, 'v$asm_disk_stat.cold_reads');
push (@what, 'v$asm_disk_stat.cold_writes');
push (@what, 'v$asm_disk_stat.hot_reads');
push (@what, 'v$asm_disk_stat.hot_writes');
$what_delta{'cold_reads'} = 1;
$what_delta{'cold_writes'} = 1;
$what_delta{'hot_reads'} = 1;
$what_delta{'hot_writes'} = 1;
}
else
{
push (@what, 'v$asm_disk_stat.cold_bytes_read');
push (@what, 'v$asm_disk_stat.cold_bytes_written');
push (@what, 'v$asm_disk_stat.hot_bytes_read');
push (@what, 'v$asm_disk_stat.hot_bytes_written');
$what_delta{'cold_bytes_reads'} = 1;
$what_delta{'cold_bytes_writes'} = 1;
$what_delta{'hot_bytes_reads'} = 1;
$what_delta{'hot_bytes_writes'} = 1;
}
}
if (defined($args{'e'}))
{
push (@what, 'v$asm_disk_stat.read_errs');
push (@what, 'v$asm_disk_stat.write_errs');
$what_delta{'read_errs'} = 1;
$what_delta{'write_errs'} = 1;
}
if (defined($args{'t'}))
{
push (@what, 'v$asm_disk_stat.read_time');
push (@what, 'v$asm_disk_stat.write_time');
$what_delta{'read_time'} = 1;
$what_delta{'write_time'} = 1;
}
if (defined($args{'iostat'}))
{
($dt) = @{$args{'iostat'}};
if ($dt !~ m/^\d+$/ or $dt eq 0)
{
@eargs = ($dt);
asmcmdshare_error_msg(9399, \@eargs);
return;
}
}
push (@from, 'v$asm_disk_stat');
push (@from, 'v$asm_diskgroup_stat');
push (@where, 'v$asm_disk_stat.group_number > 0');
push (@where, 'v$asm_disk_stat.group_number = '.
'v$asm_diskgroup_stat.group_number');
push (@order, 'v$asm_diskgroup_stat.name, v$asm_disk_stat.group_number, '.
'v$asm_disk_stat.name');
@io_list_prev = ();
$asmcmdglobal_hash{'running'} = 1;
do
{
$sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where,
\@order, \@binds);
@io_list = ();
while (defined($row = asmcmdshare_fetch($sth)))
{
my(%io_info) = ();
while (($k, $v) = each (%{$row}))
{
$k =~ tr/[A-Z]/[a-z]/;
$io_info{$k} = $v;
}
push (@io_list, \%io_info);
}
asmcmdshare_finish($sth);
#if there is a previous scan, get the delta
if (@io_list_prev)
{
@delta = ();
# entries are uniquely identified by (dgroup, dsk)
foreach $h(@io_list)
{
my ($gnam, $dnam);
$gnam = $h->{'group_name'};
$dnam = $h->{'name'};
foreach $p(@io_list_prev)
{
# Fix for Bug-15915185
# If the diskgroup is dismounted and mounted again
# The group_name and name fields is initialized.
if (defined($p->{'group_name'}) && defined($p->{'name'}))
{
if ($p->{'group_name'} eq $gnam && $p->{'name'} eq $dnam)
{
my (%io_delta_info) = ();
while (($k, $v) = each (%{$p}))
{
$k =~ tr/[A-Z]/[a-z]/;
if (defined($what_delta{$k}))
{
# Fix for Bug-9873674.
# Set the value of the variable to 0.
# The variable becomes uninitialized
# if a disk is offlined while iostat is in running mode.
if (defined($h->{$k}) && defined $v)
{
$io_delta_info{$k} = ($h->{$k} - $v);
}
else
{
$io_delta_info{$k} = 0;
}
$io_delta_info{$k} =~ m/(.*)/;
$io_delta_info{$k} = sprintf("%.2f", $1);
}
else
{
$io_delta_info{$k} = $v;
}
}
push (@delta, \%io_delta_info);
last;
}
}
else
{
@delta = @io_list;
}
}
}
}
else
{
@delta = @io_list;
}
# initialize the min_col_wid array
foreach (@what)
{
my (@a) = split(/[\.\ ]/, $_);
my ($colnam) = $a[$#a];
$min_col_wid{$colnam} = length($asmcmddisk_iostat_header{$colnam});
}
# initialize width with the deltas
foreach $h(@delta)
{
while (($k, $v) = each(%{$h}))
{
if (( defined($k) && defined($v) ))
{
$min_col_wid{$k} = max($min_col_wid{$k}, length($v));
}
}
}
$print_format = '';
foreach (@what)
{
my (@a) = split(/[\.\ ]/, $_);
my ($colnam) = $a[$#a];
$print_format .= "%-" . $min_col_wid{$colnam} ."s ";
}
$print_format .= "\n";
#print header
if (!defined ($args{'suppressheader'}) )
{
@what_print = ();
foreach (@what)
{
my (@a) = split(/[\.\ ]/, $_);
my ($colnam) = $a[$#a];
push (@what_print, $asmcmddisk_iostat_header{$colnam});
}
$print_string = sprintf($print_format, @what_print);
asmcmdshare_print($print_string);
}
#print rows
foreach $h (@delta)
{
@what_print = ();
foreach (@what)
{
my (@a) = split(/[\.\ ]/, $_);
my ($colnam) = $a[$#a];
if (defined ($h->{$colnam}))
{
push (@what_print, $h->{$colnam} );
}
else
{
push (@what_print, "");
}
}
$print_string = sprintf($print_format, @what_print);
asmcmdshare_print($print_string);
}
# if a loop is specified, sleep the specified time
if (defined($dt))
{
asmcmdshare_print "\n";
@io_list_prev = @io_list;
eval{
sleep $dt;
}
}
} while (defined($dt) and $asmcmdglobal_hash{'running'} == 1);
# reset 'running' flag. This is set/used only for iostat command.
$asmcmdglobal_hash{'running'} = 0; # iostat completed, reset flag.
return;
}
########
# NAME
# asmcmddisk_process_lsod
#
# 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
#
# NOTES
# Only asmcmdcore_shell() calls this routine.
########
sub asmcmddisk_process_lsod
{
my ($dbh) = shift;
my (%args);
my ($disk_pattern, $process_pattern);
my ($headers);
my (@what , @from, $sth, $qry, @where, @order, @binds, @tmp_cols);
my ($ret);
my (@dsk_list);
my ($row, $k, $v, $h);
my (%min_col_wid, $print_format, $print_string, @what_print);
my ($gname, $gnum);
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
# Process the disk pattern.
if (defined($args{'lsod'}))
{
($disk_pattern) = @{$args{'lsod'}};
# Prefix & suffix with * for disk pattern.
if($disk_pattern !~ /^\*]/)
{
# disk_pattern does not start '*'
$disk_pattern = "*".$disk_pattern ;
}
if($disk_pattern =~ /\*$/)
{ # disk_Pattern not ending with '*'
$disk_pattern = $disk_pattern."*" ;
}
$disk_pattern =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\%,g ;
}
if (defined($args{'process'}))
{
$process_pattern = $args{'process'};
$process_pattern =~ tr/[a-z]/[A-Z]/;
# prefix & suffix with * for process pattern
if($process_pattern !~ /^\*/ )
{
$process_pattern = "*".$process_pattern
}
if($process_pattern !~ /\*$/)
{
$process_pattern =$process_pattern."*";
}
# replace * with % for SQL.
$process_pattern =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\%,g ;
}
if (defined($args{'G'}))
{
my $gname = $args{'G'};
$gnum = asmcmdshare_get_gnum_from_gname($dbh, $gname);
if (!defined ($gnum))
{
my (@eargs) = ($gname);
asmcmdshare_error_msg(8001, \@eargs);
return;
}
}
# print headers?
$headers = defined($args{'suppressheader'});
push(@what, 'x$kfklsod.inst_id as inst_id');
push(@what, 'v$process.program as program');
push(@what, 'v$process.spid as ospid');
push(@what, 'x$kfklsod.path_kfklsod as path');
# if group number is provided, filter it out
if (defined($gnum))
{
push(@where, 'v$asm_disk.group_number = ?');
push(@binds, [$gnum, SQL_INTEGER]);
push(@where, 'v$asm_disk.path = x$kfklsod.path_kfklsod');
push(@from, 'v$asm_disk');
}
push(@from, 'x$kfklsod');
push(@from, 'v$process');
push(@where, 'v$process.pid = x$kfklsod.process_kfklsod');
if (defined($process_pattern))
{
push(@where, "v\$process.program like ?");
push(@binds, [$process_pattern, SQL_VARCHAR]);
}
if (defined($disk_pattern))
{
push(@where, "x\$kfklsod.path_kfklsod like ?");
push(@binds, [$disk_pattern, SQL_VARCHAR]);
}
push(@order, 'program');
push(@order, 'path_kfklsod');
push(@order, 'ospid');
#$$$ need to implement -g
$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{$_} = length($asmcmddisk_lsod_header{$_});
}
#get the rows
while (defined($row = asmcmdshare_fetch($sth)))
{
my(%dsk_info) = ();
while(($k,$v) = each(%{$row}))
{
$k =~ tr/[A-Z]/[a-z]/;
$dsk_info{$k} = $v;
$min_col_wid{$k} = max($min_col_wid{$k}, length($v));
}
push (@dsk_list, \%dsk_info);
}
asmcmdshare_finish($sth);
#create print format
$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, $asmcmddisk_lsod_header{$_} );
}
$print_string = sprintf($print_format, @what_print);
asmcmdshare_print($print_string);
}
#print rows
foreach $h (@dsk_list)
{
@what_print = ();
foreach (@what)
{
push (@what_print, $h->{$_});
}
$print_string = sprintf($print_format, @what_print);
asmcmdshare_print($print_string);
}
}
########
# NAME
# asmcmddisk_process_lsdsk
#
# DESCRIPTION
# This function processes the asmcmd command lsdsk.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_lsdsk
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my ($ret); # asmcmddisk_parse_int_args() return value. #
my ($discovery) = 0; # flag: 1 for -c and 0 otherwise. #
my ($global) = 0; # flag: 1 for -g and 0 otherwise. #
my ($missing) = 0; # flag: 1 for -M and 0 otherwise. #
our (@disk_list); # list of disks returned from query. #
my ($disk_pattern, $gnum); # disk pattern and group number. #
my (%min_col_wid); # hash of minimum column width. #
my ($row, $row_g, $row_k, $row_s, $row_p, $row_t, $row_n); # row formats. #
my ($print_string, @what_print);
my ($inst); # use ASM instance, 1 for yes, 0 for no. #
my (%disk_types, $header_status); # list of available disk types. #
my ($mode); # mode of the command (interactive or not). #
my ($i);
my ($membership_str);
my (%found_hash) = ();
$disk_types{'MEMBER'}=0;
$disk_types{'CANDIDATE'}=0;
$disk_types{'FORMER'}=0;
$disk_types{'INVALID'}=0;
$disk_types{'UNKNOWN'}=0;
$disk_types{'CONFLICT'}=0;
$disk_types{'INCOMPATIBLE'}=0;
$disk_types{'PROVISIONED'}=0;
$disk_types{'FOREIGN'}=0;
$disk_types{'MISSING'}=0;
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
# Process the --discovery and -g flags.
if (defined($args{'discovery'}))
{
$discovery = 1;
}
if (defined($args{'g'}))
{
$global = 1;
}
if (defined ($args{'M'}))
{
$missing = 1;
}
# Process -G group name.
if (defined ($args{'G'}) && defined($dbh))
{
$gnum = asmcmdshare_get_gnum_from_gname($dbh, $args{'G'});
if (!defined($gnum))
{
my (@eargs) = ($args{'G'});
asmcmdshare_error_msg(8001, \@eargs);
return;
}
}
# Process the disk pattern.
if (defined($args{'lsdsk'}))
{
($disk_pattern) = @{$args{'lsdsk'}};
}
if(defined ($args{'member'}))
{
$membership_str = 'MEMBER';
}
elsif(defined ($args{'candidate'}))
{
$membership_str = 'CANDIDATE FORMER PROVISIONED' ;
$discovery = 1;
}
# If we have a connection to an ASM instance and the -I option is not set. #
if (defined ($dbh) && !defined ($args{'I'}))
{
$inst = 1;
}
else
{
$inst = 0;
}
if ($inst)
{
asmcmdshare_trace(3, "NOTE: Querying ASM instance to get list of disks",
'y', 'n');
# Run the query to get a list of disks.
@disk_list = asmcmdshare_get_dsk($dbh, $gnum, $disk_pattern, undef,
$discovery, $global, $missing,
\%found_hash);
}
else # If no connection to ASM instance. #
{
@disk_list = asmcmddisk_scan_dsk($args{'G'}, $disk_pattern);
}
# Sort the results.
@disk_list = sort asmcmddisk_path_forward @disk_list;
if (defined ($args{'M'}))
{
return unless ($inst);
asmcmddisk_missing($dbh, \@disk_list, \%found_hash);
}
# Calculate column width.
asmcmddisk_lsdsk_init_col_wid (\%min_col_wid);
asmcmdshare_ls_calc_min_col_wid (\@disk_list, \%min_col_wid);
# Set up width values for printf().
$row_g = "%" . $min_col_wid{'inst_id'} . "s ";
if ($inst)
{
$row_k = "%" . $min_col_wid{'total_mb'} . "s " .
"%" . $min_col_wid{'free_mb'} . "s " .
"%" . $min_col_wid{'os_mb'} . "s " .
"%-" . $min_col_wid{'name'} . "s " .
"%-" . $min_col_wid{'failgroup'} . "s " .
"%-" . $min_col_wid{'site_name'} . "s " .
"%-" . $min_col_wid{'site_guid'} . "s " .
"%-" . $min_col_wid{'site_status'} . "s " .
"%-" . $min_col_wid{'failgroup_type'} . "s " .
"%-" . $min_col_wid{'library'} . "s " .
"%-" . $min_col_wid{'label'} . "s " .
"%-" . $min_col_wid{'fgroup_label'} . "s " .
"%-" . $min_col_wid{'site_label'} . "s " .
"%-" . $min_col_wid{'udid'} . "s " .
"%-" . $min_col_wid{'product'} . "s " .
"%-" . $min_col_wid{'redundancy'} . "s ";
$row_s = "%" . $min_col_wid{'reads'} . "s " .
"%" . $min_col_wid{'writes'} . "s " .
"%" . $min_col_wid{'read_errs'} . "s " .
"%" . $min_col_wid{'write_errs'} . "s " .
"%" . $min_col_wid{'read_time'} . "s " .
"%" . $min_col_wid{'write_time'} . "s " .
"%" . $min_col_wid{'bytes_read'} . "s " .
"%" . $min_col_wid{'bytes_written'} . "s " .
"%" . $min_col_wid{'voting_file'} . "s ";
$row_p = "%" . $min_col_wid{'group_number'} . "s " .
"%" . $min_col_wid{'disk_number'} . "s " .
"%" . $min_col_wid{'incarnation'} . "s " .
"%-" . $min_col_wid{'mount_status'} . "s " .
"%-" . $min_col_wid{'header_status'} . "s " .
"%-" . $min_col_wid{'mode_status'} . "s " .
"%-" . $min_col_wid{'state'} . "s ";
$row_t = "%-" . $min_col_wid{'create_date'} . "s " .
"%-" . $min_col_wid{'mount_date'} . "s " .
"%-" . $min_col_wid{'repair_timer'} . "s ";
}
else
{
$row_k = "%" . $min_col_wid{'total_mb'} . "s " .
"%-" . $min_col_wid{'name'} . "s " .
"%-" . $min_col_wid{'failgroup'} . "s " .
"%-" . $min_col_wid{'site_guid'} . "s " .
"%-" . $min_col_wid{'fgroup_label'} . "s " .
"%-" . $min_col_wid{'site_label'} . "s ";
$row_s = "";
$row_p = "%" . $min_col_wid{'disk_number'} . "s " .
"%-" . $min_col_wid{'group_name'} . "s " .
"%-" . $min_col_wid{'header_status'} . "s ";
$row_t = "%-" . $min_col_wid{'create_date'} . "s " .
"%-" . $min_col_wid{'mount_date'} . "s ";
}
$row_n = "%-s\n";
$row .= $row_g if (defined ($args{'g'}) ||
defined ($args{'M'})); # lsdsk -g. #
$row .= $row_k if (defined ($args{'k'})); # lsdsk -k. #
$row .= $row_s if (defined ($args{'statistics'})); # lsdsk --statistics.#
$row .= $row_p if (defined ($args{'p'})); # lsdsk -p. #
$row .= $row_t if (defined ($args{'t'})); # lsdsk -t. #
$row .= $row_n; # Always display disk path. #
# Now generate the printf() code based on user-specified flags.
@what_print = ();
if (defined ($args{'g'}) || defined ($args{'M'}))
{
push(@what_print, $ASMCMDDISK_LSDSK_INST_ID);
}
if (defined ($args{'k'}))
{
if ($inst)
{
push(@what_print, $ASMCMDDISK_LSDSK_TOTAL_MB);
push(@what_print, $ASMCMDDISK_LSDSK_FREE_MB);
push(@what_print, $ASMCMDDISK_LSDSK_OS_MB);
push(@what_print, $ASMCMDDISK_LSDSK_NAME);
push(@what_print, $ASMCMDDISK_LSDSK_FGROUP);
push(@what_print, $ASMCMDDISK_LSDSK_SITENAME);
push(@what_print, $ASMCMDDISK_LSDSK_SITEGUID);
push(@what_print, $ASMCMDDISK_LSDSK_SITESTATUS);
push(@what_print, $ASMCMDDISK_LSDSK_FGROUP_TYPE);
push(@what_print, $ASMCMDDISK_LSDSK_LIBRARY);
push(@what_print, $ASMCMDDISK_LSDSK_LABEL);
push(@what_print, $ASMCMDDISK_LSDSK_FGROUPLABEL);
push(@what_print, $ASMCMDDISK_LSDSK_SITELABEL);
push(@what_print, $ASMCMDDISK_LSDSK_UDID);
push(@what_print, $ASMCMDDISK_LSDSK_PRODUCT);
push(@what_print, $ASMCMDDISK_LSDSK_REDUND);
}
else
{
push(@what_print, $ASMCMDDISK_LSDSK_TOTAL_MB);
push(@what_print, $ASMCMDDISK_LSDSK_NAME);
push(@what_print, $ASMCMDDISK_LSDSK_FGROUP);
push(@what_print, $ASMCMDDISK_LSDSK_SITEGUID);
push(@what_print, $ASMCMDDISK_LSDSK_FGROUPLABEL);
push(@what_print, $ASMCMDDISK_LSDSK_SITELABEL);
}
}
if (defined ($args{'statistics'}))
{
if ($inst)
{
push(@what_print, $ASMCMDDISK_LSDSK_READS);
push(@what_print, $ASMCMDDISK_LSDSK_WRITES);
push(@what_print, $ASMCMDDISK_LSDSK_READ_ERRS);
push(@what_print, $ASMCMDDISK_LSDSK_WRITE_ERRS);
push(@what_print, $ASMCMDDISK_LSDSK_READ_TIME);
push(@what_print, $ASMCMDDISK_LSDSK_WRITE_TIME);
push(@what_print, $ASMCMDDISK_LSDSK_BYTES_READ);
push(@what_print, $ASMCMDDISK_LSDSK_BYTES_WRITTEN);
push(@what_print, $ASMCMDDISK_LSDSK_VOTING_FILE);
}
# Nothing to print if not $inst.
}
if (defined ($args{'p'}))
{
if ($inst)
{
push(@what_print, $ASMCMDDISK_LSDSK_GNUM);
push(@what_print, $ASMCMDDISK_LSDSK_DNUM);
push(@what_print, $ASMCMDDISK_LSDSK_INCARN);
push(@what_print, $ASMCMDDISK_LSDSK_MOUNTSTAT);
push(@what_print, $ASMCMDDISK_LSDSK_HEADERSTAT);
push(@what_print, $ASMCMDDISK_LSDSK_MODESTAT);
push(@what_print, $ASMCMDDISK_LSDSK_STATE);
}
else
{
push(@what_print, $ASMCMDDISK_LSDSK_DNUM);
push(@what_print, $ASMCMDDISK_LSDSK_DISKGROUP);
push(@what_print, $ASMCMDDISK_LSDSK_HEADERSTAT);
}
}
if (defined ($args{'t'}))
{
if ($inst)
{
push(@what_print, $ASMCMDDISK_LSDSK_CREATE_DATE);
push(@what_print, $ASMCMDDISK_LSDSK_MOUNT_DATE);
push(@what_print, $ASMCMDDISK_LSDSK_REPAIR_TIMER);
}
else
{
push(@what_print, $ASMCMDDISK_LSDSK_CREATE_DATE);
push(@what_print, $ASMCMDDISK_LSDSK_MOUNT_DATE);
}
}
# Always display the path.
push(@what_print, $ASMCMDDISK_LSDSK_PATH);
# Now print the header if not --suppressheader and if we have at least one
# row of results.
if (!defined ($args{'suppressheader'}) && (@disk_list > 0))
{
$print_string = sprintf($row, @what_print);
asmcmdshare_print($print_string);
}
# Print the actual rows of results.
for ($i = 0; $i < @disk_list; $i++)
{
next if (defined ($args{'M'}) &&
$disk_list[$i]->{'header_status'} ne "MISSING");
# Construct the printf() statement to eval() later.
@what_print = ();
if (defined ($args{'g'}) ||defined ($args{'M'}))
{
push(@what_print, $disk_list[$i]->{'inst_id'});
}
if (defined ($args{'k'}))
{
if ($inst)
{
push(@what_print, $disk_list[$i]->{'total_mb'});
push(@what_print, $disk_list[$i]->{'free_mb'});
push(@what_print, $disk_list[$i]->{'os_mb'});
push(@what_print, $disk_list[$i]->{'name'});
push(@what_print, $disk_list[$i]->{'failgroup'});
push(@what_print, $disk_list[$i]->{'site_name'});
push(@what_print, $disk_list[$i]->{'site_guid'});
push(@what_print, $disk_list[$i]->{'site_status'});
push(@what_print, $disk_list[$i]->{'failgroup_type'});
push(@what_print, $disk_list[$i]->{'library'});
push(@what_print, $disk_list[$i]->{'label'});
push(@what_print, $disk_list[$i]->{'fgroup_label'});
push(@what_print, $disk_list[$i]->{'site_label'});
push(@what_print, $disk_list[$i]->{'udid'});
push(@what_print, $disk_list[$i]->{'product'});
push(@what_print, $disk_list[$i]->{'redundancy'});
}
else
{
push(@what_print, $disk_list[$i]->{'total_mb'});
push(@what_print, $disk_list[$i]->{'name'});
push(@what_print, $disk_list[$i]->{'failgroup'});
push(@what_print, $disk_list[$i]->{'site_guid'});
push(@what_print, $disk_list[$i]->{'fgroup_label'});
push(@what_print, $disk_list[$i]->{'site_label'});
}
}
if (defined ($args{'statistics'}))
{
if ($inst)
{
push(@what_print, $disk_list[$i]->{'reads'});
push(@what_print, $disk_list[$i]->{'writes'});
push(@what_print, $disk_list[$i]->{'read_errs'});
push(@what_print, $disk_list[$i]->{'write_errs'});
push(@what_print, $disk_list[$i]->{'read_time'});
push(@what_print, $disk_list[$i]->{'write_time'});
push(@what_print, $disk_list[$i]->{'bytes_read'});
push(@what_print, $disk_list[$i]->{'bytes_written'});
push(@what_print, $disk_list[$i]->{'voting_file'});
}
# Nothing to print if not $inst.
}
if (defined ($args{'p'}))
{
if ($inst)
{
push(@what_print, $disk_list[$i]->{'group_number'});
push(@what_print, $disk_list[$i]->{'disk_number'});
push(@what_print, $disk_list[$i]->{'incarnation'});
push(@what_print, $disk_list[$i]->{'mount_status'});
push(@what_print, $disk_list[$i]->{'header_status'});
push(@what_print, $disk_list[$i]->{'mode_status'});
push(@what_print, $disk_list[$i]->{'state'});
}
else
{
push(@what_print, $disk_list[$i]->{'disk_number'});
push(@what_print, $disk_list[$i]->{'group_name'});
push(@what_print, $disk_list[$i]->{'header_status'});
}
}
if (defined ($args{'t'}))
{
if ($inst)
{
push(@what_print, $disk_list[$i]->{'create_date'});
push(@what_print, $disk_list[$i]->{'mount_date'});
push(@what_print, $disk_list[$i]->{'repair_timer'});
}
else
{
push(@what_print, $disk_list[$i]->{'create_date'});
push(@what_print, $disk_list[$i]->{'mount_date'});
}
}
# Always show path.
push(@what_print, $disk_list[$i]->{'path'});
# Now evaluate the printf() expression.
next if (defined ($membership_str) &&
$membership_str !~ $disk_list[$i]->{'header_status'});
$print_string = sprintf($row, @what_print);
asmcmdshare_print($print_string);
# Collect what kinds of disks do we have
$header_status = $disk_list[$i]->{'header_status'};
$disk_types{$header_status}=$disk_types{$header_status}+1;
}
$mode = $asmcmdglobal_hash{'mode'};
if ( $mode eq 'n')
{
if ( $disk_types{'MEMBER'} > 0 && $disk_types{'CANDIDATE'} > 0 ){
if($asmcmdglobal_hash{'ispooled'})
{
$asmcmdglobal_hash{'e'} = 2;
}
else
{
exit 2;
}
}
elsif ( $disk_types{'MEMBER'} == 0 && $disk_types{'CANDIDATE'} > 0 ){
if($asmcmdglobal_hash{'ispooled'})
{
$asmcmdglobal_hash{'e'} = 1;
}
else
{
exit 1;
}
}
elsif ( $disk_types{'MEMBER'} > 0 && $disk_types{'CANDIDATE'} == 0 ){
if($asmcmdglobal_hash{'ispooled'})
{
$asmcmdglobal_hash{'e'} = 0;
}
else
{
exit 0;
}
}
else
{
if($asmcmdglobal_hash{'ispooled'})
{
$asmcmdglobal_hash{'e'} = -1;
}
else
{
exit -1;
}
}
}
}
########
# NAME
# asmcmddisk_scan_dsk
#
# DESCRIPTION
# This function scans the header information on a set of disks
# identified by <disk_pattern> and returns that information.
#
# PARAMETERS
# gname (IN) - string name of the disk group by which to filter
# results.
# disk_pattern (IN) - discovery string that identifies the set of
# disks to scan.
#
# RETURNS
# An array of hashes of disk header fields.
#
########
sub asmcmddisk_scan_dsk
{
my ($gname, $disk_pattern) = @_;
my (@disk_list); # List of hashes of disk information to be printed. #
my (@path_list); # List of path strings returned from the discovery glob. #
my ($path); # Iterator through the list of paths (@path_list). #
my ($has_header); # Boolean: whether a given disk has an ASM header. #
my (@eargs); # Error arguments used for error output. #
my ($kfodop); # arguments to kfod.#
my (@buf, @lines);
if (!defined($asmcmdshare_unix_os{$^O}))
{
# Error out if the OS is not UNIX. We currently support only UNIX OSes.
asmcmdshare_error_msg(9378, undef);
return;
}
# Use default discovery string if one is not specified.
if (!defined ($disk_pattern) || $disk_pattern eq '')
{
$disk_pattern = $ASMCMDDISK_LINUX_DISCOVER;
}
# Substitute any acceptable wild card character with '*', which
# is known to be accepted. This way, any asmcmd wild card
# character can be used.
$disk_pattern =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\*,g;
$kfodop = "asm_diskstring='$disk_pattern' disks=all".
" _asm_a=FALSE no=TRUE op=DISKS";
# directories in which kfod can be found.
my (@patharr) =("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
@buf = asmcmdshare_execute_tool ("kfod",
".exe",
$kfodop,
\@patharr);
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
# The require statement failed. Quit lsdsk with error.
@eargs = ("kfod ".$kfodop, $@);
asmcmdshare_error_msg(9375, \@eargs);
last;
}
@path_list=();
@lines = @buf;
foreach (@lines)
{
my (@line);
$_ =~ s/^\s+//;
@line = split(' ', $_);
push (@path_list, $line[1]);
}
# Open each path one at a time.
foreach $path (@path_list)
{
# Skip directories.
if (-d $path)
{
next;
}
my (%disk_attr); # One row of disk record representing one disk. #
my ($attr); # A single disk header field. #
my ($kfed_args); # kfed arguments line. #
my (@lines); # Array of lines of output from kfed. #
my ($au_size); # AU size of the disk group. #
my ($num_aus); # The number of AUs in the disk group. #
my ($month, $day, $hour, $minute, $second, $year);
# Untaint $path. Match everything except newlines, carriage returns,
# and tabs
$path =~ /([^\n^\r^\t]+)/;
$path = $1;
# Form the kfed execution line to read the disk header.
$kfed_args = "op=read aunum=0 blknum=0 dev=\'$path\'";
# Execute the kfed command and capture the results.
asmcmdshare_trace(4, "asmcmddisk_scan_dsk(): Reading disk header of $path"
." using kfed", 'n', 'n');
@buf = asmcmdshare_execute_tool("kfed",
".exe",
$kfed_args,
\@patharr);
# 22579643: If we get "KFED-00322: invalid content encountered during block
# traversal", it means the disk header is invalid and kfed will exit with a
# non-zero status. We shouldn't error out in this case.
if ($asmcmdglobal_hash{'utilsucc'} ne 'true' and $buf[-2] !~ /^KFED-00322/)
{
# The require statement failed. Quit lsdsk with error.
@eargs = ($kfed_args, $@);
asmcmdshare_error_msg(9375, \@eargs);
last;
}
# If we get "KFED-00303: unable to open file," skip the disk.
# There is no need to print the error, as we're doing discovery,
# essentially.
if ($buf[0] =~ /^KFED-00303/)
{
next;
}
# Split output into an array of lines of output.
@lines = @buf;
# Initialize all fields of the hash to avoid Perl errors.
%disk_attr = asmcmdshare_init_disk_attr();
# We already know the path.
$disk_attr{'path'} = $path;
# Validate disk header.
$has_header = asmcmddisk_validate_header(\@lines);
# If no header, then mark as candidate.
if (!$has_header)
{
$disk_attr{'header_status'} = 'CANDIDATE';
}
else
{
# If there is a header, retrieve the header information.
# Get the header status.
$disk_attr{'header_status'} = $lines[21];
$disk_attr{'header_status'} =~ /KFDHDR_(\w+)$/;
$disk_attr{'header_status'} = $1;
# Get the disk group name.
$disk_attr{'group_name'} = $lines[23];
$disk_attr{'group_name'} =~ s/^kfdhdb.grpname\:\s+(\w+?)\s\;.+/$1/;
$disk_attr{'group_name'} = $1;
# Get the disk number.
$disk_attr{'disk_number'} = $lines[19];
$disk_attr{'disk_number'} =~ s/^kfdhdb.dsknum\:\s+(\d+?)\s\;.+/$1/;
$disk_attr{'disk_number'} = $1;
# Get the fail group name.
$disk_attr{'failgroup'} = $lines[24];
$disk_attr{'failgroup'} =~ s/^kfdhdb.fgname\:\s+(\w+?)\s\;.+/$1/;
$disk_attr{'failgroup'} = $1;
# Get the disk name.
$disk_attr{'name'} = $lines[22];
$disk_attr{'name'} =~ s/^kfdhdb.dskname\:\s+(\w+?)\s\;.+/$1/;
$disk_attr{'name'} = $1;
# Get the site GUID.
$disk_attr{'site_guid'} = asmcmddisk_convert_guid(@lines[25..40]);
# Get the failgroup label.
$disk_attr{'fgroup_label'} = $lines[93];
$disk_attr{'fgroup_label'} =~ s/^kfdhdb.fglbl\:\s+(\w*?)\s\;.+/$1/;
$disk_attr{'fgroup_label'} = $1;
# Get the site label.
$disk_attr{'site_label'} = $lines[92];
$disk_attr{'site_label'} =~ s/^kfdhdb.sitelbl\:\s+(\w*?)\s\;.+/$1/;
$disk_attr{'site_label'} = $1;
# Get the total MB of the disk.
$au_size = $lines[63];
$au_size =~ s/^kfdhdb.ausize\:\s+(\d+?)\s\;.+/$1/;
$au_size = $1;
$num_aus = $lines[65];
$num_aus =~ s/^kfdhdb.dsksize\:\s+(\d+?)\s\;.+/$1/;
$num_aus = $1;
# Calcute total MB. Be careful not to overflow 4-byte integers.
if ($au_size < $ASMCMDDISK_1MB)
{
$disk_attr{'total_mb'} = $au_size * $num_aus / $ASMCMDDISK_1MB;
}
else
{
$disk_attr{'total_mb'} = $au_size / $ASMCMDDISK_1MB * $num_aus;
}
# Get the creation time stamp.
$disk_attr{'create_date'} =
asmcmddisk_convert_date($lines[57], $lines[58]);
# Get the mount time stamp.
$disk_attr{'mount_date'} =
asmcmddisk_convert_date($lines[59], $lines[60]);
}
# Add to disk results list only if 1) we are not restricting by
# disk group or 2) this disk belongs to the specified disk group.
if (!defined($gname) || ($disk_attr{'group_name'} eq uc($gname)))
{
# Reference the disk hash from the disk_list array.
push (@disk_list, \%disk_attr);
}
}
return @disk_list;
}
########
# NAME
# asmcmddisk_convert_date
#
# DESCRIPTION
# This function converts a date that is in kfed output format
# to a string format.
#
# PARAMETERS
# hi (IN) - the high 4-bytes of the date from kfed
# lo (IN) - the low 4-bytes of the date from kfed
#
# RETURNS
# Date string in "MON DAY HH24:MM:SS YEAR" format.
#
########
sub asmcmddisk_convert_date
{
my ($hi, $lo) = @_;
my ($year, $month, $day, $hour, $minute, $second);
my ($date);
my (%months) = ( 1 => 'Jan',
2 => 'Feb',
3 => 'Mar',
4 => 'Apr',
5 => 'May',
6 => 'Jun',
7 => 'Jul',
8 => 'Aug',
9 => 'Sep',
10 => 'Oct',
11 => 'Nov',
12 => 'Dec',
);
# Month, day, hour, and year are kept in the high bits.
$month = $day = $hour = $year = $hi;
# Minute, second, and smaller units are kept in the low bits.
$minute = $second = $lo;
# Use regex to extract the month.
$month =~ /MNTH=0x([0-9a-f]+)/;
$month = $1;
# Use regex to extract the day.
$day =~ /DAYS=0x([0-9a-f]+)/;
$day = $1;
# Use regex to extract the hour.
$hour =~ /HOUR=0x([0-9a-f]+)/;
$hour = $1;
# Use regex to extract the year.
$year =~ /YEAR=0x([0-9a-f]+)/;
$year = $1;
# Use regex to extract the minute.
$minute =~ /MINS=0x([0-9a-f]+)/;
$minute = $1;
# Use regex to extract the second.
$second =~ /SECS=0x([0-9a-f]+)/;
$second = $1;
# Add preceding zeros to values. For the year, the value should be
# 4 digits long, so add a preceding zero if it's only three digits
# long. For all other values, they should be 2 digits long, so add
# preceding zeros if they are each only 1 digit long.
$month = '0' . $month if (length($month) < 2);
$day = '0' . $day if (length($day) < 2);
$hour = '0' . $hour if (length($hour) < 2);
$minute = '0' . $minute if (length($minute) < 2);
$second = '0' . $second if (length($second) < 2);
$year = '0' . $year if (length($year) < 4);
# Convert from hex to decimal. Note that H denotes one nybble, or
# 4-bits, or one hex digit. These five values are at most two
# nybbles, or one byte long. C denotes a byte and is portable.
$month = unpack ("C", pack ("H2", $month));
$day = unpack ("C", pack ("H2", $day));
$hour = unpack ("C", pack ("H2", $hour));
$minute = unpack ("C", pack ("H2", $minute));
$second = unpack ("C", pack ("H2", $second));
# The year requires 4 nybbles, or 2 bytes. H4 always formats in
# 2-byte big-endian format, and n always reads in 2-byte big-endian
# format, so there is no portability issue here, either.
$year = unpack ("n", pack ("H4", "07d7"));
# Add preceding zeros for formatting purposes.
$day = '0' . $day if ($day < 10);
$hour = '0' . $hour if ($hour < 10);
$minute = '0' . $minute if ($minute < 10);
$second = '0' . $second if ($second < 10);
$month = $months{$month};
$date = "$month $day $hour:$minute:$second $year";
return $date;
}
########
# NAME
# asmcmddisk_convert_guid
#
# DESCRIPTION
# This function converts a GUID that is in kfed output format to a string
# format.
#
# PARAMETERS
# guidrows (IN) - an array containing the 16 rows of the GUID from kfed
#
# RETURNS
# 32-character hexadecimal string representation of the GUID (if the GUID is
# zero, returns a string of 32 zeroes - this is consistent with what we
# display in V$ASM_DISK)
#
########
sub asmcmddisk_convert_guid
{
my (@guidrows) = @_;
my ($row);
my ($guidstr) = "";
foreach $row (@guidrows)
{
# Use regex to extract GUID
$row =~ /: 0x([0-9a-f]+)/;
$row = $1;
$guidstr .= $row;
}
return $guidstr;
}
########
# NAME
# asmcmddisk_validate_header
#
# DESCRIPTION
# Given the results from kfed, this function determines if
# this block is a valid ASM disk header block.
#
# PARAMETERS
# lines_ref (IN) - reference to array of lines of kfed results
#
# RETURNS
# TRUE if <block> is a valid ASM disk header and FALSE otherwise.
#
# NOTES
# This function uses a similar algorithm as that of kfbtValid(),
# with the exception of the checksum, which is skipped. The reason
# is that the results kfed returns have already been checksummed.
########
sub asmcmddisk_validate_header
{
my ($lines_ref) = shift;
my ($has_header) = 1;
my ($disk_endian);
my ($disk_hard);
my ($disk_btype);
my ($disk_dformat);
if (@{$lines_ref} < 16)
{
return 0;
}
# Retrieve the four elements to check: endianness, H.A.R.D., block
# type, and data format.
# Endianness
$disk_endian = $lines_ref->[0];
$disk_endian =~ s/^kfbh.endian\:\s+(\d+?)\s\;.+/$1/;
# H.A.R.D.
$disk_hard = $lines_ref->[1];
$disk_hard =~ s/^kfbh.hard\:\s+(\d+?)\s\;.+/$1/;
# Block type
$disk_btype = $lines_ref->[2];
$disk_btype =~ s/^kfbh.type\:\s+(\d+?)\s\;.+/$1/;
# Data format
$disk_dformat = $lines_ref->[3];
$disk_dformat =~ s/^kfbh.datfmt\:\s+(\d+?)\s\;.+/$1/;
# Do header verification.
if (!asmcmddisk_verify_endian($disk_endian))
{
$has_header = 0;
}
elsif (!asmcmddisk_verify_hard($disk_hard))
{
$has_header = 0;
}
elsif (!asmcmddisk_verify_btype($disk_btype))
{
$has_header = 0;
}
elsif (!asmcmddisk_verify_dformat($disk_dformat))
{
$has_header = 0;
}
return $has_header;
}
########
# NAME
# asmcmddisk_validate_stmt
#
# DESCRIPTION
# Given the current values of global variables, decides if the generated
# SQL statement is valid for the disk group type.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# 1 if SQL statement is valid for affected disk group (existing or new).
# undef otherwise.
#
# NOTES
########
sub asmcmddisk_validate_stmt
{
my $dbh = shift;
my $rc = 1;
if (!defined($dgstmt) || $dgstmt eq '')
{
# ASMCMD-9391 "invalid XML file"
asmcmdshare_error_msg(9391, undef);
undef $rc;
}
elsif (defined($asmcmddisk_new_redundancy) && defined($asmcmddisk_dgname))
{
$rc = asmcmddisk_is_new_redundancy_ok($dbh, $asmcmddisk_dgname,
$asmcmddisk_new_redundancy);
}
elsif (defined($asmcmddisk_chdg_hasSites))
{
my @eargs = ("asmcmddisk_validate_stmt01");
my $gnum;
$gnum = asmcmdshare_get_gnum_from_gname($dbh, $asmcmddisk_dgname);
if (asmcmdshare_get_redund($dbh, $gnum) == $ASMCMDGLOBAL_EXTERNAL)
{
$xml_error = 1;
undef $asmcmddisk_chdg_hasSites;
undef $rc;
# ASMCMD-9531 "External redundancy disk groups cannot contain sites."
asmcmdshare_error_msg(9531, \@eargs);
}
}
return $rc;
}
########
# NAME
# asmcmddisk_verify_endian
#
# DESCRIPTION
# Given a string of binary digits <disk_endian>, this function
# verifies if the endianness of this disk endian string is
# the same as the endianness of this machine.
#
# PARAMETERS
# disk_endian (IN) - binary string of disk endianness
#
# RETURNS
# TRUE if the disk endianness matches the machine endianness;
# FALSE otherwise.
#
########
sub asmcmddisk_verify_endian
{
my ($disk_endian) = shift;
my ($ret) = 1;
# If disk endian is not the same as system endian, then check fails.
if ($disk_endian != $asmcmdglobal_hash{'endn'})
{
$ret = 0;
}
return $ret;
}
########
# NAME
# asmcmddisk_verify_hard
#
# DESCRIPTION
# Given an integer <disk_hard>, check to see if this value is
# the ASM HARD 4K constant.
#
# PARAMETERS
# disk_hard (IN) - integer of the disk HARD value
#
# RETURNS
# TRUE if <disk_hard> equals <ASMCMDDISK_HARD_4K>; FALSE otherwise.
#
########
sub asmcmddisk_verify_hard
{
my ($disk_hard) = shift;
my ($ret) = 1;
if ($disk_hard != $ASMCMDDISK_HARD_4K)
{
$ret = 0;
}
return $ret;
}
########
# NAME
# asmcmddisk_verify_btype
#
# DESCRIPTION
# Given an integer <disk_btype>, determine if the disk block type
# is the disk header block type.
#
# PARAMETERS
# disk_btype (IN) - integer value for the disk block type
#
# RETURNS
# TRUE if <disk_btype> is the disk header block type; FALSE otherwise
#
########
sub asmcmddisk_verify_btype
{
my ($disk_btype) = shift;
my ($ret) = 1;
if ($disk_btype != $ASMCMDDISK_BTYPE_DISKHEAD)
{
$ret = 0;
}
return $ret;
}
########
# NAME
# asmcmddisk_verify_dformat
#
# DESCRIPTION
# Determine if the disk format in <disk_dformat> is correct.
#
# PARAMETERS
# disk_dformat (IN) - disk format
#
# RETURNS
# TRUE if <disk_dformat> is correct; FALSE otherwise.
#
########
sub asmcmddisk_verify_dformat
{
my ($disk_dformat) = shift;
my ($ret) = 1;
if ($disk_dformat == 0)
{
$ret = 0;
}
return $ret;
}
########
# NAME
# asmcmddisk_path_forward
#
# DESCRIPTION
# Routine for ordering a sort, used by Perl's built-in function sort().
# Order alphabetically by path from v$asm_disk.
#
# PARAMETERS
# None.
#
# RETURNS
# N/A.
########
sub asmcmddisk_path_forward
{
$a->{'path'} cmp $b->{'path'};
}
########
# NAME
# asmcmddisk_missing
#
# DESCRIPTION
# Routine for adding missing disks to disk list. They are added with
# header_status set to "MISSING"
#
# PARAMETERS
# None.
#
# RETURNS
# N/A.
########
sub asmcmddisk_missing
{
my ($dbh, $list_ref, $found_ref) = @_;
my ($i);
my ($inst);
my ($key);
my (%inst_info) = asmcmddisk_get_inst($dbh);
# Go through the disk list and cross check angainst the instance list
# looking for path/inst_id combos that have not been found.
for ($i = 0; $i < @{$list_ref}; $i++)
{
foreach $inst (keys(%inst_info))
{
$key = $list_ref->[$i]->{'path'} . $inst;
# Was a disk found for this path/instance? If not add it it as missing
if (!defined($found_ref->{$key}))
{
my (%new_disk);
%new_disk = asmcmdshare_init_disk_attr();
$new_disk{'path'} = $list_ref->[$i]->{'path'};
$new_disk{'inst_id'} = $inst_info{$inst};
$new_disk{'header_status'} = 'MISSING';
$found_ref->{$key} = 1;
push(@{$list_ref}, \%new_disk);
}
}
}
}
########
# NAME
# asmcmddisk_lsdsk_init_col_wid
#
# DESCRIPTION
# This routine initializes the minimum column width hash with default values
# for lsdsk. These default values are determined by the length of the values
# of the printed column headers. These header values are not necessarily
# the same as the column names in v$asm_disk but look similar.
#
# PARAMETERS
# min_col_wid_ref (OUT) - reference to hash of minimum column width.
#
# RETURNS
# Null.
#
# NOTES
# Must call this routine or asmcmddisk_lsdsk_init_col_wid() before calling
# asmcmdbase_ls_calc_min_col_wid().
########
sub asmcmddisk_lsdsk_init_col_wid
{
my ($min_col_wid_ref) = shift;
$min_col_wid_ref->{'inst_id'} = length($ASMCMDDISK_LSDSK_INST_ID);
$min_col_wid_ref->{'group_number'} = length($ASMCMDDISK_LSDSK_GNUM);
$min_col_wid_ref->{'disk_number'} = length($ASMCMDDISK_LSDSK_DNUM);
$min_col_wid_ref->{'incarnation'} = length($ASMCMDDISK_LSDSK_INCARN);
$min_col_wid_ref->{'group_name'} = length($ASMCMDDISK_LSDSK_DISKGROUP);
$min_col_wid_ref->{'mount_status'} = length($ASMCMDDISK_LSDSK_MOUNTSTAT);
$min_col_wid_ref->{'header_status'} = length($ASMCMDDISK_LSDSK_HEADERSTAT);
$min_col_wid_ref->{'mode_status'} = length($ASMCMDDISK_LSDSK_MODESTAT);
$min_col_wid_ref->{'state'} = length($ASMCMDDISK_LSDSK_STATE);
$min_col_wid_ref->{'redundancy'} = length($ASMCMDDISK_LSDSK_REDUND);
$min_col_wid_ref->{'library'} = length($ASMCMDDISK_LSDSK_LIBRARY);
$min_col_wid_ref->{'os_mb'} = length($ASMCMDDISK_LSDSK_OS_MB);
$min_col_wid_ref->{'total_mb'} = length($ASMCMDDISK_LSDSK_TOTAL_MB);
$min_col_wid_ref->{'free_mb'} = length($ASMCMDDISK_LSDSK_FREE_MB);
$min_col_wid_ref->{'name'} = length($ASMCMDDISK_LSDSK_NAME);
$min_col_wid_ref->{'failgroup'} = length($ASMCMDDISK_LSDSK_FGROUP);
$min_col_wid_ref->{'site_name'} = length($ASMCMDDISK_LSDSK_SITENAME);
$min_col_wid_ref->{'site_guid'} = length($ASMCMDDISK_LSDSK_SITEGUID);
$min_col_wid_ref->{'site_status'} = length($ASMCMDDISK_LSDSK_SITESTATUS);
$min_col_wid_ref->{'failgroup_type'}= length($ASMCMDDISK_LSDSK_FGROUP_TYPE);
$min_col_wid_ref->{'label'} = length($ASMCMDDISK_LSDSK_LABEL);
$min_col_wid_ref->{'fgroup_label'} = length($ASMCMDDISK_LSDSK_FGROUPLABEL);
$min_col_wid_ref->{'site_label'} = length($ASMCMDDISK_LSDSK_SITELABEL);
$min_col_wid_ref->{'path'} = length($ASMCMDDISK_LSDSK_PATH);
$min_col_wid_ref->{'udid'} = length($ASMCMDDISK_LSDSK_UDID);
$min_col_wid_ref->{'product'} = length($ASMCMDDISK_LSDSK_PRODUCT);
$min_col_wid_ref->{'create_date'} = length($ASMCMDDISK_LSDSK_CREATE_DATE);
$min_col_wid_ref->{'mount_date'} = length($ASMCMDDISK_LSDSK_MOUNT_DATE);
$min_col_wid_ref->{'repair_timer'} = length($ASMCMDDISK_LSDSK_REPAIR_TIMER);
$min_col_wid_ref->{'reads'} = length($ASMCMDDISK_LSDSK_READS);
$min_col_wid_ref->{'writes'} = length($ASMCMDDISK_LSDSK_WRITES);
$min_col_wid_ref->{'read_errs'} = length($ASMCMDDISK_LSDSK_READ_ERRS);
$min_col_wid_ref->{'write_errs'} = length($ASMCMDDISK_LSDSK_WRITE_ERRS);
$min_col_wid_ref->{'read_time'} = length($ASMCMDDISK_LSDSK_READ_TIME);
$min_col_wid_ref->{'write_time'} = length($ASMCMDDISK_LSDSK_WRITE_TIME);
$min_col_wid_ref->{'bytes_read'} = length($ASMCMDDISK_LSDSK_BYTES_READ);
$min_col_wid_ref->{'bytes_written'} = length($ASMCMDDISK_LSDSK_BYTES_WRITTEN);
$min_col_wid_ref->{'voting_file'} = length($ASMCMDDISK_LSDSK_VOTING_FILE);
return;
}
########
# NAME
# asmcmddisk_process_remap
#
# DESCRIPTION
# This function implements the asmcmd remap functionality. Given
# a disk group name, a disk name, and a range of physical blocks,
# this function translates this information into file virtual extents
# in ASM. The dbms_diskgroup.remap() PL/SQL function is called
# to remap the identified virtual extent, if any block within are
# unreadable.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() can call this routine.
#
# Physical blocks are essentially disk sectors.
#
# The AU physical block size is retrieved from the disk
# x@kfkid.blksz_kfkid
#
########
sub asmcmddisk_process_remap
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my ($qry); # SQL query statement. #
my ($sth); # SQL query handler. #
my ($row); # SQL query row. #
my ($gname); # Group name. #
my ($gnum); # Group number. #
my ($dname); # Disk name. #
my ($dnum); # Disk number. #
my ($range); # Range of blocks. #
my ($start, $end); # Start and end of range of blocks. #
my ($ret); # Return value for SQL execution statement. #
my ($plsql_stmt); # ASM PL/SQL statement to execute. #
my (@eargs); # Error arguments. #
my ($pblocksize); # Physical block size of the disk. #
my ($ausize); # AU size. #
my ($pblocks_per_au); # Number of physical blocks per AU. #
my ($aunum); # AU number to fetch in x$kfdat. #
my ($fnum); # File number. #
my ($pxn); # Physical extent number. #
my ($bnum); # Physical block number. #
my ($pblock_offset_in_au); # Physical block offset into an AU. #
my ($file_redund); # File redundancy from redun_kffil. #
my ($redund_factor); # Redundancy factor: 3=high, 2=mirror, 1=unprot. #
my ($vxn); # Virtual extent number, or extent set number. #
my ($au_block_start); # Start of a physical block range on a single AU. #
my ($au_block_end); # End of a physical block range on a single AU. #
# an extent. #
my ($filename); # Filename, in the form +diskgroup.filenumber.incarnation. #
my ($map_failed) = 0; # Whether an AU failed to map to an ASM file. #
# Integer division only, not floating point, which is default.
use integer;
# The remap command requires at least ASM version 11gR1.
if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_11gR1) < 0 )
{
asmcmdshare_error_msg(9381, undef);
return;
}
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
($gname, $dname, $range) = @{$args{'remap'}};
$range =~ s/\s//g; # Remove all spaces. #
($start, $end) = split ('-', $range, 2);
# Make sure that the range is in the format of [0-9]+-[0-9]+, and that
# the $end is >= $start.
if (!defined ($start) || !defined ($end) ||
($start !~ /\d/) || ($start =~ /\D/) ||
($end !~ /\d/) || ($end =~ /\D/) || ($end < $start))
{
asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'});
return;
}
# Validate and get group number.
$gnum = asmcmdshare_get_gnum_from_gname($dbh, $gname);
if (!defined ($gnum))
{
@eargs = ($gname);
asmcmdshare_error_msg(8001, \@eargs);
return;
}
# Validate and get disk number.
$dnum = asmcmddisk_get_dnum_from_dname($dbh, $gnum, $dname);
if (!defined ($dnum))
{
@eargs = ($dname, $gname);
asmcmdshare_error_msg(9371, \@eargs);
return;
}
# Get the sector size for this disk
# All the disks of a diskgroup have the same sector size
# If migration is happening, the value will be the target sector size
# even if migration is not complete
$pblocksize = asmcmddisk_get_dsk_secsize($dbh, $gnum, $dnum);
# Get the ausize for the disk group.
$ausize = asmcmddisk_get_dg_ausize($dbh, $gnum);
$pblocks_per_au = $ausize / $pblocksize;
$bnum = $start;
# Walk through all the physical blocks one AU at a time.
while ($bnum <= $end)
{
# Mark the starting physical block of a new AU.
$au_block_start = $bnum;
# Calculate the AU we want.
$aunum = $bnum / $pblocks_per_au;
# Calculate the physical block offset into the last AU represented
# by the physical block range.
$pblock_offset_in_au = $bnum % $pblocks_per_au;
# Query for the file number and physical extent number.
$qry = 'select fnum_kfdat, xnum_kfdat from x$kfdat where ' .
'group_kfdat=? and number_kfdat=?' .
' and aunum_kfdat=?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,$gnum);
$sth->bind_param(2,$dnum);
$sth->bind_param(3,$aunum);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$fnum = $row->{'FNUM_KFDAT'};
$pxn = $row->{'XNUM_KFDAT'};
asmcmdshare_finish($sth);
# Find ending physical block of the AU, which is the last block
# in this AU.
$au_block_end = $bnum - ($pblock_offset_in_au + 1) + $pblocks_per_au;
# Advances $bnum to the block past the last block on the AU.
$bnum = $au_block_end + 1;
# Error arguments should use $au_block_end as the range ending
# unless $au_block_end exceeds $end, in which case $end
# should be used. The reason we use $end in the latter case
# is that we don't want the error message to include a range
# that is outside of the range originally specified by the user.
if ($au_block_end <= $end)
{
@eargs = ($au_block_start, $au_block_end);
}
else
{
@eargs = ($au_block_start, $end);
}
# If no file is allocated on this AU, then return error.
if (!defined ($fnum) || ($fnum == 0))
{
asmcmdshare_error_msg(9372, \@eargs);
$map_failed = 1;
next;
}
# Get redundancy information on the file from x$kffil.
$qry = 'select redun_kffil from x$kffil ' .
'where group_kffil=? and number_kffil=?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,$gnum);
$sth->bind_param(2,$fnum);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$file_redund = $row->{'REDUN_KFFIL'};
asmcmdshare_finish($sth);
if (!defined ($file_redund))
{
# Assume external redundancy by default if unknown file, which
# is usually when $fnum==0. Having File 0 will error out correctly
# in PL/SQL, so no need to error out here.
$redund_factor = 1;
}
elsif ($file_redund eq "UNPROT")
{
$redund_factor = 1;
}
elsif ($file_redund eq "MIRROR")
{
$redund_factor = 2;
}
else
{
$redund_factor = 3;
}
if ($redund_factor == 1)
{
asmcmdshare_error_msg(9382, \@eargs);
$map_failed = 1;
next;
}
# Calculate the virtual extent number of the file.
$vxn = $pxn / $redund_factor;
# Construct the PL/SQL statement to remap virtual extent $vxn of
# file $fnum or disk group $gnum.
$plsql_stmt = qq|
begin
dbms_diskgroup.remap($gnum, $fnum, $vxn);
exception when others then
raise;
end; |;
# Run SQL. #
$ret = asmcmdshare_do_stmt($dbh, $plsql_stmt);
}
# Print error 9373 if any physical blocks failed to map to an ASM file. */
if ($map_failed)
{
asmcmdshare_error_msg(9373, undef);
}
else
{
asmcmdshare_print "Remap requests submitted\n";
}
return;
}
########
# NAME
# asmcmddisk_process_dropdg
#
# DESCRIPTION
# This function processes the asmcmd command dropdg.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_dropdg
{
my ($dbh) = shift;
my (%args);
my ($force);
my ($mode);
my ($contents);
my (@dgroup_list);
my ($dgname);
my ($dgroup_pattern, $global);
my ($qry, $ret);
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
$force = 0;
$contents = 0;
# check for the force option
$force = 1 if (defined($args{'f'}));
# check for the including contents option
$contents = 1 if (defined($args{'r'}));
($dgname) = @{$args{'dropdg'}};
$qry = 'DROP DISKGROUP ' . $dgname . ' ';
if ($contents){
if ($force){
$qry .= 'FORCE ';
}
$qry .= 'INCLUDING CONTENTS ';
}
$ret = asmcmdshare_do_stmt($dbh, $qry);
$mode = $asmcmdglobal_hash{'mode'};
if ( !$asmcmdglobal_hash{'ispooled'} and ($mode eq 'n') and (!defined($ret)))
{
exit -1;
}
return;
}
########
# NAME
# asmcmddisk_is_new_redundancy_ok
#
# DESCRIPTION
# In order to change the redundancy of an existing disk group from a type to
# another, this function checks wether the desired transition is valid or not.
#
# TO
# EXTERN NORMAL HIGH EXTEND FLEX
# +--------+--------+--------+--------+--------+
# EXTERN | undef | undef | undef | undef | undef |
# +--------+--------+--------+--------+--------+
# F NORMAL | undef | undef | undef | 1 |
# R +--------+--------+--------+--------+
# O HIGH | undef | undef | 1 |
# M +--------+--------+--------+
# EXTEND | undef | undef |
# +--------+--------+
# FLEX | undef |
# +--------+
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gname (IN) - disk group name. must be non-null.
# redund (IN) - desired disk group redundancy, must be non-null.
#
# RETURNS
# 1 - When the diskgroup redundancy type can be changed
# undef - When the diskgroup redundancy type can NOT be changed
#
# NOTES
# Only asmcmddisk_process_chdg() calls this function.
########
sub asmcmddisk_is_new_redundancy_ok
{
my ($dbh) = shift;
my ($gname) = shift; # Disk group name
my ($redund) = shift; # New redundancy type
my ($rc) = 1; # return code
my ($currnt); # Current redundancy type
my (@groups); # List of disk groups info,
my ($group);
my (@eargs);
return unless (defined ($dbh) && defined ($gname) && defined ($redund));
@eargs = ($gname);
@groups = asmcmdshare_get_dg($dbh, $gname, 1, 0);
# 23623646: The query must return no more than one row.
asmcmdshare_assert(@groups < 2, \@eargs);
$group = shift @groups;
# 23623646: If non returned, then disk group is not found. It is possible that
# type is undefined if disk group is not mounted. In that case this field is
# set to empty string.
if (!defined($group) or $group->{'type'} eq '')
{
# ASMCMD-8001 "diskgroup '%s' does not exist or is not mounted"
asmcmdshare_error_msg(8001, \@eargs);
undef $rc;
}
else
{
$currnt = $group->{'type'};
if ($currnt !~ m/^(NORMAL|HIGH)$/ || $redund ne 'FLEX')
{
# ASMCMD-8029 "Cannot change diskgroup redundancy from '%s' to '%s'"
@eargs = ($currnt, $asmcmddisk_new_redundancy);
asmcmdshare_error_msg(8029, \@eargs);
undef $rc;
}
}
return $rc;
}
########
# NAME
# asmcmddisk_mkdg_start
#
# DESCRIPTION
# This function processes the start of xml tags in mkdg.
#
# PARAMETERS
# expat (IN) - expat parser object
# element (IN) - tag element name
# attrs (IN) - tag element attributes
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_mkdg() calls this function.
########
#
# transition map
#
# Tags on the xml elements
# dg => disk group
# fg => failure group
# dsk => disk
# a => attribute
# TO
# |dg |fg |dsk |a |migrate|site |
# +-------+-------+-------+-------+-------+-------+-------+
# dg |err | | | | | |
# +-------+-------+-------+-------+-------+-------+-------+
# fg |err |err | |err |err |err |
# F +-------+-------+-------+-------+-------+-------+-------+
# R dsk |err |err |err |err |err |err |
# O +-------+-------+-------+-------+-------+-------+-------+
# M a |err |err |err |err |err |err |
# +-------+-------+-------+-------+-------+-------+-------+
# migrate|err | | |err |err |err |
# +-------+-------+-------+-------+-------+-------+-------+
# site |err | | |err |err |err |
# +-------+-------+-------+-------+-------+-------+-------+
#
########
sub asmcmddisk_mkdg_start
{
my ($expat, $element, %attrs) = @_;
my (@eargs);
my ($qtype);
if ($element eq 'dg')
{
my ($name, $redun);
undef $asmcmddisk_mkdg_isNotExternal;
if (@asmcmddisk_parser_state != 0)
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
push (@asmcmddisk_parser_state, $element);
if (defined($attrs{'name'}))
{
$name = $attrs{'name'};
}
else
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
$dgstmt = "create diskgroup " . $name . " ";
if (defined($attrs{'redundancy'}))
{
$redun = $attrs{'redundancy'};
$dgstmt .= $redun . " redundancy ";
if (uc($redun) ne 'EXTERNAL')
{
# We are expecting to parse a 'site' element iff redundancy type is
# not external
$asmcmddisk_mkdg_isNotExternal = 1;
}
}
@asmcmddisk_parser_disks = ();
@asmcmddisk_parser_attrs = ();
}
elsif ($element eq 'site')
{
my ($site_name);
push (@asmcmddisk_parser_state, $element);
unless (defined($asmcmddisk_mkdg_isNotExternal))
{
# ASMCMD-9531: "External redundancy disk groups cannot contain sites"
asmcmdshare_error_msg(9531, undef);
$xml_error = 1;
return;
}
if (defined($attrs{'name'}))
{
$site_name = $attrs{'name'};
}
else
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
$dgstmt .= " site " . $site_name . " ";
}
elsif ($element eq 'fg')
{
my ($name);
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] !~ m/^(dg|site)$/)
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
push (@asmcmddisk_parser_state, $element);
# If QUORUM key word specified, add it before failgroup keyword
# If none specified or REGULAR specified, ignore, as REGULAR is default
if (defined ($attrs{'qtype'}))
{
$qtype = uc($attrs{'qtype'}) ;
if (($qtype eq 'QUORUM' ) || ($qtype eq 'REGULAR'))
{
$dgstmt .= " $qtype ";
}
else
{
asmcmdshare_error_msg ( 9391, undef );
$xml_error= 1 ;
return ;
}
}
if (defined ($attrs{'name'}))
{
$name = $attrs{'name'};
$dgstmt .= " failgroup " . $name . " ";
}
else
{
# It's an error if 'name' is not specified in <fg>
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
@asmcmddisk_parser_disks = ();
}
elsif ($element eq 'dsk')
{
my ($string, $name, $size, $force, $clause);
my $under_fg = 0;
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
($asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'dg' &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'fg')
)
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
if ($asmcmddisk_parser_state[$#asmcmddisk_parser_state] eq 'fg')
{
# This <dsk> is present under <fg>
$under_fg = 1;
if (defined ($attrs{'qtype'}))
{
# if <dsk> is under <fg>, 'qtype' is not allowed in <dsk>.
# It must be specified at <fg> level.
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
}
push (@asmcmddisk_parser_state, $element);
$clause = '';
if (defined ($attrs{'string'}))
{
$string = $attrs{'string'};
$clause .= " \'$string\'";
}
if (defined ($attrs{'name'}))
{
$name = $attrs{'name'};
$clause .= " name $name";
}
if (defined ($attrs{'size'}))
{
$size = $attrs{'size'};
$clause .= " size $size";
}
if (defined ($attrs{'force'}))
{
$force = $attrs{'force'};
$clause .= " force";
}
if ($under_fg == 1)
{
# Accumulate <dsk> entries in asmcmddisk_parser_disks
# to process at the end in </fg> tag.
push (@asmcmddisk_parser_disks, $clause);
}
else
{
# Process <dsk> entries right now
if (defined ($attrs{'qtype'}))
{
$qtype = uc($attrs{'qtype'});
if (($qtype eq 'QUORUM' ) || ($qtype eq 'REGULAR'))
{
$dgstmt .= " $qtype ";
}
else
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
}
$dgstmt .= " disk " . $clause ;
}
}
elsif ($element eq 'a')
{
my ($string, $name, $value, $clause);
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'dg')
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
push (@asmcmddisk_parser_state, $element);
if (defined ($attrs{'name'}))
{
$name = $attrs{'name'};
}
if (defined ($attrs{'value'}))
{
$value = $attrs{'value'};
}
$clause = "\'$name\' = \'$value\'";
push (@asmcmddisk_parser_attrs, $clause);
}
else
{
@eargs = ($element);
asmcmdshare_error_msg(9390, \@eargs);
$xml_error = 1;
return;
}
}
########
# NAME
# asmcmddisk_mkdg_end
#
# DESCRIPTION
# This function processes the end of xml tags in mkdg.
#
# PARAMETERS
# expat (IN) - expat parser object
# element (IN) - tag element name
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_mkdg() calls this function.
########
sub asmcmddisk_mkdg_end
{
my ($expat, $element) = @_;
pop @asmcmddisk_parser_state;
if ($element eq 'fg')
{
if ($#asmcmddisk_parser_disks == -1)
{
my @eargs = ($element);
asmcmdshare_error_msg(9398, \@eargs);
$xml_error = 1;
return;
}
# For <fg> tag, disks need to be combined with comma to form
# "failgroup fgname 'dsk1_path', 'dsk2_path' ..."
$dgstmt .= " disk " . join(', ', @asmcmddisk_parser_disks);
@asmcmddisk_parser_disks = ();
}
if ($element eq 'dg' || $element eq 'site')
{
if (@asmcmddisk_parser_attrs > 0)
{
$dgstmt .= " attribute " . join(', ', @asmcmddisk_parser_attrs);
@asmcmddisk_parser_attrs = ();
}
}
}
########
# NAME
# asmcmddisk_mkdg_char
#
# DESCRIPTION
# This function processes the text value of xml node in mkdg.
#
# PARAMETERS
# expat (IN) - expat parser object
# string (IN) - one line of text from text node
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_mkdg() calls this function.
########
sub asmcmddisk_mkdg_char
{
my ($expat, $string) = @_ ;
# in case of multi-line, this callback is called for each line,
# append and collect all lines together
if ( length (chomp($string)) > 0 )
{
$asmcmddisk_parser_text .= $string ;
}
}
########
# NAME
# asmcmddisk_process_mkdg
#
# DESCRIPTION
# This function processes the asmcmd command mkdg.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_mkdg
{
my ($dbh) = shift;
my (%args);
my ($qry, $ret);
my ($parser, $file);
my ($string_args) = '';
my (@eargs);
$xml_error = 0;
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# check for a configuration file
if (-r ${$args{'mkdg'}}[0])
{
($file) = @{$args{'mkdg'}};
}
elsif (-e ${$args{'mkdg'}}[0])
{
# ASMCMD-9392, "provided XML file cannot be read"
asmcmdshare_error_msg(9392, undef);
return;
}
else
{
#$string_args = join (" ", @ARGV);
($string_args) = @{$args{'mkdg'}};
}
@asmcmddisk_parser_state = ();
$dgstmt = "";
# We do not expect any text values for any nodes.
$asmcmddisk_parser_text = "" ;
# specify the handler callbacks
$parser = XML::Parser->new(Handlers =>{Start => \&asmcmddisk_mkdg_start,
End => \&asmcmddisk_mkdg_end,
Char => \&asmcmddisk_mkdg_char,
},
ErrorContext => 5
);
eval
{
if (defined($file))
{
$parser->parsefile($file);
}
else
{
$parser->parse($string_args);
}
};
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;
}
if (asmcmdexceptions::catch())
{
my (@msg, $err);
$err = asmcmdexceptions::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;
}
# error out, if any of the nodes has text values. simply print the text.
# $asmcmddisk_parser_text = trim($asmcmddisk_parser_text);
$asmcmddisk_parser_text =~ s/\s+// ;
if (length ($asmcmddisk_parser_text) > 0 )
{
$xml_error = 1 ;
@eargs = ($asmcmddisk_parser_text);
asmcmdshare_error_msg(9395, \@eargs);
return ;
}
if ($xml_error == 1)
{
return;
}
# Run SQL. #
$ret = asmcmdshare_do_stmt($dbh, $dgstmt);
}
########
# NAME
# asmcmddisk_process_chkdg
#
# DESCRIPTION
# This function processes the asmcmd command chkdg.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_chkdg
{
my ($dbh) = shift;
my (%args);
my ($qry, $ret);
my ($dgname);
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# get the diskgroup name
($dgname) = @{$args{'chkdg'}};
$qry = "ALTER DISKGROUP " . $dgname . " CHECK ";
# repair the disk group
if (defined($args{'repair'}))
{
$qry .= " REPAIR";
}
else
{
$qry .= " NOREPAIR";
}
$ret = asmcmdshare_do_stmt($dbh, $qry);
asmcmdshare_print "Diskgroup altered.\n" ;
return $ret;
}
########
# NAME
# asmcmddisk_chdg_start
#
# DESCRIPTION
# This function processes the start of xml tags in chdg.
#
# PARAMETERS
# expat (IN) - expat parser object
# element (IN) - tag element name
# attrs (IN) - tag element attributes
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_chdg() calls this function.
########
#
# transition map
#
# TO
# |chdg |add |drop |rsz |fg |dsk |replace|migrate|site |rename|
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# chdg |err | | | |err |err | |err |err | |
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# add |err |err |err |err | | |err |err | |err |
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# drop |err |err |err |err | | |err |err | |err |
# F +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# R rsz |err |err |err |err | | |err |err |err |err |
# O +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# M fg |err |err |err |err |err | |err |err |err |err |
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# dsk |err |err |err |err |err |err |err |err |err |err |
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# replace|err |err |err |err |err | |err |err |err |err |
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# migrate|err | |err |err |err |err |err |err |err |err |
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# site |err |err |err |err | |err |err |err |err |err |
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# rename |err |err |err |err |err | |err |err |err |err |
# +-------+-----+------+------+-----+----+-----+-------+-------+-----+------+
# FROM replace node only username, userid, disk nodes are valid.
#
########
sub asmcmddisk_chdg_start
{
my ($expat, $element, %attrs) = @_;
my (@eargs);
my ($qtype);
if ($element eq 'chdg')
{
my ($dgname);
if ($#asmcmddisk_parser_state != -1)
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
if (!defined($attrs{'name'}))
{
@eargs = ('name');
asmcmdshare_error_msg(9391, \@eargs);
$xml_error = 1;
return;
}
push (@asmcmddisk_parser_state, $element);
$dgname = $attrs{'name'};
$asmcmddisk_migrate_dg = $dgname;
$asmcmddisk_dgname = $dgname; # Save the disk group name.
$dgstmt = 'alter diskgroup ' . $dgname . ' ';
if (defined($attrs{'power'}))
{
$asmcmddisk_chdg_power = $attrs{'power'};
$asmcmddisk_migrate_power = $attrs{'power'};
}
else
{
$asmcmddisk_chdg_power = -1;
$asmcmddisk_migrate_power = -1;
}
if (defined($attrs{'redundancy'}))
{
$asmcmddisk_new_redundancy = uc $attrs{'redundancy'};
$dgstmt .= "convert redundancy to $asmcmddisk_new_redundancy ";
}
if (defined($attrs{'wait'}))
{
if(lc($attrs{'wait'}) eq 'true')
{
$asmcmddisk_chdg_wait = 1;
}
elsif(lc($attrs{'wait'}) eq 'false')
{
$asmcmddisk_chdg_wait = -1;
}
else
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
}
else
{
$asmcmddisk_chdg_wait = 0;
}
# In case of invalid tags like <delete>, this array needs to be cleared.
@asmcmddisk_parser_disks = () ;
}
elsif ($element eq 'add')
{
# Must be chdg add or chdg migrate add
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
($asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'chdg' &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'migrate'))
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
# If migrate tag is specified, then all adds must be under migrate
# <chdg> <migrate> <add .. /> <add ../> </migrate> </chdg>
# is the only valid syntax
if ($asmcmddisk_migrate == APPLMIGR_TRUE)
{
if ($asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'migrate')
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
}
else
{
# Unset appliance migration
$asmcmddisk_migrate = APPLMIGR_FALSE;
}
$dgstmt .= ' ' . $element . ' ';
push (@asmcmddisk_parser_state, $element);
@asmcmddisk_parser_disks = ();
}
elsif ($element eq 'migrate')
{
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'chdg')
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
# <chdg> <migrate> <add .. /> <add ../> </migrate> </chdg>
# is the only valid syntax
# $asmcmddisk_migrate is set to FALSE if there was at least one add that is
# not under the migrate tag.
if ($asmcmddisk_migrate == APPLMIGR_FALSE)
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
# Set appliance migration
$asmcmddisk_migrate = APPLMIGR_TRUE;
push (@asmcmddisk_parser_state, $element);
@asmcmddisk_parser_disks = ();
}
elsif ($element eq 'drop')
{
# Must be chdg drop. Also, migrate and drop cannot be used together.
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
($asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'chdg') ||
($asmcmddisk_migrate == APPLMIGR_TRUE))
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
$dgstmt .= ' ' . $element . ' ';
push (@asmcmddisk_parser_state, $element);
@asmcmddisk_parser_disks = ();
}
elsif ($element eq 'replace' )
{
# empty the hash tables before use.
%asmcmddisk_user_names = ();
%asmcmddisk_user_ids = () ;
%asmcmddisk_replace_disks = () ;
if(defined ($attrs{'power'}))
{
$asmcmddisk_replace_power = $attrs{'power'};
}
else
{
$asmcmddisk_replace_power = -1 ; #not specified in the XML file.
}
if(defined($attrs{'wait'}))
{
if(lc($attrs{'wait'}) eq 'true' )
{
$asmcmddisk_replace_wait = 1 ;
}
elsif(lc($attrs{'wait'}) eq 'false')
{
$asmcmddisk_replace_wait = -1 ;
}
else
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
}
else # value not specified so assume NO.
{
$asmcmddisk_replace_wait = 0 ;
}
# Migrate and replace cannot go together
if ($asmcmddisk_migrate == APPLMIGR_TRUE)
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
else
{
# Unset appliance migration
$asmcmddisk_migrate = APPLMIGR_FALSE;
}
push (@asmcmddisk_parser_state, $element);
@asmcmddisk_parser_disks = ();
}
elsif ($element eq 'resize')
{
my ($size);
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'chdg')
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
# Migrate and resize cannot go together
if ($asmcmddisk_migrate == APPLMIGR_TRUE)
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
else
{
# Unset appliance migration
$asmcmddisk_migrate = APPLMIGR_FALSE;
}
$dgstmt .= ' ' . $element . ' ';
if (defined($attrs{'size'}))
{
$size = $attrs{'size'};
$dgstmt .= " all size " . $size." ";
}
push (@asmcmddisk_parser_state, $element);
@asmcmddisk_parser_disks = ();
}
elsif ($element eq 'rename')
{
# Must be chdg rename. Also, migrate and rename cannot be used together.
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
($asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'chdg') ||
($asmcmddisk_migrate == APPLMIGR_TRUE))
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
$dgstmt .= ' ' . $element . ' ';
push (@asmcmddisk_parser_state, $element);
@asmcmddisk_parser_disks = ();
}
elsif ($element eq 'site')
{
my ($parent_element) = $asmcmddisk_parser_state[$#asmcmddisk_parser_state];
my ($valid_parent) = '^(add|drop)$';
if (!defined($parent_element) || $parent_element !~ m/$valid_parent/ ||
!defined($attrs{'name'}))
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
push (@asmcmddisk_parser_state, $element);
if ($parent_element eq 'add')
{
$dgstmt .= "site " . $attrs{'name'} . " ";
}
elsif ($parent_element eq 'drop')
{
$dgstmt .= 'disks in site ' . $attrs{'name'} . ' ';
}
# 24570890: since <site> tag is present, the disk group type must be
# different than EXTERNAL, so we turn on this flag to check DG type later.
$asmcmddisk_chdg_hasSites = 1;
}
elsif ($element eq 'fg')
{
my ($parent_element) = $asmcmddisk_parser_state[$#asmcmddisk_parser_state];
my ($valid_parent) = '^(add|drop|resize|site)$';
# get failure group attributes
%asmcmddisk_parser_fg = ();
if (!defined($parent_element) || $parent_element !~ m/$valid_parent/ ||
!defined($attrs{'name'}))
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
elsif ($parent_element eq 'site')
{
my ($oper) = $asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1];
if ($oper eq 'drop')
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
}
$asmcmddisk_parser_fg{'name'}= $attrs{'name'};
if (defined($attrs{'size'}))
{
$asmcmddisk_parser_fg{'size'}=$attrs{'size'};
}
if (defined($attrs{'force'}))
{
$asmcmddisk_parser_fg{'force'}=$attrs{'force'};
}
# check for QUORUM/REGULAR only for new failuregroups
if (defined($attrs{'qtype'}))
{
# QUORUM is support for all operations: ADD, DROP, RESIZE
$qtype = $attrs{'qtype'};
# supported keywords are QUORUM and REGULAR.
$qtype = uc($qtype);
if (($qtype eq 'QUORUM') || ($qtype eq 'REGULAR' ))
{
$asmcmddisk_parser_fg{'qtype'}=$qtype ;
}
else
{
asmcmdshare_error_msg (9391, undef);
$xml_error=1;
return ;
}
}
push (@asmcmddisk_parser_state, $element);
}
elsif ($element eq 'dsk')
{
# get attributes for a disk
my (%dsk_attr);
if (!defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state]) ||
($asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'add' &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'drop' &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'resize' &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'fg' &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'replace' &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state] ne 'rename')
)
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
# if element == dsk and father == replace, get attributes 'to' and 'from'
if($asmcmddisk_parser_state[$#asmcmddisk_parser_state] eq 'replace')
{
$asmcmddisk_replace_disks {$attrs{'from'}} = $attrs{'to'} ;
}
elsif ($asmcmddisk_parser_state[$#asmcmddisk_parser_state] eq 'rename')
{
# Error out if the disk name was not passed, or if both failgroup and site
# were passed
if (!defined($attrs{'name'}))
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
if (defined($attrs{'fg'}) && defined($attrs{'site'}))
{
asmcmdshare_error_msg(9543, undef);
$xml_error = 1;
return;
}
$dsk_attr{'name'} = $attrs{'name'};
if (defined($attrs{'to'}))
{
$dsk_attr{'to'} = $attrs{'to'};
}
if (defined($attrs{'site'}))
{
$dsk_attr{'site'} = $attrs{'site'};
}
if (defined($attrs{'fg'}))
{
$dsk_attr{'fg'} = $attrs{'fg'};
}
push (@asmcmddisk_parser_disks, \%dsk_attr);
}
else
{
if (defined($attrs{'string'}))
{
$dsk_attr{'string'} = $attrs{'string'};
}
if (defined($attrs{'name'}))
{
$dsk_attr{'name'} = $attrs{'name'};
}
if (defined($attrs{'size'}))
{
$dsk_attr{'size'} = $attrs{'size'};
}
if (defined($attrs{'force'}))
{
$dsk_attr{'force'} = $attrs{'force'};
}
# QUORUM/REGULAR type - only for new disks
if (defined($attrs{'qtype'}))
{
$qtype = $attrs{'qtype'};
# valid keywords are QUORUM & REGULAR
$qtype = uc($qtype);
if ($qtype ne 'QUORUM' && $qtype ne 'REGULAR')
{
asmcmdshare_error_msg(9391,undef);
$xml_error = 1;
return ;
}
$dsk_attr{'qtype'} = $qtype ;
}
$asmcmddisk_migrate_ndisks++;
push (@asmcmddisk_parser_disks, \%dsk_attr);
}
push (@asmcmddisk_parser_state, $element);
}
# replace by username operation
elsif ($element eq 'username' )
{
$asmcmddisk_user_names {$attrs{'from'}} = $attrs{'to'} ;
}
elsif ($element eq 'userid')
{
$asmcmddisk_user_ids {$attrs{'from'}} = $attrs{'to'} ;
}
else
{
@eargs = ($element);
asmcmdshare_error_msg(9390, \@eargs);
$xml_error = 1;
return;
}
}
########
# NAME
# asmcmddisk_chdg_end
#
# DESCRIPTION
# This function processes the end of xml tags in chdg.
#
# PARAMETERS
# expat (IN) - expat parser object
# element (IN) - tag element name
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_chdg() calls this function.
########
sub asmcmddisk_chdg_end
{
my ($expat, $element) = @_;
my ($oper, $fgname);
my (@eargs);
my ($qtype);
my ($first);
my ($user);
my ($userid);
my ($disk) ;
if ( $element eq 'dsk')
{
# if element == dsk and father == replace
if( defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1]) &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1] eq 'replace')
{
$first = 0;
if ( keys %asmcmddisk_replace_disks )
{
$dgstmt .= " replace DISK " ;
foreach $disk (keys %asmcmddisk_replace_disks )
{
# for the first item in the list do not need to add ','
if ($first == 0 )
{
$first = 1 ;
}
else #add ',' for each item.
{
$dgstmt .= " , " ;
}
#replace disk with given locator
$dgstmt .= "\'". $disk . "\' WITH \'";
$dgstmt .= $asmcmddisk_replace_disks{$disk}."\'" ;
}
# Done with replace-disk, clear it.
%asmcmddisk_replace_disks = () ;
}
# power can be specified for REPLACE DISK clause, this is different
# case compared to REBALANCE clause, so handled at <replace> node.
if ($asmcmddisk_replace_power != -1)
{
$dgstmt .= " POWER ".$asmcmddisk_replace_power ;
}
# wait can be specified for REPLACE DISK clause, this is different
# case compared to REBALANCE clause, so handled at <replace> node.
if ($asmcmddisk_replace_wait == 1)
{
$dgstmt .= " WAIT " ;
}
elsif ($asmcmddisk_replace_wait == -1)
{
$dgstmt .= " NOWAIT " ;
}
}
elsif (defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1]) &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1] eq 'rename')
{
my ($dsk) = $asmcmddisk_parser_disks[0];
$dgstmt .= " disk '$dsk->{'name'}' ";
if (defined($dsk->{'to'}))
{
$dgstmt .= " to '$dsk->{'to'}' ";
}
if (defined($dsk->{'site'}))
{
$dgstmt .= " site $dsk->{'site'} ";
}
if (defined($dsk->{'fg'}))
{
$dgstmt .= " failgroup $dsk->{'fg'} ";
}
@asmcmddisk_parser_disks = ();
}
# if element == dsk and father != fg, operate
elsif ( defined($asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1]) &&
$asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1] ne 'fg')
{
my ($dsk);
$oper = $asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1];
# there is just one disk in the array
$dsk = $asmcmddisk_parser_disks[0];
#QUORUM
if(defined($dsk->{'qtype'}))
{
$qtype = $dsk->{'qtype'};
$dgstmt .= " $qtype " ; # only QUORUM/REGULAR; others errored out.
}
# add operation
if ($oper eq 'add')
{
$dgstmt .= " disk " . "\'" . $dsk->{'string'} . "\' ";
if (defined($dsk->{'name'}))
{
$dgstmt .= " name " . $dsk->{'name'} . " ";
}
if (defined($dsk->{'size'}))
{
$dgstmt .= " size " . $dsk->{'size'} . " ";
}
if (defined($dsk->{'force'}) && (lc($dsk->{'force'}) eq 'true'))
{
$dgstmt .= " force ";
}
}
# drop operation
elsif ($oper eq 'drop')
{
$dgstmt .= " disk " . $dsk->{'name'} . " ";
if (defined($dsk->{'force'}))
{
if (lc($dsk->{'force'}) eq 'true')
{
$dgstmt .= "force ";
}
}
}
# resize operation
elsif ($oper eq 'resize')
{
$dgstmt .= " disk " . $dsk->{'name'} . " ";
$dgstmt .= "size " . $dsk->{'size'} . " ";
}
@asmcmddisk_parser_disks = ();
}
}
# if element == fg, operate
if ($element eq 'fg')
{
my @dsks = ();
my $parent = '';
my $disk_string;
$fgname = $asmcmddisk_parser_fg{'name'};
if ($asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1] eq 'site')
{
$parent = 'site';
$oper = $asmcmddisk_parser_state[$#asmcmddisk_parser_state - 2];
}
else
{
$oper = $asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1];
}
if (!defined($oper) || !defined($fgname))
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
}
# add or migrate operation
elsif ($oper eq 'add')
{
# valid values are QUORUM/REGULAR. If any other values provided,
# asmcmddisk_chdg_start would have error-ed out.
if (defined($asmcmddisk_parser_fg{'qtype'}) &&
($asmcmddisk_parser_fg{'qtype'} eq 'QUORUM' ))
{
$dgstmt .= " QUORUM " ;
}
$dgstmt .= " failgroup " . $fgname . " disk ";
if($#asmcmddisk_parser_disks == -1)
{
@eargs = ($element);
asmcmdshare_error_msg(9398, \@eargs);
$xml_error = 1;
return;
}
foreach (@asmcmddisk_parser_disks)
{
$disk_string = "\'" . $_->{'string'} . "\'";
if (defined($_->{'name'}))
{
$disk_string .= " name " . $_->{'name'} . " ";
}
if (defined($_->{'size'}))
{
$disk_string .= " size " . $_->{'size'} . " ";
}
if (defined($_->{'force'}))
{
if (lc($_->{'force'}) eq 'true')
{
$disk_string .= " force ";
}
}
push (@dsks, $disk_string);
}
$dgstmt .= join (", ", @dsks) . " ";
}
# drop operation
elsif($oper eq 'drop')
{
# valid key values are QUORUM/REGULAR. If any other values provided,
# asmcmddiks_chdg_start would have error-ed out.
if (defined($asmcmddisk_parser_fg{'qtype'}) &&
($asmcmddisk_parser_fg{'qtype'} eq 'QUORUM' ) )
{
$dgstmt .= " disks in QUORUM failgroup " . $fgname ." " ;
}
else # REGULAR FAILGROUP
{
$dgstmt .= " disks in failgroup " . $fgname . " ";
}
if (defined($asmcmddisk_parser_fg{'force'}))
{
if ( lc($asmcmddisk_parser_fg{'force'}) eq 'true' )
{
$dgstmt .= "force ";
}
}
}
# resize operation
elsif($oper eq 'resize')
{
# valid key words are QUORUM/REGULAR. If any other value provided,
# asmcmddisk_chdg_start would have error-ed out.
if ( defined($asmcmddisk_parser_fg{'qtype'} ) &&
( $asmcmddisk_parser_fg{'qtype'} eq 'QUORUM' ) )
{
$dgstmt .= " disks in QUORUM failgroup " . $fgname ;
}
else
{
$dgstmt .= " disks in failgroup " . $fgname;
}
if (defined($asmcmddisk_parser_fg{'size'}))
{
$dgstmt .= " size " . $asmcmddisk_parser_fg{'size'} . " ";
}
}
@asmcmddisk_parser_disks = ();
}
$first = 0 ;
# replace operation
if ($element eq 'username')
{
if ((keys %asmcmddisk_user_names > 0) && (keys %asmcmddisk_user_ids > 0 ))
{
# both user names & ids can not be provided together. Only one.
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
# power can only be specified for replace-disk
if ($asmcmddisk_replace_power != -1 )
{
asmcmdshare_error_msg(9391, undef);
$xml_error = 1;
return;
}
# wait can only be specified for replace-disk
if($asmcmddisk_replace_wait != 0)
{
asmcmdshare_error_msg(9391, undef);
$xml_error=1;
return ;
}
# process the replace-user-clause. Multiple set of names can be provided.
if ( keys %asmcmddisk_user_names )
{
$dgstmt .= " replace USER BY NAME " ;
foreach $user (keys %asmcmddisk_user_names)
{
# for the first item in the list do not need to add ','
if ($first == 0 )
{
$first = 1 ;
}
else #add ',' for each item.
{
$dgstmt .= " , " ;
}
#replace user with given another user name
$dgstmt .= "\'". $user . "\' WITH \'" ;
$dgstmt .= $asmcmddisk_user_names{$user}. "\'" ;
}
}
# Done with replace-user-names, clear it.
%asmcmddisk_user_names = ();
}
# process replace-user-id clause.
if ($element eq 'userid')
{
if ( keys %asmcmddisk_user_ids )
{
$dgstmt .= " replace USER BY NUMBER " ;
foreach $userid (keys %asmcmddisk_user_ids )
{
# for the first item in the list do not need to add ','
if ($first == 0 )
{
$first = 1 ;
}
else #add ',' for each item.
{
$dgstmt .= " , " ;
}
#replace user with given another user name
$dgstmt .= "\'". $userid . "\' WITH \'" ;
$dgstmt .= $asmcmddisk_user_ids{$userid}. "\'" ;
}
}
# Done with replace-user-ids, clear it.
%asmcmddisk_user_ids = () ;
}
if($element eq 'chdg')
{
my ($power, $wait);
if(defined($asmcmddisk_chdg_power) && $asmcmddisk_chdg_power != -1)
{
$power = $asmcmddisk_chdg_power;
}
if(defined($asmcmddisk_chdg_wait) && $asmcmddisk_chdg_wait != 0)
{
$wait = $asmcmddisk_chdg_wait;
}
if(defined($power) || defined($wait))
{
$dgstmt .= " REBALANCE ";
}
if(defined($power))
{
$dgstmt .= "POWER ". $power." ";
}
if(defined($wait))
{
if($wait == 1)
{
$dgstmt .= "WAIT ";
}
elsif($wait == -1)
{
$dgstmt .= "NOWAIT ";
}
}
}
pop @asmcmddisk_parser_state;
}
########
# NAME
# asmcmddisk_chdg_char
#
# DESCRIPTION
# This function processes the text values
#
# PARAMETERS
# expat (IN) - expat parser object
# string (IN) - one line of the text node
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_chdg() calls this function.
########
sub asmcmddisk_chdg_char
{
my ($expat, $string) = @_ ;
# This function is called for each line found the text node.
# append all the text
$asmcmddisk_parser_text .= $string ;
}
########
# NAME
# asmcmddisk_process_chdg
#
# DESCRIPTION
# This function processes the asmcmd command chdg.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_chdg
{
my ($dbh) = shift;
my (%args);
my ($qry, $ret);
my ($parser, $file);
my ($string_args) = '';
my (@eargs);
$xml_error = 0;
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# check for a configuration file
if (-r ${$args{'chdg'}}[0])
{
($file) = @{$args{'chdg'}};
}
else
{
#$string_args = join (" ", @ARGV);
($string_args) = @{$args{'chdg'}};
}
@asmcmddisk_parser_state = ();
$asmcmddisk_new_redundancy = undef;
$asmcmddisk_dgname = undef;
$asmcmddisk_parser_text = "" ;
$dgstmt = "";
# specify the handler callbacks
$parser = XML::Parser->new(Handlers =>{Start => \&asmcmddisk_chdg_start,
End => \&asmcmddisk_chdg_end,
Char => \&asmcmddisk_chdg_char
});
eval
{
if (defined($file))
{
$parser->parsefile($file);
}
else
{
$parser->parse($string_args);
}
};
if ($@)
{
@eargs = ($@);
asmcmdshare_error_msg(9395, \@eargs);
return;
}
# error out, if any of the nodes has text values. Simply print the text.
$asmcmddisk_parser_text =~ s/\s+// ;
if (length($asmcmddisk_parser_text) > 0)
{
@eargs = ($asmcmddisk_parser_text);
asmcmdshare_error_msg(9395, \@eargs);
$xml_error = 1;
}
$xml_error = 1 unless (defined(asmcmddisk_validate_stmt($dbh)));
return if ($xml_error == 1);
# Run SQL. #
if ($asmcmddisk_migrate == APPLMIGR_TRUE)
{
if ($ret = asmcmddisk_process_migrate($dbh))
{
asmcmdshare_print "Diskgroup altered.\n" ;
}
}
else
{
$ret = asmcmdshare_do_stmt($dbh, $dgstmt);
asmcmdshare_print "Diskgroup altered.\n" ;
}
}
########
# NAME
# asmcmddisk_process_migrate
#
# DESCRIPTION
# This function processes the asmcmd command migrate.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# 0 on FAILURE. 1 on SUCCESS.
#
# NOTES
# Only asmcmddisk_process_chdg() calls this function.
########
sub asmcmddisk_process_migrate
{
my ($dbh) = @_;
my ($skipadd) = 0;
my ($sth);
my ($qry) = "";
my ($err) = "";
my ($row);
my ($failsize);
my ($ndisk);
my ($ok) = 0;
my ($applmode);
# If we do not catch this, the current asmcmd with no disks specified
# would effectively be an alter diskgroup <dg> rebalance in Step 7.
# The rebalance may be unnecessary and costly, especially if the user
# did not intend it.
if ($asmcmddisk_migrate_ndisks eq 0)
{
asmcmdshare_print "No disks specified.\n";
return $ok;
}
# Step 1: Check appliance mode
$qry = "SELECT VALUE FROM V\$ASM_ATTRIBUTE WHERE ";
$qry .= "NAME='appliance.mode' AND GROUP_NUMBER=";
$qry .= "(SELECT GROUP_NUMBER FROM V\$ASM_DISKGROUP WHERE ";
$qry .= "NAME=UPPER('$asmcmddisk_migrate_dg'))";
$sth = asmcmdshare_do_select($dbh, $qry);
$row = asmcmdshare_fetch($sth);
# No rows
if ($sth->rows eq 0)
{
asmcmdshare_print("Diskgroup does not exist or is not mounted.\n");
return $ok;
}
# appliance mode must be TRUE or MIGRATING to start migration
$applmode = $row->{'VALUE'};
if ($applmode ne 'TRUE' && $applmode ne 'MIGRATING')
{
asmcmdshare_print("Appliance mode is disabled.\n");
return $ok;
}
asmcmdshare_finish($sth);
# Step 2: Check that all failure groups have equal number of disks.
# Consider the following scenario
# - diskgroup is already migrated and appliance.mode = TRUE
# - the diskgroup has 1 dropped disk
#
# Run asmcmd chdg migrate ...
# appliance mode is set to MIGRATING
# Explain would fail as the disks are already added to the diskgroup
# Appliance mode cannot be set again to TRUE as there is a dropped disk.
# Also, once appliance mode is set to MIGRATING, no new reconfigurations are
# allowed. So, the dropped disk cannot be added back.
# So the diskgroup is in this intermediate MIGRATING state till we
# interfere and set appliance.mode to MIGRATION_DONE with the migration
# comment.
# The above issue can be avoided if we check that the diskgroup has all the
# disks available (either ONLINE or OFFLINE) to begin with. In other words,
# the disks are all in NORMAL state and no disks are dropped.
# get failure group size
$qry = "SELECT VALUE_KFENV FROM X\$KFENV WHERE ";
$qry .= "NAME_KFENV='appliance._failgroup_size' AND GROUP_KFENV=";
$qry .= "(SELECT GROUP_NUMBER FROM V\$ASM_DISKGROUP WHERE ";
$qry .= "NAME=UPPER('$asmcmddisk_migrate_dg'))";
$sth = asmcmdshare_do_select($dbh, $qry);
$row = asmcmdshare_fetch($sth);
# No rows
if ($sth->rows eq 0)
{
asmcmdshare_print("Diskgroup does not exist or is not mounted.\n");
return $ok;
}
# appliance._failgroup_size
$failsize = $row->{'VALUE_KFENV'};
asmcmdshare_finish($sth);
# get the number of disks in each failure group
$qry = "SELECT COUNT(DISK_NUMBER) AS NDISK FROM V\$ASM_DISK WHERE ";
$qry .= "GROUP_NUMBER=(SELECT GROUP_NUMBER FROM V\$ASM_DISKGROUP WHERE ";
$qry .= "NAME=UPPER('$asmcmddisk_migrate_dg')) AND ";
$qry .= "STATE='NORMAL' GROUP BY FAILGROUP";
$sth = asmcmdshare_do_select($dbh, $qry);
$row = asmcmdshare_fetch($sth);
# No rows
if ($sth->rows eq 0)
{
asmcmdshare_print("Diskgroup does not exist or is not mounted.\n");
return $ok;
}
do
{
$ndisk = $row->{'NDISK'};
# Number of disks in each FG must be equal to appliance._failgroup_size
if ($ndisk ne $failsize)
{
asmcmdshare_print("Not all failure groups have $failsize non-dropping ");
asmcmdshare_print("disks to execute migration.\n");
return $ok;
}
}
while ($row = asmcmdshare_fetch($sth));
asmcmdshare_finish($sth);
# Step 3: Set appliance.mode attribute to MIGRATING
$qry = "ALTER DISKGROUP $asmcmddisk_migrate_dg SET ATTRIBUTE ";
$qry .= "'APPLIANCE.MODE'='MIGRATING' /* _ASM_APPL_MIGRATION */";
asmcmdshare_do_stmt($dbh, $qry);
# Step 4: Check if the resultant configuration is valid under appliance
# mode. The explain query with the appliance migration comments not only
# checks if adding the disks is okay, but it also checks if after adding the
# disks, appliance mode can be enabled.
eval
{
$qry = "EXPLAIN WORK SET STATEMENT_ID='MIGR' FOR $dgstmt ";
$qry .= "/* _ASM_APPL_MIGRATION */";
asmcmdshare_do_stmt($dbh, $qry);
};
if(asmcmdexceptions::catch())
{
$err = $DBI::errstr;
# We can reach here under several scenarios
#
# 1. 15409, "All disks in disk group %s are not of the same type."
# 2. 15410, "Disks in disk group %s do not have equal size."
# 3. 15411, "Failure groups in disk group %s have different number of "
# "disks."
# 4. 15414, "Appliance mode requires at least %s failure groups and "
# "either %s or %s disks in all failure groups."
# 5. 15415, "could not retrieve appliance properties for disk group %s"
#
# The above is possible when
# a. The original configuration has some disks dropped
# b. This is not a proper migration command
# c. The appliance properties of one or more disks could not be retrieved
#
# Revert back to the previous configuration.
if ($err =~ /ORA-15409/ ||
$err =~ /ORA-15410/ ||
$err =~ /ORA-15411/ ||
$err =~ /ORA-15414/ ||
$err =~ /ORA-15415/)
{
# Set appliance mode back to TRUE.
$qry = "ALTER DISKGROUP $asmcmddisk_migrate_dg SET ATTRIBUTE ";
$qry .= "'APPLIANCE.MODE'='MIGRATION_DONE' /* _ASM_APPL_MIGRATION */";
asmcmdshare_do_stmt($dbh, $qry);
# Re-throw the exception
asmcmdexceptions::throw("asmcmdexceptions");
}
# 6. 15029, "disk '%s' is already mounted by this instance"
#
# If all the adding disks in the migration hit the above error, then all the
# disks specificed in the migration are already added to the disk group. It
# is likely the previous migration crashed after adding the disks.
#
# In this case, roll forward by trying to set appliance.mode to TRUE
# and skip adding the disks again.
elsif ($err =~ /ORA-15029/)
{
# Unfortunately, the error is truncated. So it is not possible to tell if
# all the disks were added to the disk group or if it is an incorrect
# migration command. In other words, the number of ORA-15029 errors in
# the error string may be less than the number of disks that really hit
# the error.
# One way to find this out is to execute explain for adding one disk at
# a time. If each of the explain returns ORA-15029, then it is guaranteed
# that all the disks are added to the disk group. In such a case, we can
# skip the add and try to set appliance mode.
# Otherwise, we must bail out here. But it does not harm to try to set
# appliance mode before bailing out. So we can handle both the cases in
# the same way and avoid individual disk explain.
$skipadd = 1;
}
else
{
# For all other errors, re-throw the exception. The user will retry the
# migration asmcmd. If the user still faces an issue, we are likely to see
# a bug.
asmcmdexceptions::throw("asmcmdexceptions");
}
}
if ($skipadd eq 0)
{
# Step 5: Add the disks
# rebalance power is not specificed
if ($asmcmddisk_migrate_power eq -1)
{
$dgstmt .= " rebalance power 0";
}
else
{
$dgstmt =~ s/power \d+/power 0/;
}
$dgstmt .= " /* _ASM_APPL_MIGRATION */ ";
# Ideally, this must never fail. Explain above should have caught all
# failures.
asmcmdshare_do_stmt($dbh, $dgstmt);
}
# Step 6: Enable appliance mode
$dgstmt = "ALTER DISKGROUP $asmcmddisk_migrate_dg SET ATTRIBUTE ";
$dgstmt .= "'APPLIANCE.MODE'='TRUE' /* _ASM_APPL_MIGRATION */";
asmcmdshare_do_stmt($dbh, $dgstmt);
# Step 7: Rebalance the disk group
# power not specified
if ($asmcmddisk_migrate_power eq -1)
{
$dgstmt = "ALTER DISKGROUP $asmcmddisk_migrate_dg REBALANCE";
asmcmdshare_do_stmt($dbh, $dgstmt);
}
# if power > 0 (note that power may be specified as 0)
elsif ($asmcmddisk_migrate_power)
{
$dgstmt = "ALTER DISKGROUP $asmcmddisk_migrate_dg REBALANCE ";
$dgstmt .= "POWER $asmcmddisk_migrate_power";
asmcmdshare_do_stmt($dbh, $dgstmt);
}
$ok = 1;
return $ok;
}
########
# NAME
# asmcmddisk_process_mount
#
# DESCRIPTION
# This function processes the asmcmd command mount.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_mount
{
my ($dbh) = shift;
my (%args);
my ($qry, $ret);
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# all disk groups?
if (defined($args{'a'}))
{
$qry = "ALTER DISKGROUP ALL MOUNT ";
}
else
{
my $diskgrouplist = join(',', @{$args{'mount'}});
$qry = "ALTER DISKGROUP " . $diskgrouplist . " MOUNT ";
}
# restricted option
if (defined($args{'restrict'}))
{
$qry .= " RESTRICTED";
}
# force option
if (defined($args{'f'}))
{
$qry .= " FORCE";
}
$ret = asmcmdshare_do_stmt($dbh, $qry);
}
########
# NAME
# asmcmddisk_process_umount
#
# DESCRIPTION
# This function processes the asmcmd command umount.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_umount
{
my ($dbh) = shift;
my (%args);
my ($qry, $ret);
my (@dgnames);
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# all disk groups?
if (defined($args{'a'}))
{
$qry = "ALTER DISKGROUP ALL DISMOUNT ";
}
else
{
my $diskgrouplist = join(',', @{$args{'umount'}});
$qry = "ALTER DISKGROUP " . $diskgrouplist . " DISMOUNT ";
}
# force option
if (defined($args{'f'}))
{
$qry .= " FORCE";
}
$ret = asmcmdshare_do_stmt($dbh, $qry);
}
########
# NAME
# asmcmddisk_process_online
#
# DESCRIPTION
# This function processes the asmcmd command online.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_online
{
my ($dbh) = shift;
my (%args);
my ($qry, $ret);
my ($dgname, $fgname, $dname);
my (@error);
my ($sth, $row);
my ($quorum) = "" ;
my ($power);
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# get disk group
$dgname = $args{'G'};
if (defined $args{'q'})
{
$quorum = " QUORUM " ;
}
# get failure group if defined
if (defined $args{'F'})
{
$fgname = $args{'F'};
tr/a-z/A-Z/ for $fgname;
$qry = "ALTER DISKGROUP " . $dgname ;
$qry .= " ONLINE DISKS IN " . $quorum . " FAILGROUP " . $fgname;
}
# else get disk if defined
elsif (defined $args{'D'})
{
$dname = $args{'D'};
tr/a-z/A-Z/ for $dname;
$qry = "ALTER DISKGROUP " . $dgname ;
$qry .= " ONLINE " . $quorum . " DISK " . $dname;
}
elsif (defined $args{'a'})
{
$qry = "ALTER DISKGROUP " . $dgname ;
$qry .= " ONLINE ALL ";
}
# if power argument is specified
if (defined($args{'power'}))
{
$power = $args{'power'};
$qry .= " POWER ".$power;
}
# if wait argument is specified
if (defined($args{'w'}))
{
$qry .= " WAIT";
}
$ret = asmcmdshare_do_stmt($dbh, $qry);
asmcmdshare_print "Diskgroup altered.\n" ;
}
########
# NAME
# asmcmddisk_process_offline
#
# DESCRIPTION
# This function processes the asmcmd command offline.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_offline
{
my ($dbh) = shift;
my (%args);
my ($qry, $ret);
my ($dgname, $fgname, $dname);
my (@error);
my ($sth, $row);
my ($timeout);
my ($quorum) = "" ;
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# get disk group
$dgname = $args{'G'};
$qry = "ALTER DISKGROUP " . $dgname ;
if (defined $args{'q'})
{
$quorum = " QUORUM " ;
}
# always convert parameters to uppercase
# get failure group if defined.
# Either a failgroup or a disk should be provided for the command.
# This condition is checked by the asmcmdparser module.
if (defined $args{'F'})
{
$fgname = $args{'F'};
tr/a-z/A-Z/ for $fgname;
$qry .= " OFFLINE DISKS IN " . $quorum . " FAILGROUP " . $fgname;
}
# get disk if defined
if (defined $args{'D'})
{
$dname = $args{'D'};
tr/a-z/A-Z/ for $dname;
$qry .= " OFFLINE ". $quorum. " DISK " . $dname;
}
# timeout
if (defined($args{'t'}))
{
#$ check format of timeout
$timeout = $args{'t'};
$qry .= " DROP AFTER $timeout";
}
$ret = asmcmdshare_do_stmt($dbh, $qry);
asmcmdshare_print "Diskgroup altered.\n" ;
}
########
# NAME
# asmcmddisk_process_rebal
#
# DESCRIPTION
# This function processes the asmcmd command rebal.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmddisk_process_cmd() calls this function.
########
sub asmcmddisk_process_rebal
{
my ($dbh) = shift;
my (%args);
my ($qry, $ret);
my ($dgname, $fgname, $dname);
my (@error);
my ($power, $wait, $phases, $default, $modify);
my ($dgnumber); #diskgroup number of the disk for which rebal is issued
my ($rbalOngoing); #return value, Y or N if rbal is ongoing for that diskgroup
# Get option parameters, if any.
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# get the disk group name.
($dgname) = @{$args{'rebal'}};
# default option. The command syntax prevents having both default and modify.
$default = $args{'default'} if defined $args{'default'};
# modify option. The command syntax prevents having both default and modify.
$modify = $args{'modify'} if defined $args{'modify'};
# get phases for wait or without option
if (defined($args{'with'}))
{
# make a list, space separated
$phases = join(' ', @{$args{'with'}});
}
elsif (defined($args{'without'}))
{
# make a list, space separated
$phases = join(' ', @{$args{'without'}});
}
# power option
$power = $args{'power'} if defined $args{'power'};
# wait option
$wait = $args{'w'} if defined $args{'w'};
$qry = "ALTER DISKGROUP " . $dgname . " REBALANCE";
$qry .= " MODIFY POWER " if (defined($default));
$qry .= " MODIFY POWER " . $modify if (defined($modify));
$qry .= " WITH " . $phases if (defined($args{'with'}) && defined($phases));
$qry .= " WITHOUT " . $phases if
(defined($args{'without'}) && defined($phases));
$qry .= " POWER " . $power if (defined($power));
$qry .= " WAIT" if (defined($wait));
$ret = asmcmdshare_do_stmt($dbh, $qry);
if (defined ($ret))
{
$dgnumber = asmcmddisk_get_gnum_from_gname($dbh, $dgname);
$rbalOngoing = asmcmdbase::asmcmdbase_is_rbal ($dbh,$dgnumber);
if ($rbalOngoing eq 'Y')
{
asmcmdshare_print "Rebal on progress.\n";
}
elsif ($rbalOngoing eq 'N')
{
asmcmdshare_print "Rebal completed.\n";
}
}
}
########
# NAME
# asmcmddisk_get_fname_from_fnum
#
# DESCRIPTION
# This routine constructs the SQL used to get the file name given the file number
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gnum (IN) - the diskgroup number
# fnum (IN) - the file number
#
#
# RETURNS
# The file name.
########
sub asmcmddisk_get_fname_from_fnum
{
my ($dbh, $gnum, $fnum) = @_;
my ($sth, $qry, $row);
my ($fname); # Disk number return value; see RETURNS above. #
# Get disk number from disk name.
$qry = 'select name from v$asm_alias where file_number=?' .
' and system_created=\'N\' and group_number=?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,$fnum);
$sth->bind_param(2,$gnum);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$fname = $row->{'NAME'};
asmcmdshare_finish($sth);
return $fname;
}
########
# NAME
# asmcmddisk_get_dnum_from_dname
#
# DESCRIPTION
# This routine constructs the SQL used to fetch the disk number of the
# disk that has the name $dname, iff belows to a mounted disk group.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# dname (IN) - the name for the disk for which we need the disk
# number.
#
# RETURNS
# The disk number is a string if the disk is mounted in a dg; undefined
# otherwise.
########
sub asmcmddisk_get_dnum_from_dname
{
my ($dbh, $gnum, $dname) = @_;
my ($sth, $qry, $row);
my ($dnum); # Disk number return value; see RETURNS above. #
# Get disk number from disk name.
$qry = 'select disk_number from v$asm_disk_stat where name = ?' .
' and group_number = ?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,uc($dname),SQL_VARCHAR);
$sth->bind_param(2,$gnum,SQL_INTEGER);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$dnum = $row->{'DISK_NUMBER'};
asmcmdshare_finish($sth);
return $dnum;
}
########
# NAME
# asmcmddisk_get_gnum_from_gname
#
# DESCRIPTION
# This routine constructs the SQL used to fetch the group number of the
# disk group that has the name $gname, iff the disk group is mounted
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gname (IN) - the name for the disk group for which we need the group
# number.
#
# RETURNS
# The diskgroup number is a string if the diskgroup is mounted; undefined
# otherwise.
########
sub asmcmddisk_get_gnum_from_gname
{
my ($dbh, $gname) = @_;
my ($sth, $qry, $row);
my ($gnum); # Disk group number return value. #
# Remove the leading "+" from the disk group name, if any
$gname = substr($gname, 1) if substr($gname, 0, 1) eq "+";
# Get disk group number from disk group name.
$qry = 'select group_number from v$asm_diskgroup_stat where name = ?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,uc($gname),SQL_VARCHAR);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$gnum = $row->{'GROUP_NUMBER'};
asmcmdshare_finish($sth);
return $gnum;
}
########
# NAME
# asmcmddisk_get_dg_ausize
#
# DESCRIPTION
# This routine constructs the SQL used to fetch the AU size of the
# disk group with group number <gnum>.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gnum (IN) - the disk group number
#
# RETURNS
# The AU size is a string if the disk is mounted in a dg; undefined
# otherwise.
########
sub asmcmddisk_get_dg_ausize
{
my ($dbh, $gnum) = @_;
my ($sth, $qry, $row);
my ($ausize);
my (@eargs);
# Get AU size.
$qry = 'select allocation_unit_size from v$asm_diskgroup_stat ' .
'where group_number=?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,$gnum);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$ausize = $row->{'ALLOCATION_UNIT_SIZE'};
asmcmdshare_finish($sth);
# make sure we get/return valid values
@eargs = ($gnum, $gnum, $ausize);
asmcmdshare_assert((defined($ausize) && $ausize > 0), \@eargs);
return $ausize;
}
########
# NAME
# asmcmddisk_get_dsk_secsize
#
# DESCRIPTION
# This routine constructs the SQL used to fetch the sector size of the
# disk group with group number <gnum>.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gnum (IN) - the disk group number
#
# RETURNS
# The AU size is a string if the disk is mounted in a dg; undefined
# otherwise.
########
sub asmcmddisk_get_dsk_secsize
{
my ($dbh, $gnum, $dnum) = @_;
my ($sth, $qry, $row);
my ($secsize);
my (@eargs);
# Get sector size.
$qry = 'select x$kfkid.blksz_kfkid as blksz from x$kfkid, x$kfdsk ' .
'where x$kfkid.idptr_kfkid=x$kfdsk.kfkid_kfdsk and ' .
'x$kfdsk.grpnum_kfdsk = ?' .
' and x$kfdsk.number_kfdsk = ?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,$gnum);
$sth->bind_param(2,$dnum);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$secsize = $row->{'BLKSZ'};
asmcmdshare_finish($sth);
# make sure we get/return valid values
@eargs = ($gnum, $dnum, $secsize);
asmcmdshare_assert((defined($secsize) && $secsize > 0), \@eargs);
return $secsize;
}
########
# NAME
# asmcmddisk_get_inst
#
# DESCRIPTION
# This function queries the gv$instance table to retrieve
# a list of active instances
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# inst_info (OUT) - hash of active instances
#
# RETURNS
# A hash with one entry per instance with the instance name as key
#
# NOTES
########
sub asmcmddisk_get_inst
{
my ($dbh) = @_; # db handle
my ($sth, $row);
my (@what, @from); # contents of select stmt
my (%inst_info); # Hash of instances
push (@from, 'gv$instance');
push (@what, 'inst_id');
$sth = asmcmdshare_do_construct_select($dbh, \@what, \@from);
# Fetch results, store each row in hash
while (defined ($row = asmcmdshare_fetch($sth)))
{
$inst_info{$row->{'INST_ID'}} = $row->{'INST_ID'}
if (defined ($row->{'INST_ID'}));
}
asmcmdshare_finish($sth);
return (%inst_info);
}
########
# NAME
# asmcmddisk_process_help
#
# DESCRIPTION
# This function is the help function for the ASMCMDDISK module.
#
# PARAMETERS
# command (IN) - display the help message for this command.
#
# RETURNS
# 1 if command found; 0 otherwise.
########
sub asmcmddisk_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 (asmcmddisk_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
# asmcmddisk_is_cmd
#
# DESCRIPTION
# This routine checks if a user-entered command is one of the known
# ASMCMD internal commands that belong to the ASMCMDDISK module.
#
# PARAMETERS
# arg (IN) - user-entered command name string.
#
# RETURNS
# True if $arg is one of the known commands, false otherwise.
########
sub asmcmddisk_is_cmd
{
my ($arg) = shift;
return defined ( $asmcmddisk_cmds{ $arg } );
}
########
# NAME
# asmcmddisk_is_wildcard_cmd
#
# DESCRIPTION
# This routine determines if an ASMCMDDISK 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 asmcmddisk_is_wildcard_cmd
{
my ($arg) = shift;
return defined ($asmcmddisk_cmds{ $arg }) &&
(asmcmdshare_get_cmd_wildcard($arg) eq "true" ) ;
}
########
# NAME
# asmcmddisk_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 asmcmddisk module currently supports only lsdsk as a command that
# can run without an ASM instance.
########
sub asmcmddisk_is_no_instance_cmd
{
my ($arg) = shift;
my ($rc);
return 1 unless defined($asmcmddisk_cmds{$arg});
$rc = asmcmdshare_get_cmd_noinst($arg);
if ($rc eq "true")
{
return 1;
}
elsif ($rc eq "undef")
{
return -1;
}
return 0;
}
########
# NAME
# asmcmddisk_parse_int_args
#
# DESCRIPTION
# This routine parses the arguments for flag options for ASMCMDDISK
# 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 ASMCMDDISK internal command.
########
sub asmcmddisk_parse_int_args
{
my ($cmd, $args_ref) = @_;
my (@string);
my ($key);
#include deprecated options if any
if($asmcmdglobal_deprecated_options{ $cmd })
{
foreach my $key(keys %{$asmcmdglobal_deprecated_options{ $cmd }})
{
push(@string, $asmcmdglobal_deprecated_options{$cmd}{$key}[0]);
}
}
# Use asmcmdparser_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. #
asmcmddisk_syntax_error($cmd);
return undef;
}
return 0;
}
########
# NAME
# asmcmddisk_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 ASMCMDDISK 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 asmcmddisk_syntax_error
{
my ($cmd) = shift;
my ($cmd_syntax); # Correct syntax for $cmd. #
my ($succ) = 0;
#display syntax only for commands in this module.
if (asmcmddisk_is_cmd($cmd))
{
$cmd_syntax = asmcmdshare_get_help_syntax($cmd); # Get syntax for $cmd. #
$cmd_syntax = asmcmdshare_trim_str ($cmd_syntax); # Trim blank spaces #
asmcmdshare_printstderr 'usage: ' . $cmd_syntax . "\n";
asmcmdshare_printstderr 'help: help ' . $cmd . "\n";
$succ = 1;
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
}
return $succ;
}
########
# NAME
# asmcmddisk_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 asmcmddisk_get_asmcmd_cmds
{
return asmcmdshare_filter_invisible_cmds(%asmcmddisk_cmds);
}
########
# NAME
# asmcmddisk_process_stamp
#
# DESCRIPTION
# This function stamps the site, failgroup and disk labels to every ASM
# disk matching the provided discovery string. It's a wrapper for the
# command 'kfed op=stamp'
#
# PARAMETERS
# dbh (IN) - initialized instance handle, can be null.
#
# RETURNS
# Null
#
# NOTES
# If no discovery string is provided in command line this function fails.
########
sub asmcmddisk_process_stamp
{
my ($dbh) = @_;
my ($kfed_op, $kfod_op);
my ($sth, $row);
my ($ret);
my ($line);
my ($verlbl) = 1;
my (%args);
my (@path) = ();
my (@kfod_o) = ();
my (@disks) = ();
my (@eargs) = ();
my (@what) = ();
my (@from) = ();
my %memberdisks = ();
push(@path, "$ENV{'ORACLE_HOME'}/bin/");
push(@path, "$ENV{'ORACLE_HOME'}/rdbms/bin/");
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
my ($dscstr) = $args{'dscstr'};
my ($site) = $args{'site'};
my ($failgroup) = $args{'failgroup'};
my ($disklbl) = $args{'disk'};
my ($force) = $args{'f'};
# Check if discovery string is valid
if (!defined($dscstr))
{
# ASMCMD-9534, "Discovery string is not provided."
asmcmdshare_error_msg(9534, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Check if the discovery string points to disks that are managed by AFD or
# ASMLIB - we don't allow stamping disk labels on these disks
if (($dscstr =~ /^AFD:/ || $dscstr =~ /^ORCL:/) && defined($disklbl))
{
# ASMCMD-9540, "Disks are managed by AFD or ASMLIB."
asmcmdshare_error_msg(9540, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
# A failgroup is required if site is defined
if (defined($site) && !defined($failgroup))
{
# ASMCMD-9535, "Failure group label is not provided."
asmcmdshare_error_msg(9535, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Check if site label is valid
if (defined($site) && !asmcmddisk_validate_label($site, 'site'))
{
push(@eargs, $site);
# ASMCMD-9536, "The site label '%s' is invalid."
asmcmdshare_error_msg(9536, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Check if failgroup label is valid
if (defined($failgroup) && !asmcmddisk_validate_label($failgroup))
{
push(@eargs, $failgroup);
# ASMCMD-9537, "The failure group label '%s' is invalid."
asmcmdshare_error_msg(9537, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Check if disk label is valid
if (defined($disklbl) && !asmcmddisk_validate_label($disklbl, 'disk'))
{
push(@eargs, $disklbl);
# ASMCMD-9538, "The disk label '%s' is invalid."
asmcmdshare_error_msg(9538, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Run KFOD to find disks matching the discovery string
# Then, for every disk found, run KFED to stamp the labels
$kfod_op = "op=disks disks=all status=TRUE nohdr=TRUE dscvgroup=TRUE ".
"asm_diskstring=$dscstr";
(@kfod_o) = asmcmdshare_execute_tool("kfod", ".exe", $kfod_op, \@path);
# Check if there was an error running KFOD, if successful store the disks
if ($asmcmdglobal_hash{'utilsucc'} eq 'true')
{
foreach my $line (@kfod_o)
{
my ($state, $disk, $dg_name) = (split('\s', $line))[1, 2, 3];
if (defined($state) && $state =~ /^MEMBER/i && defined($dg_name))
{
$memberdisks{$dg_name} = $disk;
}
push(@disks, $disk);
}
}
else
{
foreach $line (@kfod_o)
{
asmcmdshare_print($line);
}
$asmcmdglobal_hash{'e'} = -1 ;
return;
}
# Check if the disks were discovered by KFOD
if (!scalar(@disks))
{
push(@eargs, $dscstr);
# ASMCMD-9539, "No disks were discovered using the string '%s'."
asmcmdshare_error_msg(9539, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Check if any disk belongs to a mounted diskgroup
if (defined($dbh))
{
push(@what, 'STATE');
push(@from, 'gv$asm_diskgroup');
foreach my $dg_name (keys %memberdisks)
{
my $disk = $memberdisks{$dg_name};
my (@where) = ("NAME='$dg_name'");
$sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where);
$row = asmcmdshare_fetch($sth);
if ($row->{'STATE'} =~ /^MOUNTED/i)
{
# ASMCMD-9541, "Disk %s belongs to mounted diskgroup %s."
push(@eargs, $disk, $dg_name);
asmcmdshare_error_msg(9541, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
}
elsif (scalar(keys %memberdisks) > 0)
{
foreach my $dg_name (keys %memberdisks)
{
# ASMCMD-9542, "Disk %s is member of diskgroup %s and ASMCMD is not
# connected to an instance."
@eargs = ($memberdisks{$dg_name}, $dg_name);
asmcmdshare_error_msg(9542, \@eargs);
}
asmcmdshare_print(
"\n*** WARNING!!! ***\n".
"ASMCMD is not connected to an ASM instance and cannot confirm the ".
"disk is part\nof any mounted disk group.\n".
"It is strongly suggested to connect ASMCMD to an instance and retry the".
" command.\nThe stamping of the labels must be done while all the ASM ".
"instances in the\ncluster have dismounted the diskgroup the disks to ".
"stamp are part of.\nIf any ASM node is accessing the disks while the ".
"labels are being modified,\nyou can lose data.\n".
"Do you want to continue anyway? (yes/no)\n"
);
my $user_input = <STDIN>;
chomp $user_input;
# Continue only if the user types 'yes'
if ($user_input !~ /^yes$/i)
{
asmcmdshare_print("Aborting ASMCMD stamp operation\n");
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
# Run KFED to stamp the labels for each disk discovered
my $extra_params = "";
if (defined($force))
{
$extra_params .= "force=TRUE";
}
if (defined($disklbl))
{
$extra_params .= " dsklbl='$disklbl'";
}
if (defined($failgroup))
{
$extra_params .= " fglbl='$failgroup'";
}
if (defined($site))
{
$extra_params .= " sitelbl='$site'";
}
if (defined($verlbl))
{
$extra_params .= " verlbl='$verlbl'";
}
my (%failing_disks) = ();
foreach my $disk (@disks)
{
$kfed_op = "op=stamp $extra_params";
$kfed_op .= " dev='$disk'";
my (@kfed_o) = asmcmdshare_execute_tool("kfed", ".exe", $kfed_op, \@path);
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
$failing_disks{$disk} = \@kfed_o;
}
}
# Check if KFED failed to stamp any of the disks
if (scalar(keys %failing_disks))
{
foreach my $disk (keys %failing_disks)
{
asmcmdshare_print("Failed to stamp disk $disk\n");
foreach $line (@{$failing_disks{$disk}})
{
asmcmdshare_print($line);
}
}
$asmcmdglobal_hash{'e'} = -1;
return;
}
}
########
# NAME
# asmcmddisk_validate_label
#
# DESCRIPTION
# After the user provides the label as a parameter this function
# verifies if this label is a valid ASM label.
# The type can be:
# 'site' - To validate a site label
#
# PARAMETERS
# label (IN) - label to validate
# type (IN) - label type
#
# RETURNS
# TRUE if the label is valid and FALSE otherwise.
#
# NOTES
# This validates the user provided valid characters for the label
########
sub asmcmddisk_validate_label
{
my ($label, $type) = @_;
my ($valid) = 1;
my ($maxlength) = 30; # Default to failure group label
my ($regex) = "^[A-Z][A-Z0-9_]*\$"; # Regex for disk or failure group labels
my ($lbllength);
if (defined($type) && $type eq 'disk')
{
$maxlength = 23; # Comes from AFD_MAXLABEL in asmlib.h
}
elsif (defined($type) && $type eq 'site')
{
$maxlength = 15;
$regex = "^[A-Z]([A-Z0-9-]?[A-Z0-9])*\$";
}
$label = uc($label);
if ($label !~ /$regex/)
{
$valid = 0;
}
$lbllength = length($label);
if ($lbllength > $maxlength || $lbllength < 1)
{
$valid = 0;
}
return $valid;
}
########
# NAME
# asmcmddisk_process_stamplist
#
# DESCRIPTION
# This function is a wrapper over the command 'kfod label=TRUE'
# to display the site, failgroup and disk labels for all the ASM disks
# matching the discovery string provided.
#
# PARAMETERS
# NONE.
#
# RETURNS
# Null
#
# NOTES
# If no discovery string is provided in command line this function fails.
########
sub asmcmddisk_process_stamplist
{
my ($kfod_op);
my ($ret);
my (%args);
my (%min_col_wid);
my (@path) = ();
my (@kfod_o) = ();
my (@columns) = ();
my (@eargs) = ();
push(@path, "$ENV{'ORACLE_HOME'}/bin/");
push(@path, "$ENV{'ORACLE_HOME'}/rdbms/bin/");
$ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# Check if discovery string is valid
my ($dscstr) = $args{'dscstr'};
my ($site) = $args{'site'};
my ($failgroup) = $args{'failgroup'};
my ($disklbl) = $args{'disk'};
if (!defined($dscstr))
{
# ASMCMD-9534, "Discovery string is not provided."
asmcmdshare_error_msg(9534, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Select what to print in the header
push(@columns, 'Disk-Path');
push(@columns, 'Site') if (defined($site));
push(@columns, 'Failgroup') if (defined($failgroup));
push(@columns, 'Disk-Label') if (defined($disklbl));
# Initialize the min_col_wid hash
foreach (@columns)
{
$min_col_wid{$_} = length($_);
}
# Run KFOD to discover and list available labels
$kfod_op ="op=disks disks=all label=TRUE nohdr=TRUE asm_diskstring=$dscstr";
(@kfod_o) = asmcmdshare_execute_tool("kfod", ".exe", $kfod_op, \@path);
if ($asmcmdglobal_hash{'utilsucc'} ne 'true')
{
foreach my $line (@kfod_o)
{
asmcmdshare_print($line);
}
$asmcmdglobal_hash{'e'} = -1;
return;
}
# Check if the disks were discovered by KFOD
if (!scalar(@kfod_o))
{
push(@eargs, $dscstr);
# ASMCMD-9539, "No disks were discovered using the string '%s'."
asmcmdshare_error_msg(9539, \@eargs);
$asmcmdglobal_hash{'e'} = -1;
return;
}
my (@rows);
foreach my $line (@kfod_o)
{
my (@row) = ();
my ($path, $osite, $ofg, $odsklbl) = (split('\s', $line))[1,2,3,4];
# define variables that catched undefs to avoid warnings
$path = "" if !defined($path);
$osite = "" if !defined($osite);
$ofg = "" if !defined($ofg);
$odsklbl = "" if !defined($odsklbl);
$min_col_wid{'Disk-Path'} = max($min_col_wid{'Disk-Path'}, length($path));
push(@row, $path);
if (defined($site))
{
$min_col_wid{'Site'} = max($min_col_wid{'Site'}, length($osite));
push(@row, $osite);
}
if (defined($failgroup))
{
$min_col_wid{'Failgroup'} = max($min_col_wid{'Failgroup'}, length($ofg));
push(@row, $ofg);
}
if (defined($disklbl))
{
$min_col_wid{'Disk-Label'} = max($min_col_wid{'Disk-Label'},
length($odsklbl));
push(@row, $odsklbl);
}
push(@rows, \@row);
}
# Set the format string
my ($format) = '';
foreach (@columns)
{
$format .= "%-" . $min_col_wid{$_} . "s ";
}
$format .= "\n";
# Print Header
my ($print_string);
$print_string = sprintf($format, @columns);
asmcmdshare_print($print_string);
# Print rows
foreach my $row (@rows)
{
$print_string = sprintf($format, @{$row});
asmcmdshare_print($print_string);
}
}
1;
OHA YOOOO