# Copyright (c) 2004, 2009, 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) # 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_error_msg # asmcmddisk_display_msg # asmcmddisk_signal_exception # asmcmddisk_get_cmd_desc # 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_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'); ####################### ASMCMDDISK Global Variables ###################### my (%asmcmddisk_cmds) = (lsdsk => {wildcard => 'True', flags => {'k'=>'diskGrpInfo', 'statistics'=> 'statistics', 'p'=>'diskGrpDetails', 't'=>'timeStamp', 'discovery'=>'noCache', 'g'=>'global', 'H'=>'supressHeaders', 'I'=>'scanDiskHeaders', 'G=s'=>'diskGroup', 'member'=> 'statusMember', 'candidate'=> 'statusCandidate', 'M'=>'missingDisks'} }, lsod => {wildcard => 'True', flags => {'H'=>'supressHeaders', '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'=>'timeStamp'}, no_instance => 'True' }, rebal => {flags => {'power=s'=>'powerLevel', 'w'=>'wait'}, no_instance => 'True' }, iostat => {no_instance => 'True', flags => {'G=s'=>'diskGroup', 'H'=>'supressHeaders', '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); push (@asmcmdglobal_error_message_callbacks, \&asmcmddisk_error_msg); push (@asmcmdglobal_signal_exception_callbacks, \&asmcmddisk_signal_exception); %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); # Get option parameters, if any. $ret = asmcmddisk_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); 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(8301, \@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) { my ($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)) { warn "$DBI::errstr\n"; 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})) { $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{'H'}) ) { $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]; push (@what_print, "\'" . $h->{$colnam} . "\'"); } $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); if (defined($args{'process'})) { $process_pattern = $args{'process'}; $process_pattern =~ tr/[a-z]/[A-Z]/; } 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(9397, \$eargs); return; } } # print headers? $headers = defined($args{'H'}); 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); warn "$DBI::errstr\n" 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{'H'}) ) { $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(9397, \$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) { # 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{'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_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 -H and if we have at least one row of # results. if (!defined ($args{'H'}) && (@disk_list > 0)) { eval($printf_code); warn $@ 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]->{'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); warn $@ 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) { asmcmdshare_error_msg(9374, \$kfed); 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) { asmcmdshare_error_msg(9374, \$kfod); 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; 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. 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->{'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); warn "$DBI::errstr\n" 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); return; } push (@asmcmddisk_parser_state, $element); if (defined($attrs{'name'})) { $name = $attrs{'name'}; } else { asmcmdshare_error_msg(9391, undef); 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); 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); 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); 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 { asmcmdshare_error_msg(9390, \$element); 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); 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) = ''; # 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"; asmcmdshare_error_msg(9395, \$err); return; } # Run SQL. # $ret = asmcmdshare_do_stmt($dbh, $dgstmt); warn "$DBI::errstr\n" 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); warn "$DBI::errstr\n" unless defined ($ret); } ######## # NAME # asmcmddisk_chdg_start # # DESCRIPTION # This function processes the start of xml tags in chdg. # # PARAMETERS # expat (IN) - expat parser object # element (IN) - tag element name # attrs (IN) - tag element attributes # # RETURNS # Null. # # NOTES # Only asmcmddisk_process_chdg() calls this function. ######## # # transition map # # TO # |chdg |add |drop |rsz |fg |dsk | # +-------+-------+-------+-------+-------+-------+-------+ # 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); return; } if (!defined($attrs{'name'})) { my $eargs = 'name'; asmcmdshare_error_msg(9392, \$eargs); 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); 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); 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); return; } if (!defined($attrs{'name'})) { asmcmdshare_error_msg(9391, undef); 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); 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 { asmcmdshare_error_msg(9390, \$element); 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); # 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, ''); return; } # add operation if ($oper eq 'add') { $dgstmt .= " failgroup " . $fgname . " disk "; if($#asmcmddisk_parser_disks == -1) { my $eargs = $element; asmcmdshare_error_msg(9398, \$eargs); 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) = ''; # 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 ($@) { asmcmdshare_error_msg(9395, \$@); return; } if (!defined($dgstmt) || $dgstmt eq '') { asmcmdshare_error_msg(9391, ''); return; } # Run SQL. # $ret = asmcmdshare_do_stmt($dbh, $dgstmt); warn "$DBI::errstr\n" unless defined ($ret); } ######## # 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 ($dgname); # 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'})) { $dgname = "ALL"; } else { # check for a disk group if (!defined($ARGV[0])) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dgname = $ARGV[0]; } $qry = "ALTER DISKGROUP " . $dgname . " MOUNT "; # restricted option if (defined($args{'restrict'})) { $qry .= " RESTRICTED"; } # force option if (defined($args{'f'})) { $qry .= " FORCE"; } $ret = asmcmdshare_do_stmt($dbh, $qry); warn "$DBI::errstr\n" 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 ($dgname); # 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'})) { $dgname = "ALL"; } else { # check for a disk group if (!defined($ARGV[0])) { asmcmddisk_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dgname = $ARGV[0]; } $qry = "ALTER DISKGROUP " . $dgname . " DISMOUNT "; # force option if (defined($args{'f'})) { $qry .= " FORCE"; } $ret = asmcmdshare_do_stmt($dbh, $qry); warn "$DBI::errstr\n" 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) ) { asmcmddisk_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); warn "$DBI::errstr\n" unless defined ($ret); } ######## # 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) ) { asmcmddisk_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); warn "$DBI::errstr\n" unless defined ($ret); } ######## # 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); # 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); warn "$DBI::errstr\n" unless defined ($ret); } # 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); warn "$DBI::errstr\n" 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'})); 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); warn "$DBI::errstr\n" 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 ($syntax); # Command syntax for $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. # $syntax = asmcmddisk_get_cmd_syntax($command); $desc = asmcmddisk_get_cmd_desc($command); print " $syntax\n" . "$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; $cmd_syntax = asmcmddisk_get_cmd_syntax($cmd); # Get syntax for $cmd. # 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_error_msg # # DESCRIPTION # This function is a wrapper around asmcmddisk_display_msg(), the # function responsible for displaying error messages for the asmcmddisk # module. # # This function is called by the general function asmcmdshare_error_msg, # which calls the respective _error_msg function in each module. # # This function records an error but does not signal one. # # PARAMETERS # err_num (IN) - ASMCMD internal error number. # args_ref (IN) - (Optional) Reference to array of error arguments # # RETURNS # 1 if the error number is supported by the asmcmddisk module; 0 # otherwise. # # NOTES # Only asmcmdshare_error_message should call this function. *Do not* # call this function directly; call asmcmdshare_error_message, # instead. ######## sub asmcmddisk_error_msg { my ($err_num, $args_ref) = @_; my ($succ) = 0; my (@eargs); # Assert that $err_num is not within 8000-9400, inclusive. @eargs = ("asmcmddisk_error_msg_05", $err_num); asmcmdshare_assert( (($err_num > 8000) || ($err_num < 9400)) , \@eargs); $succ = asmcmddisk_display_msg($err_num, $args_ref); return $succ; } ######## # NAME # asmcmddisk_display_msg # # DESCRIPTION # This routine prints error and exception messages to STDERR for the # asmcmddisk module. # # PARAMETERS # err_num (IN) - ASMCMD internal error number. # args_ref (IN) - (Optional) Reference to array of error arguments # # RETURNS # 1 if error message is found; 0 otherwise. # # NOTES # This function maintains a hash of error numbers supported by this # module in order to return quickly if the error number is not # supported by asmcmddisk. # # If an error is found, this function prints the error message. ######### sub asmcmddisk_display_msg { my ($err_num, $args_ref) = @_; my ($errmsg) = ''; # Error message from from $DBI::errstr. # my ($succ) = 0; # 1 if error message found, 0 otherwise. # my ($argument); # Argument iterator of the $args_ref array. # # Define a hash of error messages that exist for this module. # 9371-9400 my (%error_messages) = ( 9371 => q!disk '$arg1' does not exist in disk group '$arg2'!, 9372 => q!physical blocks $arg1-$arg2 do not ! . q!map to a valid ASM file!, 9373 => q!not all physical blocks submitted for remap!, 9374 => q!kfed binary not found at $arg! . "\n\n" . q! Run these commmands if running Oracle 10.2 or later:! . "\n" . q! cd $ORACLE_HOME/rdbms/lib! . "\n" . q! make -f ins_rdbms.mk ikfed! . "\n\n" . q! Run these commmands if running Oracle 10.1:! . "\n" . q! cd $ORACLE_HOME/rdbms/lib! . "\n" . q! mv ssskfeded.o sskfeded.o! . "\n" . q! make -f ins_rdbms.mk $ORACLE_HOME/rdbms/lib/kfed! . "\n" . q! mv kfed ../../bin/! . "\n\n" . q! Verify that $ORACLE_HOME/bin/kfed exists and has the! . "\n" . q! execute permission bit set.! . "\n", 9375 => q!error occurred when executing! . "\n" . q! $arg1! . "\n\n" . q!$arg2!, 9378 => q!the scanning of disk headers is supported only ! . q!on UNIX platforms!, 9381 => q!remap command requires ASM software version ! . qq!$ASMCMDGLOBAL_VER_11gR1 or later.!, 9382 => q!physical blocks $arg1-$arg2 map to an unmirrored file!, 9383 => q!mapextent command requires ASM software version ! . qq!$ASMCMDGLOBAL_VER_11gR2 or later.!, 9384 => q!mapau command requires ASM software version ! . qq!$ASMCMDGLOBAL_VER_11gR2 or later.!, 9390 => q!invalid xml tag '$arg'!, 9391 => q!invalid xml file!, 9392 => q!missing argument '$arg'!, 9393 => q!cannot specify a failure group and a diskgroup at the same time!, 9394 => q!cannot specify a failure group and a disk at the same time!, 9395 => q!error parsing XML file: $arg!, 9396 => q!configuration file doesn\'t exist!, 9397 => q!diskgroup '$arg' does not exist or is not mounted!, 9398 => q!element $arg is empty!, 9399 => q!argument '$arg' is not a valid number!, ); $errmsg = $error_messages{$err_num}; # Process error arguments for errors that support them. if (($err_num == 9371) || ($err_num == 9372) || ($err_num == 9375) || ($err_num == 9382)) { # Substitute the string '$arg' with the value of $args_ref->[0] $errmsg =~ s,\$arg1,$args_ref->[0],; $errmsg =~ s,\$arg2,$args_ref->[1],; } if (($err_num == 9374) || ($err_num == 9390) || ($err_num == 9392) || ($err_num == 9395) || ($err_num == 9397) || ($err_num == 9399)) { $errmsg =~ s,\$arg,$$args_ref,; } # Print error only if this module supports this error number. if (defined ($errmsg)) { $succ = 1; print STDERR 'ASMCMD-0' . $err_num . ': ' . $errmsg . "\n"; } return $succ; } ######## # NAME # asmcmddisk_signal_exception # # DESCRIPTION # This function is a wrapper around asmcmddisk_display_msg(), the # function responsible for displaying error messages for the asmcmddisk # module. This function is a callback for asmcmdshare_signal_exception. # # PARAMETERS # exception_num (IN) - ASMCMD internal error/exception number. # args_ref (IN) - (Optional) Reference to array of error arguments # # RETURNS # 1 if the error number is supported by the asmcmddisk module; 0 # otherwise. # # NOTES # Only asmcmdshare_signal_exception should call this function. *Do not* # call this function directly; call asmcmdshare_signal_exception, # instead. The caller of this function always exits 1. ######## sub asmcmddisk_signal_exception { my ($exception_num, $args_ref) = @_; my ($succ) = 0; # Assert that $exception_num is within 8200-8299, inclusive. if (($exception_num < 8000) || ($exception_num > 9400)) { die "asmcmd: 8202 internal error: [asmcmddisk_signal_exception_05] " . "[$exception_num]\n"; } $succ = asmcmddisk_display_msg($exception_num, $args_ref); return $succ; } ######## # NAME # asmcmddisk_get_cmd_desc # # DESCRIPTION # This routine returns the help description of the command specified by $cmd. # # PARAMETERS # cmd (IN) - the name of the command of which we're looking up the # description. # # RETURNS # The description paragraph(s) for command $cmd; undefined if $cmd does not # exist. # # NOTES # IMPORTANT: the commands descriptions must be preceded by eight (8) spaces # of indention! This formatting is mandatory. ######## sub asmcmddisk_get_cmd_desc { my ($cmd) = shift; my (%cmd_desc); # Hash storing the description for each internal command. # $cmd_desc{'lsdsk'} = ' List ASM-visible disks. [pattern] restricts the output to only disks that matches the pattern. Wild-card characters and slashes can be part of the pattern. This command can run in two modes: connected or non-connected. In connected mode, ASMCMD uses the V$ and GV$ tables to retrieve disk information. In non-connected mode, ASMCMD scans disk headers to retrieve disk information, using an ASM discovery string to restrict the discovery set. If ASMCMD is connected to an instance, then lsdsk runs in connected mode, unless the -I flag is specified. If ASMCMD is not connected to an instance, then lsdsk runs in non-connected mode. In connected mode this command queries the V$ASM_DISK_STAT view by default, which the -c and -g flags can modify. The first four flags modify how much information is displayed for each disk. Note that the number of columns returned differs between running in connected and non-connected modes. In connected mode columns holding the following data are returned: (no flag) V$ASM_DISK.PATH -k V$ASM_DISK.TOTAL_MB, V$ASM_DISK.FREE_MB, V$ASM_DISK.OS_MB, V$ASM_DISK.NAME, V$ASM_DISK.FAILGROUP, V$ASM_DISK.LIBRARY, V$ASM_DISK.LABEL, V$ASM_DISK.UDID, V$ASM_DISK.PRODUCT, V$ASM_DISK.REDUNDANCY, V$ASM_DISK.PATH --statistics V$ASM_DISK.READS, V$ASM_DISK.WRITES, V$ASM_DISK.READ_ERRS, V$ASM_DISK.WRITE_ERRS, V$ASM_DISK.READ_TIME, V$ASM_DISK.WRITE_TIME, V$ASM_DISK.BYTES_READ, V$ASM_DISK.BYTES_WRITTEN, V$ASM_DISK.PATH, V$ASM_DISK.VOTING_FILE -p V$ASM_DISK.GROUP_NUMBER, V$ASM_DISK.DISK_NUMBER, V$ASM_DISK.INCARNATION, V$ASM_DISK.MOUNT_STATUS, V$ASM_DISK.HEADER_STATUS, V$ASM_DISK.MODE_STATUS, V$ASM_DISK.STATE, V$ASM_DISK.PATH -t V$ASM_DISK.CREATE_DATE, V$ASM_DISK.MOUNT_DATE, V$ASM_DISK.REPAIR_TIMER, V$ASM_DISK.PATH In non-connected mode columns holding the following data are returned: (no flag) PATH -k TOTAL_MB, NAME, FAILGROUP, PATH --statistics PATH -p DISK_NUM, DISKGROUP, HEADER_STATUS, PATH -t CREATE_DATE, MOUNT_DATE, PATH If any combination of the three flags are chosen, then the output shows the union of the columns associated with each flag. The following flags have special properties: --discovery Selects from V$ASM_DISK, or GV$ASM_DISK, if -g flag is also active. If the ASM software version is 10gR1, then the effects of this flag is always active, whether or not it is set. This flag is disregarded if lsdsk is running in non-connected mode. -g Selects from GV$ASM_DISK_STAT, or GV$ASM_DISK, if the -c flag is also active; GV$ASM_DISK.INST_ID is included in the output. This flag is disregarded if lsdsk is running in non-connected mode. -H Suppresses column header from the output. -I Scan disk headers for information instead of extracting it from a view. -G Restrict results to only disks belonging to the specified disk group. Restrict results to only disks of membership status --member membership status Member --candidate membership status Candidate -M Displays the disks that are visible to some but not all active instances. These are disks that, if included in a disk group, will cause the mount of that disk group to fail on the instances where the disks are not visible.'; $cmd_desc{'lsod'} = ' Displays the open devices -G Filters devices belonging to diskgroup --process Filters processes of name . Filters disks of name . -H Suppresses column header from the output. '; $cmd_desc{'remap'} = ' Remap a range of physical blocks on a disk, if they are unreadable. is the name of the disk group in which a disk needs to be remapped. is the V$ASM_DISK.NAME of the disk that needs to be remapped. is a range of physical blocks to remap, in this format: from-to. The physical block size is assumed to be a sector size of 512. The AU size is assumed to be 1 megabyte. Note that this command remaps only unreadable bad disk sectors; it does not repair blocks with incorrect content, whether readable or not. Examples: ASMCMD> remap DATA DATA_0001 5000-7500'; $cmd_desc{'dropdg'} = ' Drop an existing disk group. disk group name to drop [-r] including contents [-f] force '; $cmd_desc{'mkdg'} = ' Create a disk group based on the XML configuration. It can be a provided as a file or inline. The XML accepts the following tags, with their respective attributes: disk group name disk group name redundancy normal, external, high failure group name failure group name disk name disk name path disk path size size of the disk to add attribute name attribute name value attribute value Example: '; $cmd_desc{'chkdg'} = ' Check integrity of a disk group. --repair Repair the diskgroup Name of the disk group to check. '; $cmd_desc{'chdg'} = ' Change disk group configuration according to the XML configuration. It can be provided as a file or inline. The XML accepts the following tags, with their respective attributes: change diskgroup clause (add/delete disks/failure groups) name disk group to change power power to perform rebalance items to add are placed here items to drop are placed here failure group name failure group name disk name disk name path disk path size size of the disk to add Example: '; $cmd_desc{'mount'} = ' Mount a disk group. [-a] Mount all diskgroups specified in asm_diskgroup parameter [--restrict] Mount in restricted mode. [-f] Mount in force mode. Disk group to mount. '; $cmd_desc{'umount'} = ' Dismount a disk group. [-a] Dismount all disk groups [-f] Dismount in force mode. Disk group to dismmount. '; $cmd_desc{'online'} = ' Online one or more disks in a disk group. -G Disk group name. [-a] Online all disks in the disk group. -F Online all disks in the failure group. -D Online a single disk. [-w] Wait. '; $cmd_desc{'offline'} = ' Offline one or more disks in a disk group. -G Disk group name. -F Offline all disks in the failure group. -D Offline a single disk. [-t ] Timeout before offline happens. '; $cmd_desc{'rebal'} = ' Rebalance a disk group. Default is with power 1. [--power] Specify the power for rebalance, can be up to 11. [-w] Wait. Do not perform rebalance right away. Disk group to rebalance. '; $cmd_desc{'iostat'} = ' Report statistics and input/output statistics. The statistics are reported just for mounted devices. [interval] Refresh interval. [--io] Display information in IOs, instead of bytes. [--region] Display regions information [-e] Display error statistics. [-t] Display time statistics. [-G ] List statistics for a specific disk group. [-H] Suppress column headers. '; return $cmd_desc{$cmd}; } ######## # NAME # asmcmddisk_get_cmd_syntax # # DESCRIPTION # This routine returns the help syntax of the command specified by $cmd. # # PARAMETERS # cmd (IN) - the name of the command of which we're looking up the # syntax. # # RETURNS # The syntax for command $cmd; undefined if $cmd does not exist. ######## sub asmcmddisk_get_cmd_syntax { my ($cmd) = shift; my (%cmd_syntax); # Hash storing the syntax for each internal command. # $cmd_syntax{'lsdsk'} = 'lsdsk [-kptgMHI] [-G ] ' . '[--member | --candidate] [pattern]' . '[--discovery] [--statistics]' ; $cmd_syntax{'lsod'} = 'lsod [-H] [-G ] [--process ]'. ' [disk]'; $cmd_syntax{'remap'} = 'remap ' . ''; $cmd_syntax{'mkdg'} = 'mkdg <| >'; $cmd_syntax{'chdg'} = 'chdg <| >'; $cmd_syntax{'dropdg'} = 'dropdg [-r [-f]] '; $cmd_syntax{'chkdg'} = 'chkdg '; $cmd_syntax{'mount'} = 'mount [--restrict] [-f] <-a|dgname>'; $cmd_syntax{'umount'} = 'umount [-f] <-a|dgname>'; $cmd_syntax{'online'} = 'online [-a] -G <-F |'. '-D > [-w]'; $cmd_syntax{'offline'} = 'offline -G <-F |-D > ' . '[-t ] '; $cmd_syntax{'rebal'} = 'rebal [--power ] [-w] '; $cmd_syntax{'iostat'} = 'iostat [-etH][--io][--region] [-G dgname]'. ' [interval]'; return $cmd_syntax{$cmd}; } ######## # 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;