# Copyright (c) 2004, 2011, 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) # adileepk 04/12/11 - Backport adileepk_lsdsk_failgroup_bug from main # moreddy 01/13/11 - Backport moreddy_bug-8667038 from main # 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_verify_endian # asmcmddisk_verify_hard # asmcmddisk_verify_btype # asmcmddisk_verify_dformat # asmcmddisk_verify_checksum # asmcmddisk_init_disk_attr # asmcmddisk_path_forward # asmcmddisk_lsdsk_init_col_wid # asmcmddisk_process_remap # asmcmddisk_get_dnum_from_dname # asmcmddisk_get_dsk # 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_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 # ############################################################################# package asmcmddisk; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(asmcmddisk_init ); use strict; use Getopt::Long qw(:config no_ignore_case bundling); use Data::Dumper; use asmcmdglobal; use asmcmdshare; use XML::Parser; use Data::Dumper; use List::Util qw[min max]; ####################### 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_FGROUP_TYPE) = 'Failgroup_Type'; our ($ASMCMDDISK_LSDSK_LABEL) = '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; 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'; ####################### ASMCMDDISK Global Variables ###################### my (%asmcmddisk_cmds) = (lsdsk => {wildcard => 'True', flags => {'k'=>'diskGrpInfo', 'statistics'=> 'statistics', 'p'=>'diskGrpDetails', 't'=>'timeStamp', 'discovery'=>'noCache', 'g'=>'global', 'suppressheader'=> 'suppressHeaders', 'I'=>'scanDiskHeaders', 'G=s'=>'diskGroup', 'member'=> 'statusMember', 'candidate'=> 'statusCandidate', 'M'=>'missingDisks'} }, lsod => {wildcard => 'True', flags => {'suppressheader'=> 'suppressHeaders', 'G=s'=>'diskGroup', 'process=s'=> 'filterProcesses'}, no_instance => 'True'}, remap => {no_instance => 'True'}, dropdg => {flags => {'r'=>'recursive', 'f'=>'force'}, no_instance => 'True'}, mkdg => {no_instance => 'True'}, chkdg => {flags => {'repair'=> 'repairDiskgroup'}, no_instance => 'True' }, chdg => {no_instance => 'True'}, mount => {flags => {'a'=>'all', 'restrict'=> 'restrictedMode', 'f'=>'force'}, no_instance => 'True'}, umount => {flags => {'a'=>'all', 'f'=>'force'}, no_instance => 'True' }, online => {flags => {'a'=>'all', 'F=s'=>'failureGroup', 'G=s'=>'diskGroup', 'w'=>'wait', 'D=s'=>'disk'}, no_instance => 'True' }, offline => {flags => {'G=s'=>'diskGroup', 'F=s'=>'failureGroup', 'D=s'=>'disk', 't=s'=>'timeStamp'}, no_instance => 'True' }, rebal => {flags => {'power=s'=>'powerLevel', 'w'=>'wait'}, no_instance => 'True' }, iostat => {no_instance => 'True', flags => {'G=s'=>'diskGroup', 'suppressheader'=> 'suppressHeaders', 'io'=>'inputOutput', 'region'=>'regionInfo', 'e'=>'errorStatistics', 't'=>'timeStamp'} } ); 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); 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); my (%min_col_wid, $print_format, $printf_code, @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 = ' . "'$gname'"); } 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($ARGV[0])) { $dt = $ARGV[0]; 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_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); if (!defined($sth)) { asmcmdshare_trace(1, $DBI::errstr, 'y', 'y'); return; } @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) { 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})) { $io_delta_info{$k} = ($h->{$k} - $v) /$dt; $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; } # 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'}) ) { $printf_code = "printf \"$print_format\", "; @what_print = (); foreach (@what) { my (@a) = split(/[\.\ ]/, $_); my ($colnam) = $a[$#a]; push (@what_print, "\'" . $asmcmddisk_iostat_header{$colnam} . "\'"); } $printf_code .= "(" . join (", ", @what_print) . ")"; eval $printf_code; } #print rows foreach $h (@delta) { $printf_code = "printf \"$print_format\", "; @what_print = (); foreach (@what) { my (@a) = split(/[\.\ ]/, $_); my ($colnam) = $a[$#a]; if(defined ($h->{$colnam})) { push (@what_print, "\'" . $h->{$colnam} . "\'"); } else { push (@what_print, "\'\'"); } } $printf_code .= "(" . join (", ", @what_print) . ")"; eval $printf_code; } # if a loop is specified, sleep the specified time if (defined($dt)) { print "\n"; @io_list_prev = @io_list; eval{ sleep $dt; } } }while (defined($dt) and $asmcmdglobal_hash{'running'} == 1); 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, @tmp_cols); my ($ret); my (@dsk_list); my ($row, $k, $v, $h); my (%min_col_wid, $print_format, $printf_code, @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 (@ARGV > 0) { $disk_pattern = shift(@ARGV); $disk_pattern =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\%,g } if (defined($args{'process'})) { $process_pattern = $args{'process'}; $process_pattern =~ tr/[a-z]/[A-Z]/; $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 =' . $gnum); 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 '" . $process_pattern . "'"); } if (defined($disk_pattern)) { push(@where, "x\$kfklsod.path_kfklsod like '" . $disk_pattern . "'"); } push(@order, 'program'); push(@order, 'path_kfklsod'); push(@order, 'ospid'); #$$$ need to implement -g $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where, \@order); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth); @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'}) ) { $printf_code = "printf \"$print_format\", "; @what_print = (); foreach (@what) { push (@what_print, "\'" . $asmcmddisk_lsod_header{$_} . "\'"); } $printf_code .= "(" . join (", ", @what_print) . ")"; eval $printf_code; } #print rows foreach $h (@dsk_list) { $printf_code = "printf \"$print_format\", "; @what_print = (); foreach (@what) { push (@what_print, "\'" . $h->{$_} . "\'"); } $printf_code .= "(" . join (", ", @what_print) . ")"; eval $printf_code; } } ######## # 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 ($printf_code); # dynamically generated printf() code for eval(). # 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 -d 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 (@ARGV > 0) { $disk_pattern = shift(@ARGV); } # --member and --candidate are mutually exclusive options if(defined ($args{'member'}) && defined ($args{'candidate'})) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } elsif(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 = asmcmddisk_get_dsk($dbh, $gnum, $disk_pattern, $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{'failgroup_type'}" . "s " . "%-$min_col_wid{'library'}" . "s " . "%-$min_col_wid{'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 "; $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. $printf_code = 'printf $row, ('; if (defined ($args{'g'}) || defined ($args{'M'})) { $printf_code .= '$ASMCMDDISK_LSDSK_INST_ID, '; } if (defined ($args{'k'})) { if ($inst) { $printf_code .= '$ASMCMDDISK_LSDSK_TOTAL_MB, $ASMCMDDISK_LSDSK_FREE_MB, $ASMCMDDISK_LSDSK_OS_MB, $ASMCMDDISK_LSDSK_NAME, $ASMCMDDISK_LSDSK_FGROUP, $ASMCMDDISK_LSDSK_FGROUP_TYPE, $ASMCMDDISK_LSDSK_LIBRARY, $ASMCMDDISK_LSDSK_LABEL, $ASMCMDDISK_LSDSK_UDID, $ASMCMDDISK_LSDSK_PRODUCT, $ASMCMDDISK_LSDSK_REDUND, '; } else { $printf_code .= '$ASMCMDDISK_LSDSK_TOTAL_MB, $ASMCMDDISK_LSDSK_NAME, $ASMCMDDISK_LSDSK_FGROUP, '; } } if (defined ($args{'statistics'})) { if ($inst) { $printf_code .= '$ASMCMDDISK_LSDSK_READS, $ASMCMDDISK_LSDSK_WRITES, $ASMCMDDISK_LSDSK_READ_ERRS, $ASMCMDDISK_LSDSK_WRITE_ERRS, $ASMCMDDISK_LSDSK_READ_TIME, $ASMCMDDISK_LSDSK_WRITE_TIME, $ASMCMDDISK_LSDSK_BYTES_READ, $ASMCMDDISK_LSDSK_BYTES_WRITTEN, $ASMCMDDISK_LSDSK_VOTING_FILE, '; } # Nothing to print if not $inst. } if (defined ($args{'p'})) { if ($inst) { $printf_code .= '$ASMCMDDISK_LSDSK_GNUM, $ASMCMDDISK_LSDSK_DNUM, $ASMCMDDISK_LSDSK_INCARN, $ASMCMDDISK_LSDSK_MOUNTSTAT, $ASMCMDDISK_LSDSK_HEADERSTAT, $ASMCMDDISK_LSDSK_MODESTAT, $ASMCMDDISK_LSDSK_STATE, '; } else { $printf_code .= '$ASMCMDDISK_LSDSK_DNUM, $ASMCMDDISK_LSDSK_DISKGROUP, $ASMCMDDISK_LSDSK_HEADERSTAT, '; } } if (defined ($args{'t'})) { if ($inst) { $printf_code .= '$ASMCMDDISK_LSDSK_CREATE_DATE, $ASMCMDDISK_LSDSK_MOUNT_DATE, $ASMCMDDISK_LSDSK_REPAIR_TIMER, '; } else { $printf_code .= '$ASMCMDDISK_LSDSK_CREATE_DATE, $ASMCMDDISK_LSDSK_MOUNT_DATE, '; } } # Always display the path. $printf_code .= '$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)) { eval($printf_code); asmcmdshare_trace(1, $@, 'y', 'y') if $@; } # 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. $printf_code = 'printf $row, ('; if (defined ($args{'g'}) ||defined ($args{'M'})) { $printf_code .= q|$disk_list[$i]->{'inst_id'}, |; } if (defined ($args{'k'})) { if ($inst) { $printf_code .= q|$disk_list[$i]->{'total_mb'}, $disk_list[$i]->{'free_mb'}, $disk_list[$i]->{'os_mb'}, $disk_list[$i]->{'name'}, $disk_list[$i]->{'failgroup'}, $disk_list[$i]->{'failgroup_type'}, $disk_list[$i]->{'library'}, $disk_list[$i]->{'label'}, $disk_list[$i]->{'udid'}, $disk_list[$i]->{'product'}, $disk_list[$i]->{'redundancy'}, |; } else { $printf_code .= q|$disk_list[$i]->{'total_mb'}, $disk_list[$i]->{'name'}, $disk_list[$i]->{'failgroup'}, |; } } if (defined ($args{'statistics'})) { if ($inst) { $printf_code .= q|$disk_list[$i]->{'reads'}, $disk_list[$i]->{'writes'}, $disk_list[$i]->{'read_errs'}, $disk_list[$i]->{'write_errs'}, $disk_list[$i]->{'read_time'}, $disk_list[$i]->{'write_time'}, $disk_list[$i]->{'bytes_read'}, $disk_list[$i]->{'bytes_written'}, $disk_list[$i]->{'voting_file'}, |; } # Nothing to print if not $inst. } if (defined ($args{'p'})) { if ($inst) { $printf_code .= q|$disk_list[$i]->{'group_number'}, $disk_list[$i]->{'disk_number'}, $disk_list[$i]->{'incarnation'}, $disk_list[$i]->{'mount_status'}, $disk_list[$i]->{'header_status'}, $disk_list[$i]->{'mode_status'}, $disk_list[$i]->{'state'}, |; } else { $printf_code .= q|$disk_list[$i]->{'disk_number'}, $disk_list[$i]->{'group_name'}, $disk_list[$i]->{'header_status'}, |; } } if (defined ($args{'t'})) { if ($inst) { $printf_code .= q|$disk_list[$i]->{'create_date'}, $disk_list[$i]->{'mount_date'}, $disk_list[$i]->{'repair_timer'}, |; } else { $printf_code .= q|$disk_list[$i]->{'create_date'}, $disk_list[$i]->{'mount_date'}, |; } } # Always show path. $printf_code .= q|$disk_list[$i]->{'path'})|; # Now evaluate the printf() expression. next if (defined ($membership_str) && $membership_str !~ $disk_list[$i]->{'header_status'}); eval($printf_code); asmcmdshare_trace(1, $@, 'y', 'y') if $@; # 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 ){ exit 2; } if ( $disk_types{'MEMBER'} == 0 && $disk_types{'CANDIDATE'} > 0 ){ exit 1; } if ( $disk_types{'MEMBER'} > 0 && $disk_types{'CANDIDATE'} == 0 ){ exit 0; } exit -1; } } ######## # NAME # asmcmddisk_scan_dsk # # DESCRIPTION # This function scans the header information on a set of disks # identified by 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 ($kfed); # Path to kfed stand-alone tool. # my ($kfod, $kfod_exec); 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; } # Form path to kfed executable. $kfed = "$ENV{'ORACLE_HOME'}/bin/kfed"; # Make sure the kfed binary exists and can be executed. if (! -x $kfed) { # If not, try at a second locaction. $kfed = "$ENV{'ORACLE_HOME'}/rdbms/bin/kfed"; if (! -x $kfed) { @eargs = ($kfed); asmcmdshare_error_msg(9374, \@eargs); return; } } # Untaint $kfed. Match everything except newlines, carriage returns, # and tabs $kfed =~ /([^\n^\r^\t]+)/; $kfed = $1; # Form path to kfod executable. $kfod = "$ENV{'ORACLE_HOME'}/bin/kfod"; # Make sure the kfed binary exists and can be executed. if (! -x $kfod) { # If not, try at a second locaction. $kfod = "$ENV{'ORACLE_HOME'}/rdbms/bin/kfod"; if (! -x $kfod) { @eargs = ($kfod); asmcmdshare_error_msg(9374, \@eargs); return; } } # Untaint $kfed. Match everything except newlines, carriage returns, # and tabs $kfod =~ /([^\n^\r^\t]+)/; $kfod = $1; # 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; # discover the disks with kfod $kfod_exec = "$kfod a='$disk_pattern' di=all _asm_a=FALSE n=TRUE op=DISKS"; # untaint kfod exec $kfod_exec =~ /([^\n^\r^\t]+)/; $kfod_exec = $1; asmcmdshare_trace(3, "NOTE: Discovering disks using kfod..", 'n', 'n'); eval { $buf = `$kfod_exec`; }; if ($@ ne '') { # The require statement failed. Quit lsdsk with error. @eargs = ($kfod_exec, $@); asmcmdshare_error_msg(9375, \@eargs); last; } @path_list=(); @lines = split (/\n/, $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_exec); # kfed execution 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_exec = "$kfed 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'); eval { $buf = `$kfed_exec`; }; if ($@ ne '') { # The require statement failed. Quit lsdsk with error. @eargs = ($kfed_exec, $@); 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 =~ /^KFED-00303/) { next; } # Split output into an array of lines of output. @lines = split (/\n/, $buf); # Initialize all fields of the hash to avoid Perl errors. %disk_attr = asmcmddisk_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 total MB of the disk. $au_size = $lines[32]; $au_size =~ s/^kfdhdb.ausize\:\s+(\d+?)\s\;.+/$1/; $au_size = $1; $num_aus = $lines[34]; $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[26], $lines[27]); # Get the month time stamp. $disk_attr{'mount_date'} = asmcmddisk_convert_date($lines[28], $lines[29]); } # 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_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 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_verify_endian # # DESCRIPTION # Given a string of binary digits , 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 , 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 equals ; 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 , 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 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 is correct. # # PARAMETERS # disk_dformat (IN) - disk format # # RETURNS # TRUE if 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_init_disk_attr # # DESCRIPTION # Declares and initializes a disk fields hash. # # PARAMETERS # # # RETURNS # An initialized disk fields hash. # ######## sub asmcmddisk_init_disk_attr { my (%dsk_info); # Initialize elements to null strings so that they are defined and # don't trigger Perl warnings if the fixed view does not return # any contents for a particular column. $dsk_info{'inst_id'} = ''; $dsk_info{'group_number'} = ''; $dsk_info{'disk_number'} = ''; $dsk_info{'incarnation'} = ''; $dsk_info{'mount_status'} = ''; $dsk_info{'header_status'} = ''; $dsk_info{'mode_status'} = ''; $dsk_info{'state'} = ''; $dsk_info{'redundancy'} = ''; $dsk_info{'library'} = ''; $dsk_info{'os_mb'} = ''; $dsk_info{'total_mb'} = ''; $dsk_info{'free_mb'} = ''; $dsk_info{'name'} = ''; $dsk_info{'failgroup'} = ''; $dsk_info{'label'} = ''; $dsk_info{'path'} = ''; $dsk_info{'udid'} = ''; $dsk_info{'product'} = ''; $dsk_info{'create_date'} = ''; $dsk_info{'mount_date'} = ''; $dsk_info{'repair_timer'} = ''; $dsk_info{'reads'} = ''; $dsk_info{'writes'} = ''; $dsk_info{'read_errs'} = ''; $dsk_info{'write_errs'} = ''; $dsk_info{'read_time'} = ''; $dsk_info{'write_time'} = ''; $dsk_info{'bytes_read'} = ''; $dsk_info{'bytes_written'} = ''; $dsk_info{'voting_file'} = ''; $dsk_info{'group_name'} = ''; return %dsk_info; } ######## # 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 = asmcmddisk_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->{'failgroup_type'}= length($ASMCMDDISK_LSDSK_FGROUP_TYPE); $min_col_wid_ref->{'label'} = length($ASMCMDDISK_LSDSK_LABEL); $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; # Check if number of non-option parameters are correct: must be exactly 3. if (@ARGV != 3) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # 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; } $gname = shift (@ARGV); # Get group name parameter. # $dname = shift (@ARGV); # Get disk name parameter. # $range = shift (@ARGV); # Get block range parameter. # $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=' . $gnum . ' and number_kfdat=' . $dnum . ' and aunum_kfdat=' . $aunum; $sth = asmcmdshare_do_select($dbh, $qry); $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=' . $gnum . ' and number_kffil=' . $fnum; $sth = asmcmdshare_do_select($dbh, $qry); $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); # Display $DBI::errstr if do_stmt failed. Recording ASMCMD error 8101 # simply prints $DBI::errstr. Use 8101 for non-select SQL statements # that fail. asmcmdshare_error_msg(8101, undef) unless (defined ($ret)); } # Print error 9373 if any physical blocks failed to map to an ASM file. */ if ($map_failed) { asmcmdshare_error_msg(9373, undef); } else { print STDOUT "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'})); if ($force == 1 && $contents == 0) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } if (@ARGV != 1) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dgname = $ARGV[0]; $qry = 'DROP DISKGROUP ' . $dgname . ' '; if ($contents){ if ($force){ $qry .= 'FORCE '; } $qry .= 'INCLUDING CONTENTS '; } $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); $mode = $asmcmdglobal_hash{'mode'}; if ( $mode eq 'n' and !defined($ret)) { exit -1; } return; } ######## # 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 | # +-------+-------+-------+-------+-------+ # dg |err | | | | # F +-------+-------+-------+-------+-------+ # R fg |err |err | |err | # O +-------+-------+-------+-------+-------+ # M dsk |err |err |err |err | # +-------+-------+-------+-------+-------+ # a |err |err |err |err | # +-------+-------+-------+-------+-------+ # ######## sub asmcmddisk_mkdg_start { my ($expat, $element, %attrs) = @_; my (@eargs); if ($element eq 'dg') { my ($name, $redun); 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 "; } @asmcmddisk_parser_disks = (); @asmcmddisk_parser_attrs = (); } elsif ($element eq 'fg') { my ($name); 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'}; $dgstmt .= " failgroup " . $name . " "; } @asmcmddisk_parser_disks = (); } elsif ($element eq 'dsk') { my ($string, $name, $size, $force, $clause); 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; } 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"; } push (@asmcmddisk_parser_disks, $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' || $element eq 'dg') { if ($element eq 'fg' && $#asmcmddisk_parser_disks == -1) { my @eargs = ($element); asmcmdshare_error_msg(9398, \@eargs); $xml_error = 1; return; } if (@asmcmddisk_parser_disks > 0) { $dgstmt .= " disk " . join(', ', @asmcmddisk_parser_disks); @asmcmddisk_parser_disks = (); } } if ($element eq 'dg') { if (@asmcmddisk_parser_attrs > 0) { $dgstmt .= " attribute " . join(', ', @asmcmddisk_parser_attrs); @asmcmddisk_parser_attrs = (); } } } ######## # 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 (@ARGV < 1) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } if (-r $ARGV[0]) { $file = $ARGV[0]; } else { $string_args = join (" ", @ARGV); } @asmcmddisk_parser_state = (); $dgstmt = ""; # specify the handler callbacks $parser = XML::Parser->new(Handlers =>{Start => \&asmcmddisk_mkdg_start, End => \&asmcmddisk_mkdg_end }, ErrorContext => 5 ); eval { if (defined($file)) { $parser->parsefile($file); } else { $parser->parse($string_args); } }; if ($@) { my (@msg, $err); $err = $@; @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 ($xml_error == 1) { return; } # Run SQL. # $ret = asmcmdshare_do_stmt($dbh, $dgstmt); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); } ######## # 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 if (!defined($ARGV[0])) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dgname = $ARGV[0]; $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_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); if (defined ($ret)) { print "Diskgroup altered.\n" ; } } ######## # 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 | # +-------+-------+-------+-------+-------+-------+-------+ # chdg |err | | | |err |err | # +-------+-------+-------+-------+-------+-------+-------+ # add |err |err |err |err | | | # F +-------+-------+-------+-------+-------+-------+-------+ # R drop |err |err |err |err | | | # O +-------+-------+-------+-------+-------+-------+-------+ # M rsz |err |err |err |err | | | # +-------+-------+-------+-------+-------+-------+-------+ # fg |err |err |err |err |err | | # +-------+-------+-------+-------+-------+-------+-------+ # dsk |err |err |err |err |err |err | # +-------+-------+-------+-------+-------+-------+-------+ # ######## sub asmcmddisk_chdg_start { my ($expat, $element, %attrs) = @_; my (@eargs); if ($element eq 'chdg') { my ($dgname, $power); if ($#asmcmddisk_parser_state != -1) { asmcmdshare_error_msg(9391, undef); $xml_error = 1; return; } if (!defined($attrs{'name'})) { @eargs = ('name'); asmcmdshare_error_msg(9392, \@eargs); $xml_error = 1; return; } push (@asmcmddisk_parser_state, $element); $dgname = $attrs{'name'}; $dgstmt = 'alter diskgroup ' . $dgname . ' '; if (defined($attrs{'power'})) { $power = $attrs{'power'}; $dgstmt .= "rebalance power " . $power . " "; } } elsif ($element eq 'add' || $element eq 'drop') { 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; } $dgstmt .= ' ' . $element . ' '; 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; } $dgstmt .= ' ' . $element . ' '; if (defined($attrs{'size'})) { $size = $attrs{'size'}; $dgstmt .= " all size " . $size; } push (@asmcmddisk_parser_state, $element); @asmcmddisk_parser_disks = (); } elsif ($element eq 'fg') { # get failure group attributes %asmcmddisk_parser_fg=(); 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')) { asmcmdshare_error_msg(9391, undef); $xml_error = 1; return; } if (!defined($attrs{'name'})) { 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'}; } 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') ) { asmcmdshare_error_msg(9391, undef); $xml_error = 1; return; } 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'}; } push (@asmcmddisk_parser_state, $element); push (@asmcmddisk_parser_disks, \%dsk_attr); } 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); # if element == dsk and father != fg, operate if ( $element eq 'dsk' && 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]; # 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'})&& $dsk->{'force'} eq 'true') { $dgstmt .= " force "; } } # drop operation elsif ($oper eq 'drop') { $dgstmt .= " disk " . $dsk->{'name'} . " "; if (defined($dsk->{'force'})) { if ($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 ($disk_string); $oper = $asmcmddisk_parser_state[$#asmcmddisk_parser_state - 1]; $fgname = $asmcmddisk_parser_fg{'name'}; if (!defined($oper) || !defined($fgname)) { asmcmdshare_error_msg(9391, undef); $xml_error = 1; return; } # add operation if ($oper eq 'add') { $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 ($_->{'force'} eq 'true') { $disk_string .= " force "; } } push (@dsks, $disk_string); } $dgstmt .= join (", ", @dsks) . " "; } # drop operation elsif($oper eq 'drop') { $dgstmt .= " disks in failgroup " . $fgname . " "; if (defined($asmcmddisk_parser_fg{'force'})) { if ( $asmcmddisk_parser_fg{'force'} eq 'true' ) { $dgstmt .= "force "; } } } # resize operation elsif($oper eq 'resize') { $dgstmt .= " disks in failgroup " . $fgname; if (defined($asmcmddisk_parser_fg{'size'})) { $dgstmt .= " size " . $asmcmddisk_parser_fg{'size'} . " "; } } @asmcmddisk_parser_disks = (); } pop @asmcmddisk_parser_state; } ######## # 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 (@ARGV < 1) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } if (-r $ARGV[0]) { $file = $ARGV[0]; } else { $string_args = join (" ", @ARGV); } @asmcmddisk_parser_state = (); $dgstmt = ""; # specify the handler callbacks $parser = XML::Parser->new(Handlers =>{Start => \&asmcmddisk_chdg_start, End => \&asmcmddisk_chdg_end,}); eval{ if (defined($file)) { $parser->parsefile($file); } else { $parser->parse($string_args); } }; if ($@) { @eargs = ($@); asmcmdshare_error_msg(9395, \@eargs); return; } if ($xml_error == 1) { return; } if (!defined($dgstmt) || $dgstmt eq '') { asmcmdshare_error_msg(9391, undef); return; } # Run SQL. # $ret = asmcmdshare_do_stmt($dbh, $dgstmt); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); if (defined ($ret)) { print "Diskgroup altered.\n" ; } } ######## # 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); 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'})) { push(@dgnames, "ALL"); } else { # check for a disk group if (!defined($ARGV[0])) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } foreach (@ARGV) { push (@dgnames, $_); } } $qry = "ALTER DISKGROUP " . join(',', @dgnames) . " MOUNT "; # restricted option if (defined($args{'restrict'})) { $qry .= " RESTRICTED"; } # force option if (defined($args{'f'})) { $qry .= " FORCE"; } $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); } ######## # 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'})) { push(@dgnames, "ALL"); } else { # check for a disk group if (!defined($ARGV[0])) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } foreach (@ARGV) { push (@dgnames, $_); } } $qry = "ALTER DISKGROUP " . join(',', @dgnames) . " DISMOUNT "; # force option if (defined($args{'f'})) { $qry .= " FORCE"; } $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); } ######## # 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); # Get option parameters, if any. $ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # get disk group if (!defined($args{'G'})) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dgname = $args{'G'}; # get failure group if defined if (defined $args{'F'}) { $fgname = $args{'F'}; tr/a-z/A-Z/ for $fgname; } # get disk if defined if (defined $args{'D'}) { $dname = $args{'D'}; tr/a-z/A-Z/ for $dname; } if ( defined($fgname) and defined($dname) ) { asmcmdshare_error_msg(9393, undef); asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } if (defined($dname)) { $qry = "ALTER DISKGROUP " . $dgname ; $qry .= " ONLINE DISK " . $dname; } elsif (defined($fgname)) { $qry = "ALTER DISKGROUP " . $dgname ; $qry .= " ONLINE DISKS IN FAILGROUP " . $fgname; } else { $qry = "ALTER DISKGROUP " . $dgname ; $qry .= " ONLINE ALL "; } # if wait argument is specified if (defined($args{'w'})) { $qry .= " WAIT"; } if (!defined($qry)) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); if (defined ($ret)) { 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); # Get option parameters, if any. $ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # get disk group if (!defined($args{'G'})) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dgname = $args{'G'}; # always convert parameters to uppercase # get failure group if defined if (defined $args{'F'}) { $fgname = $args{'F'}; tr/a-z/A-Z/ for $fgname; } # get disk if defined if (defined $args{'D'}) { $dname = $args{'D'}; tr/a-z/A-Z/ for $dname; } if ( defined($fgname) and defined($dname) ) { asmcmdshare_error_msg(9394, undef); asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } if (defined($dname)) { $qry = "ALTER DISKGROUP " . $dgname ; $qry .= " OFFLINE DISK " . $dname; } elsif (defined($fgname)) { $qry = "ALTER DISKGROUP " . $dgname ; $qry .= " OFFLINE DISKS IN FAILGROUP " . $fgname; } # timeout if (defined($args{'t'})) { #$ check format of timeout $timeout = $args{'t'}; $qry .= " DROP AFTER $timeout"; } if (!defined($qry)) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); if (defined ($ret)) { 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); 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); # check for a disk group if (!defined($ARGV[0])) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dgname = $ARGV[0]; # power option $power = $args{'power'} if defined $args{'power'}; # wait option $wait = $args{'w'} if defined $args{'w'}; $qry = "ALTER DISKGROUP " . $dgname . " REBALANCE"; $qry .= " POWER " . $power if (defined($power)); $qry .= " WAIT" if (defined($wait)); $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); if (defined ($ret)) { $dgnumber = asmcmddisk_get_gnum_from_gname($dbh, $dgname); $rbalOngoing = asmcmdbase::asmcmdbase_is_rbal ($dbh,$dgnumber); if ($rbalOngoing eq 'Y') { print "Rebal on progress.\n"; } elsif ($rbalOngoing eq 'N') { 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=' . $fnum . ' and system_created=\'N\' and group_number=' . $gnum; $sth = asmcmdshare_do_select($dbh, $qry); $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=\'' . uc($dname) . '\' and group_number=' . $gnum; $sth = asmcmdshare_do_select($dbh, $qry); $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=\'' . uc($gname) . '\''; $sth = asmcmdshare_do_select($dbh, $qry); $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 . # # 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=' . $gnum; $sth = asmcmdshare_do_select($dbh, $qry); $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 . # # 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 =' . $gnum . ' and x$kfdsk.number_kfdsk ='. $dnum; $sth = asmcmdshare_do_select($dbh, $qry); $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_dsk # # DESCRIPTION # This function queries the (g)v$asm_disk(_stat) tables to retrieve # disk information based on user-specified query criteria. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # group (IN) - optional: disk group number to limit results by group. # disk_pattern (IN) - optional: search pattern to limit results. # discovery (IN) - 1 queries from non-stat view; 0 queries from _stat view. # global (IN) - 1 queries from the gv$ global view, 0 from v$ local view. # # RETURNS # An array of hashes references. Each hash reference represents one # row of results. Each element in the hash represents the value # for a particular column of the view. # # NOTES ######## sub asmcmddisk_get_dsk { my ($dbh, $group, $disk_pattern, $discovery, $global, $missing, $found_hash) = @_; my ($sth, $row); my (@dsk_list); # The return array of hashes; see RETURNS above. # my ($view); my (@what , @from, @where, @order); # Change all allow wild card characters to '%'. $disk_pattern =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\%,g if (defined ($disk_pattern)); # If Oracle Database version is less than 10gR2, then always do # discovery, because the *_stat views are not available in 10gR1. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) < 0 ) { $discovery = 1; } if ($missing) # for missing disks we must use gv$asm_disk { # and exclude member disks push (@from, 'gv$asm_disk'); push (@where, "(gv\$asm_disk.header_status = 'CANDIDATE' or gv\$asm_disk.header_status = 'PROVISIONED' or gv\$asm_disk.header_status = 'FORMER')"); } elsif ($discovery && $global) { push (@from, 'gv$asm_disk'); } elsif ($discovery && !$global) { push(@from, 'v$asm_disk'); } elsif (!$discovery && $global) { push (@from, 'gv$asm_disk_stat'); } else { push (@from, 'v$asm_disk_stat'); } if (defined($group)) { push (@where, 'group_number=' . $group); } if (defined($disk_pattern)) { push (@where, 'path like \'' . $disk_pattern . '\''); } push(@what, '*'); # select all rows $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where, \@order); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth); # Fetch results row by row and storeeach row in %dsk_info, and reference # each %dsk_info in @dsk_list. while (defined ($row = asmcmdshare_fetch($sth))) { my (%dsk_info); # Allocate fresh hash for next row. # # Initialize elements to null strings so that they are defined and # don't trigger Perl warnings if the fixed view does not return # any contents for a particular column. %dsk_info = asmcmddisk_init_disk_attr(); # Assign only if defined. $dsk_info{'inst_id'} = $row->{'INST_ID'} if (defined ($row->{'INST_ID'})); $dsk_info{'group_number'} = $row->{'GROUP_NUMBER'} if (defined ($row->{'GROUP_NUMBER'})); $dsk_info{'disk_number'} = $row->{'DISK_NUMBER'} if (defined ($row->{'DISK_NUMBER'})); $dsk_info{'incarnation'} = $row->{'INCARNATION'} if (defined ($row->{'INCARNATION'})); $dsk_info{'mount_status'} = $row->{'MOUNT_STATUS'} if (defined ($row->{'MOUNT_STATUS'})); $dsk_info{'header_status'} = $row->{'HEADER_STATUS'} if (defined ($row->{'HEADER_STATUS'})); $dsk_info{'mode_status'} = $row->{'MODE_STATUS'} if (defined ($row->{'MODE_STATUS'})); $dsk_info{'state'} = $row->{'STATE'} if (defined ($row->{'STATE'})); $dsk_info{'redundancy'} = $row->{'REDUNDANCY'} if (defined ($row->{'REDUNDANCY'})); $dsk_info{'library'} = $row->{'LIBRARY'} if (defined ($row->{'LIBRARY'})); $dsk_info{'os_mb'} = $row->{'OS_MB'} if (defined ($row->{'OS_MB'})); $dsk_info{'total_mb'} = $row->{'TOTAL_MB'} if (defined ($row->{'TOTAL_MB'})); $dsk_info{'free_mb'} = $row->{'FREE_MB'} if (defined ($row->{'FREE_MB'})); $dsk_info{'name'} = $row->{'NAME'} if (defined ($row->{'NAME'})); $dsk_info{'failgroup'} = $row->{'FAILGROUP'} if (defined ($row->{'FAILGROUP'})); $dsk_info{'label'} = $row->{'LABEL'} if (defined ($row->{'LABEL'})); $dsk_info{'path'} = $row->{'PATH'} if (defined ($row->{'PATH'})); $dsk_info{'udid'} = $row->{'UDID'} if (defined ($row->{'UDID'})); $dsk_info{'product'} = $row->{'PRODUCT'} if (defined ($row->{'PRODUCT'})); $dsk_info{'create_date'} = $row->{'CREATE_DATE'} if (defined ($row->{'CREATE_DATE'})); $dsk_info{'mount_date'} = $row->{'MOUNT_DATE'} if (defined ($row->{'MOUNT_DATE'})); $dsk_info{'repair_timer'} = $row->{'REPAIR_TIMER'} if (defined ($row->{'REPAIR_TIMER'})); $dsk_info{'reads'} = $row->{'READS'} if (defined ($row->{'READS'})); $dsk_info{'writes'} = $row->{'WRITES'} if (defined ($row->{'WRITES'})); $dsk_info{'read_errs'} = $row->{'READ_ERRS'} if (defined ($row->{'READ_ERRS'})); $dsk_info{'write_errs'} = $row->{'WRITE_ERRS'} if (defined ($row->{'WRITE_ERRS'})); $dsk_info{'read_time'} = $row->{'READ_TIME'} if (defined ($row->{'READ_TIME'})); $dsk_info{'write_time'} = $row->{'WRITE_TIME'} if (defined ($row->{'WRITE_TIME'})); $dsk_info{'bytes_read'} = $row->{'BYTES_READ'} if (defined ($row->{'BYTES_READ'})); $dsk_info{'bytes_written'} = $row->{'BYTES_WRITTEN'} if (defined ($row->{'BYTES_WRITTEN'})); $dsk_info{'voting_file'} = $row->{'VOTING_FILE'} if (defined ($row->{'VOTING_FILE'})); $dsk_info{'failgroup_type'} = $row->{'FAILGROUP_TYPE'} if (defined ($row->{'FAILGROUP_TYPE'})); push (@dsk_list, \%dsk_info); # If missing disk list requested, we create a hash of found disks keyed # by instance name and path. Later (in asmcmddisk_missing) is we cross # check this using the disk list and the active instances. if($missing) { my ($key); $key = $dsk_info{'path'} . $dsk_info{'inst_id'}; $found_hash->{$key} = 1 if(!defined($found_hash->{$key})); } } asmcmdshare_finish($sth); return (@dsk_list); } ######## # 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); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth); # 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); 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 }) && defined ($asmcmddisk_cmds{ $arg }{ wildcard }); } ######## # 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 # True if $arg is a command that can run without an ASM instance # or does not exist, false otherwise. # # 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; return !defined ($asmcmddisk_cmds{ $arg }) || !defined ($asmcmddisk_cmds{ $arg }{ no_instance }); } ######## # 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); #build the list of options to parse using GetOptions if($asmcmddisk_cmds{ $cmd }{ flags }) { foreach $key(keys %{$asmcmddisk_cmds{ $cmd }{ flags }}) { push(@string, $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 GetOptions() from the Getopt::Long package to parse arguments for # internal commands. These arguments are stored in @ARGV. if (!GetOptions($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 # if (defined ($cmd_syntax)) { print STDERR 'usage: ' . $cmd_syntax . "\n"; print STDERR '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_print_cmds(sort(keys %asmcmddisk_cmds)); } 1;