# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. # # NAME # asmcmdvol - ASM CoMmanD line interface for VOLume commands # # DESCRIPTION # ASMCMD is a Perl utility that provides easy nagivation of files within # ASM diskgroups. This module contains the functionality for # the ASM volume commands. # # NOTES # usage: asmcmdcore [-p] [command] # # MODIFIED (MM/DD/YY) # moreddy 04/12/11 - Backport moreddy_bug-12312348 from main # moreddy 01/13/11 - Backport moreddy_bug-8667038 from main # bonelso 04/17/10 - Fix asmcmdvol_error_msg() and the warning message # in volresize when shrinking a volume. # Bugs 94561723 and 9466091 # bonelso 03/30/10 - Fix for LRG4522067 # pvenkatr 03/25/10 - Syntax, command description, example all from XML # moreddy 03/22/10 - Adding more tracing # bonelso 03/17/10 - Fix asmcmdvol_error_msg() and the warning message # in volresize when shrinking a volume. # Bugs 94561723 and 9466091 # moreddy 01/18/10 - Adding tracing messages # pvenkatr 09/03/09 - Help message from xml file # sanselva 06/22/09 - volcreate --secondary option not set correctly # sanselva 06/17/09 - correct check for --primary and --secondary # sanselva 04/12/09 - ASMCMD long options and consistency # heyuen 04/06/09 - fix asmcmd_error_msg # bonelso 03/04/09 - OFS -> ACFS and add support for K suffix in resize # heyuen 10/14/08 - use dynamic modules # bonelso 10/09/08 - Add back help text for volstat # gsanders 08/05/08 - fix merge errors from 07/28 version # heyuen 07/28/08 - use command properties array # gsanders 07/10/08 - fix bug 6963383 require -d dgname option # heyuen 04/09/08 - reorder help commands and improve help message. # averhuls 04/01/08 - add to volinfo documentation. # averhuls 03/05/08 - Add -v and -g undocumented switches to volinfo. # josmith 01/05/08 - Put single quotes around list of volumes # josmith 12/13/07 - Don't put single quotes around DG names # josmith 11/21/07 - Bug 6629935 - Need quotes around identifiers # averhuls 11/15/07 - Document volset needing quotes if options contain # spaces # josmith 11/14/07 - Add resize_unit_mb column # averhuls 11/14/07 - Warn users before shrinking a non-ACFS volume. # josmith 11/02/07 - Change volume redundancy # josmith 10/17/07 - Support zones # josmith 06/20/07 - Make columns consistent with v # heyuen 05/25/07 - add return codes for errors # averhuls 04/10/07 - Bug 5958908 and 5958923 fixes # Re-document volset # averhuls 03/01/07 - add asmcmdvol_is_no_instance_cmd # josmith 09/03/06 - Add wildcard subroutine # averhuls 09/01/06 - Add process_help # averhuls 07/18/06 - Undocument volset as it intended as an internal # command. # averhuls 06/23/06 - Created # ############################################################################# # ############################ Functions List ################################# # # asmcmdvol_process_volcreate # asmcmdvol_process_voldelete # asmcmdvol_process_voldisable # asmcmdvol_process_volenable # asmcmdvol_process_volinfo # asmcmdvol_process_volresize # asmcmdvol_process_volset # asmcmdvol_process_volstat # # SQL Routines # asmcmdvol_volcreate_sql # asmcmdvol_voldelete_sql # asmcmdvol_volenable_disable_sql # asmcmdvol_volresize_sql # asmcmdvol_volset_sql # # Help Routines # asmcmdvol_get_asmcmd_cmds # # Parameter Parsing Routines # asmcmdvol_is_cmd # asmcmdvol_parse_int_args ############################################################################# package asmcmdvol; require Exporter; our @ISA = qw(Exporter); #our @EXPORT = qw(asmcmdvol_init); use strict; use Getopt::Long qw(:config no_ignore_case bundling); use asmcmdglobal; use asmcmdshare; ####################### ASMCMDVOL Global Variables ######################## our (%asmcmdvol_cmds) = ( volcreate => {no_instance => 'True', flags => {'G=s'=>'diskGroup', 'column=s'=> 'noOfColumns', 'primary=s'=> 'primaryRegion', 'secondary=s'=> 'secondaryRegion', 'redundancy=s'=> 'redundancyType', 's=s'=>'size', 'width=s'=> 'stripeWidth'} }, voldelete => {no_instance => 'True', flags => {'G=s'=>'diskGroup'} }, voldisable => {no_instance => 'True', flags => {'G=s'=>'diskGroup', 'a'=>'all'} }, volenable => {no_instance => 'True', flags => {'G=s'=>'diskGroup', 'a'=>'all'} }, volinfo => {no_instance => 'True', flags => {'G=s'=>'diskGroup', 'a'=>'all', 'show_diskgroup=s'=> 'displayDiskGroupName', 'show_volume=s'=> 'displayVolumeName'} }, volresize => {no_instance => 'True', flags => {'G=s'=>'diskGroup', 'f'=>'force', 's=s'=>'size'} }, volset => {flags => {'G=s'=>'diskGroup', 'primary=s'=> 'primaryRegion', 'secondary=s'=> 'secondaryRegion', 'usagestring=s'=> 'usageString2TagVol', 'mountpath=s'=> 'mountPath'} }, volstat => {flags => {'G=s'=>'diskGroup', 'show_volume'=> 'displayVolumeName'} } ); sub is_asmcmd { return 1; } ######## # NAME # init # # DESCRIPTION # This function initializes the asmcmdvol module. It simply registers # its callbacks with the asmcmdglobal module. Note that you must register # all 8 callbacks - even if the module has no corresponding function. # # 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, \&asmcmdvol_process_cmd); push (@asmcmdglobal_help_callbacks, \&asmcmdvol_process_help); push (@asmcmdglobal_command_list_callbacks, \&asmcmdvol_get_asmcmd_cmds); push (@asmcmdglobal_is_command_callbacks, \&asmcmdvol_is_cmd); push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdvol_is_wildcard_cmd); push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdvol_syntax_error); push (@asmcmdglobal_no_instance_callbacks, \&asmcmdvol_is_no_instance_cmd); %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdvol_cmds); #Perform ASMCMD consistency check if enabled if($asmcmdglobal_hash{'consistchk'} eq 'y') { if(!asmcmdshare_check_option_consistency(%asmcmdvol_cmds)) { exit 1; } } } ######## # NAME # process_cmd # # DESCRIPTION # This routine calls the appropriate routine to process the command # specified by $asmcmdglobal_hash{'cmd'}. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # # RETURNS # 1 if command is found in the asmcmdvol module; 0 if not. # # NOTES # Only asmcmdcore_shell() calls this routine. ######## sub asmcmdvol_process_cmd { my ($dbh) = @_; my ($succ) = 0; # Get current command from global value, which is set by # asmcmdvol_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 ASMCMD volume command. my (%cmdhash) = ( volcreate => \&asmcmdvol_process_volcreate, voldelete => \&asmcmdvol_process_voldelete, voldisable => \&asmcmdvol_process_voldisable, volenable => \&asmcmdvol_process_volenable, volinfo => \&asmcmdvol_process_volinfo, volresize => \&asmcmdvol_process_volresize, volset => \&asmcmdvol_process_volset, volstat => \&asmcmdvol_process_volstat, ); if (defined ( $cmdhash{ $cmd } )) { # If user specifies a known command, then call routine to process it. # $cmdhash{ $cmd }->($dbh); $succ = 1; } return $succ; } ######## # NAME # asmcmdvol_process_help # # DESCRIPTION # This function is the help function for the asmcmdvol module. # # PARAMETERS # command (IN) - display the help message for this command. # # RETURNS # 1 if command found; 0 otherwise. ######## sub asmcmdvol_process_help { my ($command) = shift; # User-specified argument; show help on $cmd. # my ($desc); # Command description for $cmd. # my ($succ) = 0; # 1 if command found, 0 otherwise. # if (asmcmdvol_is_cmd ($command)) { # User specified a command name to look up. # $desc = asmcmdshare_get_help_desc($command); # The following check prevents an "undefined value" error message for # legal commands that have no help information. if (defined $desc) { print "$desc\n"; $succ = 1; } else { $succ = 0; } } return $succ; } ######## # NAME # asmcmdvol_process_volcreate # # DESCRIPTION # This top-level routine processes the volcreate command. # # PARAMETERS # dbh (IN) - initialized handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdvol_process_cmd() can call this routine. ######## sub asmcmdvol_process_volcreate { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my ($ret); # asmcmdvol_parse_int_args() return value. # my ($volname); # volume name to create (required user parameter) # my ($dgname); # name of mounted diskgroup (required user parameter) # my ($size); # volume size (required user parameter) # my ($stripe_columns); # stripe columns (optional user parameter) # my ($stripe_width); # stripe width (optional user parameter) # my ($redundancy); # redundancy (optional user parameter) # my ($primary_zone); # disk zone for primary extents (optional) # my ($mirror_zone); # disk zone for mirrored extents (optional) # my (@eargs); # Check if minimum number of required parameters are correct. # For example "foo bar -s 100M". if (@ARGV < 5) { asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # parse options $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # get and validate diskgroup name (-G required) if (!defined($args{'G'})) { asmcmdshare_error_msg(8551, undef); return; } $dgname = $args{'G'}; if ($dgname =~ /\W/) { @eargs = ($dgname); asmcmdshare_error_msg(8552, \@eargs); return; } # get and validate volume name (last arg - required) $volname = pop @ARGV; if ( ! $volname ) { asmcmdshare_error_msg(8553, undef); return; } if ($volname =~ /\W/) { @eargs = ($volname); asmcmdshare_error_msg(8554, \@eargs); return; } # validate option parameters - "-s" is required . if (!defined($args{'s'})) { asmcmdshare_error_msg(8555, undef); return; } else { $size = $args{'s'}; } # initialize optional parameters $redundancy = 'default'; $stripe_columns = 'default'; $stripe_width = 'default'; if (defined($args{'redundancy'})) # Get redundancy. # { # user can specifiy in upper or lower case $redundancy = $args{'redundancy'}; tr/a-z/A-Z/ for $redundancy; # normally I would let SQL field this error but the SQL error message # is just awful. if (($redundancy ne 'UNPROTECTED') && ($redundancy ne 'MIRROR') && ($redundancy ne 'HIGH')) { @eargs = ($redundancy); asmcmdshare_error_msg(8556, \@eargs); return; } } if (defined($args{'column'})) # Get stripe columns. # { $stripe_columns = $args{'column'}; } if (defined($args{'width'})) # Get stripe width. # { $stripe_width = $args{'width'}; } if (defined($args{'primary'})) # Get primary zone # { #Run GetOpts on the value to support first unique chars for option push(@ARGV,"--".$args{'primary'}); GetOptions (\%args, 'hot','cold'); pop(@ARGV); if(defined($args{'hot'}) ^ defined($args{'cold'})) { if(defined($args{'hot'})) { $primary_zone = 'HOT'; delete($args{'hot'}); } elsif(defined($args{'cold'})) { $primary_zone = 'COLD'; delete($args{'cold'}); } } else { # part of the user input could not be parsed asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } } if (defined($args{'secondary'})) # Get secondary zone # { #Run GetOpts on the value to support first unique chars for option push(@ARGV,"--".$args{'secondary'}); GetOptions (\%args, 'hot','cold'); pop(@ARGV); if(defined($args{'hot'}) ^ defined($args{'cold'})) { if(defined($args{'hot'})) { $mirror_zone = 'MIRRORHOT'; delete($args{'hot'}); } elsif(defined($args{'cold'})) { $mirror_zone = 'MIRRORCOLD'; delete($args{'cold'}); } } else { # part of the user input could not be parsed asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } } if (@ARGV != 0) { # part of the user input could not be parsed asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # Run SQL to create the volume. asmcmdvol_volcreate_sql ($dbh, $dgname, $volname, $size, $redundancy, $stripe_columns, $stripe_width, $primary_zone, $mirror_zone); } ######## # NAME # asmcmdvol_process_voldelete # # DESCRIPTION # This top-level routine processes the voldelete command. # # PARAMETERS # dbh (IN) - initialized handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdvol_process_cmd() can call this routine. ######## sub asmcmdvol_process_voldelete { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my ($ret); # asmcmdvol_parse_int_args() return value. # my ($volname); # volume name to delete # my ($dgname); # name of mounted diskgroup # my (@eargs); # the diskgroup name option and volume name must be specified if (@ARGV != 3) { asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # parse options $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # get and validate diskgroup name (-d required) if (!defined($args{'G'})) { asmcmdshare_error_msg(8551, undef); return; } $dgname = $args{'G'}; if ($dgname =~ /\W/) { @eargs = ($dgname); asmcmdshare_error_msg(8552, \@eargs); return; } # Get the volume name. $volname = pop(@ARGV); if ( ! $volname ) { asmcmdshare_error_msg(8553, undef); return; } if ($volname =~ /\W/) { @eargs = ($volname); asmcmdshare_error_msg(8554, \@eargs); return; } # Run SQL to delete the volume. asmcmdvol_voldelete_sql ($dbh, $dgname, $volname); } ######## # NAME # asmcmdvol_process_volenable # asmcmdvol_process_voldisable # # DESCRIPTION # This top-level routine processes the volenable command. # This top-level routine processes the voldisable command. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdvol_process_cmd() can call asmcmdvol_process_voldisable # and asmcmdvol_process_volenable. # # Since the volenable and voldisable commands have the same arguments, # it makes sense to have the commands as a front end to a common function. # The only difference is whether "enable" or "disable" is sent to SQL. ######## sub asmcmdvol_process_voldisable { # add the "disable" command onto the front of the argument stack # The common routine will parse this. unshift @ARGV, 'disable'; asmcmdvol_process_volenable_disable(@_); } sub asmcmdvol_process_volenable { # add the "enable" command onto the front of the argument stack # The common routine will parse this. unshift @ARGV, 'enable'; asmcmdvol_process_volenable_disable(@_); } # The common function for volenable and voldisable. sub asmcmdvol_process_volenable_disable { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my ($ret); # asmcmdvol_parse_int_args() return value. # my ($cmd_name); # SQL "enable" or "disable" statement # my ($volname); # volume name to enable/disable # my ($dgname); # name of mounted diskgroup # my ($validargs); # Is argument list valid? # # "enable" or "disable" - NOT user supplied $cmd_name = shift(@ARGV); # initialize required parameters. if the user does not specify, # all diskgroups and/or all volumes are enabled/disabled. $dgname = 'all'; $volname = 'all'; # validate parameters, if any. since this routine is NOT called directly # from asmcmdvol_process_cmd(), we have to play some games to get the # user supplied command name. $ret = asmcmdvol_parse_int_args('vol' . $cmd_name, \%args); return unless defined ($ret); # Verify valid set of arguments. $validargs = ( (!defined($args{'a'}) && defined($args{'G'}) && @ARGV == 1) || (defined($args{'a'}) && @ARGV == 0) ); if (!$validargs) { asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } if (defined($args{'G'})) # Get diskgroup name. # { $dgname = $args{'G'}; } else { $dgname = 'all'; } if (defined($args{'a'})) # Get volume name. # { $volname = 'all'; } else { $volname = shift(@ARGV); # Put single quotes around the volume name for SQL $volname = "'$volname'"; } # Run SQL to enable/disable the volume. asmcmdvol_volenable_disable_sql ($dbh, $cmd_name, $dgname, $volname); } ######## # NAME # asmcmdvol_process_volinfo # # DESCRIPTION # This top-level routine processes the volinfo command. # # PARAMETERS # dbh (IN) - initialized handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdvol_process_cmd() can call this routine. ######## sub asmcmdvol_process_volinfo { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my ($ret); # asmcmdvol_parse_int_args() return value. # my ($sql); #SQL select command # my ($sth); # SQL statement handle. # my ($row); # One row results returned from the SQL execution. # my ($dgname); #diskgroup name # my ($volname); # volume name # my ($cur_dgname); # determines when to print dg # my ($report); # set when data is printed - used for error reporting # my ($device_name); # used only to report volume name or diskgroup name # my ($validargs); # Is argument list valid? # my (@eargs); # validate option parameters, if any. $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # Verify valid set of arguments. $validargs = ( (!defined($args{'a'}) && defined($args{'G'}) && @ARGV == 1) || (defined($args{'show_diskgroup'}) && @ARGV == 0) || (defined($args{'show_volume'}) && @ARGV == 0) || (defined($args{'a'}) && @ARGV == 0) ); if (!$validargs) { asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } if (defined($args{'G'})) # Get diskgroup name. # { $dgname = $args{'G'}; } else { $dgname = 'all'; } if (defined($args{'a'})) # Get volume name. # { $volname = 'all'; } else { $volname = shift(@ARGV); } # The --show_diskgroup and --show_volume options are for internal use only # and are not documented in "usage" or "help". if (defined($args{'show_diskgroup'})) # output ONLY the diskgroup name { $device_name = $args{'show_diskgroup'}; } if (defined($args{'show_volume'})) # output ONLY the volume name { $device_name = $args{'show_volume'}; } # if a diskgroup is specified, does it exist? if (defined($args{'G'})) { my ($dg_uc) = uc($dgname); $sql = "select * from V\$ASM_DISKGROUP_STAT where name = '$dg_uc'"; # perform the SQL select $sth = asmcmdshare_do_select($dbh, $sql); return undef if (! defined ($sth)); $row = asmcmdshare_fetch($sth); if (!defined($row)) { @eargs = ($dgname); asmcmdshare_error_msg(8001, \@eargs); return; } } # Note1: All that we want from V$ASM_DISKGROUP is the diskgroup name. # Note2: it looks like diskgroups are sorted in dg number order - which is # what we want. But, just to be sure, we enforce sort order here. In # addition, we also sort by volume name within the diskgroup. # Note3: The order of the fixed views is important! Both V$ASM_DISKGROUP # and V$ASM_VOLUME have a STATE column. The order below ensures that we get # the V$ASM_VOLUME state and not the V$ASM_DISKGROUP state. $sql = "select * from V\$ASM_DISKGROUP_STAT join V\$ASM_VOLUME on V\$ASM_VOLUME.GROUP_NUMBER = V\$ASM_DISKGROUP_STAT.GROUP_NUMBER order by V\$ASM_DISKGROUP_STAT.GROUP_NUMBER, V\$ASM_VOLUME.VOLUME_NAME"; # perform the SQL select $sth = asmcmdshare_do_select($dbh, $sql); return undef if (! defined ($sth)); # process the volumes $cur_dgname = 'none'; $report = 0; while (defined ($row = asmcmdshare_fetch($sth))) { if (defined($args{'G'}) && (uc($dgname) ne $row->{'NAME'})) { # a dgname was specified on the cmd line and this row isn't it - continue next; } if ((defined($args{'show_diskgroup'})) || (defined($args{'show_volume'}))) { if ($device_name ne $row->{'VOLUME_DEVICE'}) { # We're looking for a specific device and this isn't it - continue. next; } } elsif ((!defined($args{'a'})) && (uc($volname) ne $row->{'VOLUME_NAME'})) { # a volname was specified on the cmd line and this row isn't it - continue next; } if (defined($device_name)) { # We have already verified that this is the device we are looking for. # # We are printing either the diskgroup name or the volume name # and NOTHING else. if (defined($args{'show_diskgroup'})) { # output only the diskgroup name printf("%s\n", lc($row->{'NAME'})); } elsif (defined($args{'show_volume'})) { # output only the volume name printf("%s\n", lc($row->{'VOLUME_NAME'})); } asmcmdshare_finish ($sth); return; } if ($cur_dgname ne $row->{'NAME'}) { my($dgnum, $dgnam); # This is the first time that we've seen this disk group, so # print the dg number and name - remember that this list is sorted. # Perl complains if the printf line wraps. $dgnum = $row->{'GROUP_NUMBER'}; $dgnam = $row->{'NAME'}; printf "Diskgroup Name: $dgnam\n\n"; # Indicate that we've seen this diskgroup. $cur_dgname = $dgnam; } $report = 1; # we found information to print # Format and print the volume information. printf "\t Volume Name: $row->{'VOLUME_NAME'}\n"; printf "\t Volume Device: $row->{'VOLUME_DEVICE'}\n"; printf "\t State: $row->{'STATE'}\n"; printf "\t Size (MB): $row->{'SIZE_MB'}\n"; printf "\t Resize Unit (MB): $row->{'RESIZE_UNIT_MB'}\n"; printf "\t Redundancy: $row->{'REDUNDANCY'}\n"; printf "\t Stripe Columns: $row->{'STRIPE_COLUMNS'}\n"; printf "\t Stripe Width (K): $row->{'STRIPE_WIDTH_K'}\n"; printf "\t Usage: "; if (defined($row->{'USAGE'})) { printf "$row->{'USAGE'}"; } printf "\n"; printf "\t Mountpath: "; if (defined($row->{'MOUNTPATH'})) { printf ("%s ", $row->{'MOUNTPATH'}); } print "\n"; print "\n"; } if (!$report) { if (defined($args{'G'}) && !defined($args{'a'})) { printf("volume %s not found in diskgroup %s\n", $volname, $dgname); } elsif (defined($args{'G'})) { printf("diskgroup %s has no volumes or is not mounted\n", $dgname); } elsif ((defined($args{'show_diskgroup'})) || (defined($args{'show_volume'}))) { printf("no device name \"%s\" found\n", $device_name); } else { printf("no volumes found\n"); } } asmcmdshare_finish ($sth); } ######## # NAME # asmcmdvol_process_volresize # # DESCRIPTION # This top-level routine processes the volresize command. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdvol_process_cmd() can call this routine. ######## sub asmcmdvol_process_volresize { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my ($ret); # asmcmdvol_parse_int_args() return value. # my ($volname); # volume name to resize # my ($dgname); # name of mounted diskgroup # my ($newsize); # new size of volume # my ($cursize); # current size of volume # my ($usage); # usage string of volume # my ($force); # force option # my ($sql); # SQL select statement # my ($sth); # SQL statement handle # my ($row); # One row results returned from the SQL execution # my (@eargs); if (!((@ARGV == 5) || (@ARGV == 6))) { # must have dgname, volname, and -s newsize asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # parse options $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # get and validate diskgroup name (-G required) if (!defined($args{'G'})) { asmcmdshare_error_msg(8551, undef); return; } $dgname = $args{'G'}; if ($dgname =~ /\W/) { @eargs = ($dgname); asmcmdshare_error_msg(8552, \@eargs); return; } # get and validate volume name (last arg - required) $volname = pop(@ARGV); if ( ! $volname ) { asmcmdshare_error_msg(8553, undef); return; } if ($volname =~ /\W/) { @eargs = ($volname); asmcmdshare_error_msg(8554, \@eargs); return; } # get required -s parameter. $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # we will not have gotten here if -s was not specified $newsize = $args{'s'}; # force option if (defined($args{'f'})) { $force = 1; } # check to see if this is a non ACFS volume. # If so and the new size is less than the current size, warn the user. $sql = "select * from V\$ASM_VOLUME join V\$ASM_DISKGROUP on V\$ASM_DISKGROUP.GROUP_NUMBER = V\$ASM_VOLUME.GROUP_NUMBER"; # perform the SQL select $sth = asmcmdshare_do_select($dbh, $sql); return undef if (! defined ($sth)); # process the volumes while (defined ($row = asmcmdshare_fetch($sth))) { tr/a-z/A-Z/ for $dgname; tr/a-z/A-Z/ for $volname; if (($dgname eq $row->{'NAME'}) && ($volname eq $row->{'VOLUME_NAME'})) { $cursize = $row->{'SIZE_MB'}; if (defined($row->{'USAGE'})) { $usage = $row->{'USAGE'}; } else { $usage = "not defined"; } last; } } # did we find the volume? if (!defined($cursize)) { @eargs = ($dgname, $volname); asmcmdshare_error_msg(8557, \@eargs); return; } # newsize must be in MB for the size test if (($newsize =~ m/k/) || ($newsize =~ m/K/)) { $newsize =~ s/k//; $newsize =~ s/K//; if($newsize != 0) { $newsize = int (($newsize+1023) / 1024); } } elsif (($newsize =~ m/m/) || ($newsize =~ m/M/)) { $newsize =~ s/m//; $newsize =~ s/M//; } elsif (($newsize =~ m/g/) || ($newsize =~ m/G/)) { $newsize =~ s/g//; $newsize =~ s/G//; $newsize *= 1024; } elsif (($newsize =~ m/t/) || ($newsize =~ m/T/)) { $newsize =~ s/t//; $newsize =~ s/T//; $newsize *= (1024 * 1024); } else { print "invalid size multiplier specified\n"; return; } if (($newsize < $cursize) && ($usage ne "ACFS")) { my ($response) = 'x'; if (!defined($force)) { while (($response ne 'y') && ($response ne 'n')) { print "The requested size is smaller than the current size.\n"; print "Data corruption may occur.\n"; print "Are you sure? [y/n]: "; $response = ; chomp ($response); } if ($response eq 'n') { return; } } } # tell SQL that the size is in MB $newsize = $newsize . "m"; # Run SQL to resize the volume. asmcmdvol_volresize_sql ($dbh, $dgname, $volname, $newsize); } ######## # NAME # asmcmdvol_process_volset # # DESCRIPTION # This top-level routine processes the volset command. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdvol_process_cmd() can call this routine. ######## sub asmcmdvol_process_volset { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my ($ret); # asmcmdvol_parse_int_args() return value. # my ($dgname); # name of mounted diskgroup # my ($volname); # target volume name # my ($usage_string); # usage string to add to volume name # my ($mountpath); # mount path string to add to volume name # my ($primary_zone); # disk zone for primary extents (optional) # my ($mirror_zone); # disk zone for mirrored extents (optional) # my (@eargs); # Check if number of non-option parameters are correct. if (@ARGV < 3) { asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # parse options $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # get and validate diskgroup name (-G required) if (!defined($args{'G'})) { asmcmdshare_error_msg(8551, undef); return; } $dgname = $args{'G'}; if ($dgname =~ /\W/) { @eargs = ($dgname); asmcmdshare_error_msg(8552, \@eargs); return; } # get and validate volume name (last arg - required) $volname = pop(@ARGV); if ( ! $volname ) { asmcmdshare_error_msg(8553, undef); return; } if ($volname =~ /\W/) { @eargs = ($volname); asmcmdshare_error_msg(8554, \@eargs); return; } if (defined($args{'mountpath'})) { $mountpath = $args{'mountpath'}; } if (defined($args{'usagestring'})) { $usage_string = $args{'usagestring'}; } if (defined($args{'primary'})) # Get primary zone # { #Run GetOpts on the value to support first unique chars for option push(@ARGV,"--".$args{'primary'}); GetOptions (\%args, 'hot','cold'); pop(@ARGV); if(defined($args{'hot'}) ^ defined($args{'cold'})) { if(defined($args{'hot'})) { $primary_zone = 'HOT'; delete($args{'hot'}); } elsif(defined($args{'cold'})) { $primary_zone = 'COLD'; delete($args{'cold'}); } } else { # part of the user input could not be parsed asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } } if (defined($args{'secondary'})) # Get secondary zone # { #Run GetOpts on the value to support first unique chars for option push(@ARGV,"--".$args{'secondary'}); GetOptions (\%args, 'hot','cold'); pop(@ARGV); if(defined($args{'hot'}) ^ defined($args{'cold'})) { if(defined($args{'hot'})) { $mirror_zone = 'MIRRORHOT'; delete($args{'hot'}); } elsif(defined($args{'cold'})) { $mirror_zone = 'MIRRORCOLD'; delete($args{'cold'}); } } else { # part of the user input could not be parsed asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } } if (!defined($usage_string) && !defined($mountpath) && !defined($mirror_zone) && !defined($primary_zone)) { asmcmdvol_process_help($asmcmdglobal_hash{'cmd'}); return; } # Run SQL to add mountpath/usage string to the volume. asmcmdvol_volset_sql ($dbh, $dgname, $volname, $usage_string, $mountpath, $primary_zone, $mirror_zone); } ######## # NAME # asmcmdvol_process_volstat # # DESCRIPTION # This top-level routine processes the volstat command. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdvol_process_cmd() can call this routine. ######## sub asmcmdvol_process_volstat { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my ($ret); # asmcmdvol_parse_int_args() return value. # my ($sql); #SQL select command # my ($sth); # SQL statement handle. # my ($row); # One row results returned from the SQL execution. # my ($dgname); #diskgroup name # my ($volname); # volume name # my ($cur_dgname); # determines when to print dg # my ($report); # set when data is printed - used for error reporting # # validate option parameters, if any. $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); $dgname = 'UNSPECIFIED'; if (defined($args{'G'})) # Get diskgroup name. # { $dgname = $args{'G'}; tr/a-z/A-Z/ for $dgname; # must be upper case # } # Get optional volume name $volname = 'UNSPECIFIED'; if (@ARGV != 0) { $volname = pop(@ARGV); tr/a-z/A-Z/ for $volname; # must be upper case # } if (@ARGV != 0) { # part of the user input could not be parsed asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # Note: it looks like diskgroups are sorted in dg number order - which is # what we want. But, just to be sure, we enforce sort order here. In # addition, we also sort by volume name within the diskgroup. $sql = "select * from V\$ASM_DISKGROUP_STAT join V\$ASM_VOLUME_STAT on V\$ASM_DISKGROUP_STAT.GROUP_NUMBER = V\$ASM_VOLUME_STAT.GROUP_NUMBER order by V\$ASM_VOLUME_STAT.GROUP_NUMBER, V\$ASM_VOLUME_STAT.VOLUME_NAME"; # perform the SQL select $sth = asmcmdshare_do_select($dbh, $sql); return undef if (! defined ($sth)); # process the volumes $cur_dgname = 'none'; $report = 0; while (defined ($row = asmcmdshare_fetch($sth))) { if (($dgname ne 'UNSPECIFIED') && ($dgname ne $row->{'NAME'})) { # a dgname was specified on the cmd line and this row isn't it - continue next; } if (($volname ne 'UNSPECIFIED') && ($volname ne $row->{'VOLUME_NAME'})) { # a volname was specified on the cmd line and this row isn't it - continue next; } if ($cur_dgname ne $row->{'NAME'}) { my($dgnum, $dgnam); # This is the first time that we've seen this disk group, so # print the dg number and name - remember that this list is sorted. # Perl complains if the printf line wraps. $dgnum = $row->{'GROUP_NUMBER'}; $dgnam = $row->{'NAME'}; printf ("\nDISKGROUP NUMBER / NAME: %d / %s\n", $dgnum, $dgnam); print "---------------------------------------\n"; # Indicate that we've seen this diskgroup. $cur_dgname = $dgnam; # Now print the volume header - one per diskgroup. printf " VOLUME_NAME\n"; printf " %-16s%-16s%-16s%-16s\n", "READS","BYTES_READ","READ_TIME","READ_ERRS"; printf " %-16s%-16s%-16s%-16s\n", "WRITES","BYTES_WRITTEN","WRITE_TIME","WRITE_ERRS"; print " -------------------------------------------------------------\n"; } $report = 1; # we found information to print # Format and print the volume statistics. print " "; printf ("%s\n", $row->{'VOLUME_NAME'}); print " "; printf ("%-16s", $row->{'READS'}); printf ("%-16s", $row->{'BYTES_READ'}); printf ("%-16s", $row->{'READ_TIME'}); printf ("%-16s", $row->{'READ_ERRS'}); print "\n"; print " "; printf ("%-16s", $row->{'WRITES'}); printf ("%-16s", $row->{'BYTES_WRITTEN'}); printf ("%-16s", $row->{'WRITE_TIME'}); printf ("%-16s", $row->{'WRITE_ERRS'}); print "\n"; } if (!$report) { if (defined($args{'G'}) && defined($args{'show_volume'})) { printf("volume %s not found in diskgroup %s\n", $args{'show_volume'}, $args{'G'}); } elsif (defined($args{'G'})) { printf("diskgroup %s has no volumes or is not mounted\n", $args{'G'}); } elsif (defined($args{'show_volume'})) { printf("no volumes \"%s\" found in any mounted diskgroup\n", $args{'show_volume'}); } else { printf("no volumes found\n"); } } asmcmdshare_finish ($sth); } ######## # NAME # asmcmdvol_volcreate_sql # # DESCRIPTION # This routine constructs the SQL used to create an ASM volume. # It calls asmcmdshare_do_stmt() to execute it. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # dgname (IN) - name of diskgroup in which to create the volume. # volname (IN) - name of volume to be created within the diskgroup. # size (IN) - size of the volume. # redundancy (IN) - redundancy type of the volume. # size (IN) - size of the stripe width. # size (IN) - number of stripe columns. # # RETURNS # Null. ######## sub asmcmdvol_volcreate_sql { my ($dbh, $dgname, $volname, $size, $redundancy, $stripe_columns, $stripe_width, $primary_zone, $mirror_zone) = @_; my ($ret, $qry); # The minimum required of statements to the SQL command $qry = "alter diskgroup $dgname add volume '$volname' size $size"; # Optional SQL statements follow # if redundancy is not specified, you get the diskgroup default if ($redundancy ne 'default') { $qry = $qry . " $redundancy"; } # if the stripe width is not specified, you get the AVD default if ($stripe_width ne 'default') { $qry = $qry . " stripe_width $stripe_width"; } # if the stripe columns are not specified, you get the AVD default if ($stripe_columns ne 'default') { $qry = $qry . " stripe_columns $stripe_columns"; } # Add disk zone attributes, if any. if (defined($primary_zone) or defined($mirror_zone)) { $qry = $qry . " ATTRIBUTE("; if (defined($primary_zone)) { $qry = $qry . " $primary_zone"; } if (defined($mirror_zone)) { $qry = $qry . " $mirror_zone"; } $qry = $qry . ")"; } # execute the SQL statement $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); } ######## # NAME # asmcmdvol_volenable_disable_sql # # DESCRIPTION # This routine constructs the SQL used to enable/disable an ASM volume. # It calls asmcmdshare_do_stmt() to execute it. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # cmd_name (IN) - "enable" or "disable". # dgname (IN) - name of diskgroup or "all". # volname (IN) - name of volume or "all". # # RETURNS # Null. ######## sub asmcmdvol_volenable_disable_sql { my ($dbh, $cmd_name, $dgname, $volname) = @_; my ($ret, $qry); $qry = "alter diskgroup $dgname $cmd_name volume $volname"; # execute the SQL statement $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); } ######## # NAME # asmcmdvol_volresize_sql # # DESCRIPTION # This routine constructs the SQL used to resize an ASM volume. # It calls asmcmdshare_do_stmt() to execute it. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # dgname (IN) - name of diskgroup in which to resize the volume. # volname (IN) - name of volume within the diskgroup to be resized. # newsize (IN) - new size of volume. # # RETURNS # Null. ######## sub asmcmdvol_volresize_sql { my ($dbh, $dgname, $volname, $newsize) = @_; my ($ret, $qry); $qry = "alter diskgroup $dgname resize volume '$volname' size $newsize"; # execute the SQL statement $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); } ######## # NAME # asmcmdvol_voldelete_sql # # DESCRIPTION # This routine constructs the SQL used to delete an ASM volume. # It calls asmcmdshare_do_stmt() to execute it. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # dgname (IN) - name of diskgroup containing the volume. # volname (IN) - name of the volume to be deleted. # # RETURNS # Null. ######## sub asmcmdvol_voldelete_sql { my ($dbh, $dgname, $volname) = @_; my ($ret, $qry); $qry = "alter diskgroup $dgname drop volume '$volname'"; # execute the SQL statement $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); } ######## # NAME # asmcmdvol_volset_sql # # DESCRIPTION # This routine constructs the SQL used to add a usage string and/or a # mountpath to an ASM volume. It calls asmcmdshare_do_stmt() to execute it. # # PARAMETERS # dbh (IN) - initialized datavol handle, must be non-null. # dgname (IN) - name of diskgroup containing the volume. # volname (IN) - name of the volume to add the usage string to. # usage_string (IN) - usage string to add to the volume. # primary_zone (IN) - zone for the primary extents (hot|cold) # mirror_zone (IN) - zone for the mirrored extents (hot|cold) # # RETURNS # Null. ######## sub asmcmdvol_volset_sql { my ($dbh, $dgname, $volname, $usage_string, $mountpath, $primary_zone, $mirror_zone) = @_; my ($ret, $qry); $qry = "alter diskgroup $dgname modify volume '$volname'"; # Add disk zone attributes, if any. if (defined($primary_zone) or defined($mirror_zone)) { $qry = $qry . " ATTRIBUTE("; if (defined($primary_zone)) { $qry = $qry . " $primary_zone"; } if (defined($mirror_zone)) { $qry = $qry . " $mirror_zone"; } $qry = $qry . ")"; } if (defined($mountpath)) { $qry = $qry . " mountpath '$mountpath'"; } if (defined($usage_string)) { $qry = $qry . " usage '$usage_string'"; } # execute the SQL statement $ret = asmcmdshare_do_stmt($dbh, $qry); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); } ######## # NAME # asmcmdvol_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 ASMCMD volume 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 asmcmdvol_syntax_error { my ($cmd) = shift; my ($cmd_syntax); # Correct syntax for $cmd. # my ($succ) = 0; #display syntax only for commands from this module if(asmcmdvol_is_cmd($cmd)) { $cmd_syntax = asmcmdshare_get_help_syntax($cmd); # Get syntax for $cmd. # $cmd_syntax = asmcmdshare_trim_str ($cmd_syntax); # Trim blank spaces # if (defined ($cmd_syntax)) { print STDERR 'usage: ' . $cmd_syntax . "\n"; print STDERR 'help: help ' . $cmd . "\n"; $succ = 1; } if ($asmcmdglobal_hash{'mode'} eq 'n') { $asmcmdglobal_hash{'e'} = -1; } } return $succ; } ######## # NAME # asmcmdvol_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 hash list contains asmcmd volume functions that* DO* require that # an ASM instance be running - all volume commands. ######## sub asmcmdvol_is_no_instance_cmd { my ($arg) = shift; return !defined ($asmcmdvol_cmds{ $arg }) || !defined ($asmcmdvol_cmds{ $arg }{ no_instance }); } ######## # NAME # asmcmdvol_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 asmcmdvol_get_asmcmd_cmds { return asmcmdshare_print_cmds(sort(keys %asmcmdvol_cmds)); } ######## # NAME # asmcmdvol_is_cmd # # DESCRIPTION # This routine checks if a user-entered command is one of the known ASMCMD # internal commands that belong to the vol module. # # PARAMETERS # arg (IN) - user-entered command name string. # # RETURNS # True if $arg is one of the known commands, false otherwise. ######## sub asmcmdvol_is_cmd { my ($arg) = shift; return defined ( $asmcmdvol_cmds{ $arg } ); } ######## # NAME # asmcmdvol_parse_int_args # # DESCRIPTION # This routine parses the arguments for flag options for ASMCMD 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 ASMCMD internal command. ######## sub asmcmdvol_parse_int_args { my ($cmd, $args_ref) = @_; my (@string); my ($key); #build the list of options to parse using GetOptions if($asmcmdvol_cmds{ $cmd }{ flags }) { foreach $key(keys %{$asmcmdvol_cmds{ $cmd }{ flags }}) { push(@string, $key); } } # 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. # asmcmdvol_syntax_error($cmd); return undef; } return 0; } ######### # NAME # asmcmdvol_is_wildcard_cmd # # DESCRIPTION # This routine determines if an ASMCMDVOL 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 asmcmdvol_is_wildcard_cmd { my ($arg) = shift; return 0; } 1;