# Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved. # # NAME # asmcmdambr - ASM CoMmanD line interface ASM Metadata Backup and # Restore module # # DESCRIPTION # This module implements ASM Metadata Backup and Restore Utility. # # NOTES # usage: asmcmdcore [-p] [command] # # MODIFIED (MM/DD/YY) # sanselva 04/06/09 - ASMCMD long options and consistency # heyuen 10/30/08 - lrg-3668050 # heyuen 10/14/08 - use dynamic modules # rlong 08/07/08 - # atsukerm 07/17/08 - rename sage to cell # nchoudhu 07/17/08 - xbranchmerge sage from pt.oos2 # heyuen 03/27/08 - Backport heyuen_bug-6487591 from main # mpopeang 08/07/08 - bug7259156: backup/restore messages # heyuen 07/28/08 - use command properties array # nchoudhu 07/21/08 - xbranchmerge sage from 11.1.0.7 # heyuen 04/15/08 - reorder help messages # heyuen 02/01/08 - enable restore dirs inside system dirs # heyuen 11/28/07 - add sage_only attribute for diskgroup restore # heyuen 09/12/07 - change error msgs to asmcmdshare # heyuen 05/25/07 - add return codes for errors # pbagal 04/13/07 - Add ASMCMD comment in all SQL # heyuen 03/14/07 - add backup of v$asm_attribute # hqian 03/09/07 - fix identation and comments # hqian 03/02/07 - fix md_restore flag -l and messages # hqian 07/20/06 - #5397026: new asmcmdglobal_no_instance_callbacks # msharang 04/20/06 - Add creation of SQL file during restore # msharang 04/20/06 - Override options must match diskgroups # discovered # msharang 04/20/06 - Fix restore for multiple diskgroups # msharang 04/06/06 - Better error checking # msharang 03/28/06 - Creation # ############################################################################# # ############################ Functions List ################################# # ############################################################################# package asmcmdambr; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(asmcmdambr_init asmcmdambr_process_cmd asmcmdambr_process_help ); use strict; use Cwd; use Getopt::Long qw(:config no_ignore_case bundling); use Data::Dumper; use IO::Handle; use asmcmdglobal; use asmcmdshare; use List::Util qw[min max]; ####################### ASMCMDAMBR Global Constants ###################### our (%asmcmdambr_cmds) = ('md_backup' => {no_instance => 'True', flags => {'G=s'=>'diskGroup'} }, 'md_restore' => {no_instance => 'True', flags => {'silent'=> 'ignoreErrors', 'full'=> 'fullRestore', 'nodg'=> 'metadataRestore', 'newdg'=> 'newdgMetadataRestore', 'S=s'=> 'sqlScriptFile', 'o=s'=> 'oldDiskGroup', 'G=s'=>'diskGroup'} } ); ####################### ASMCMDAMBR Global Variables ###################### our (%supported_types) = (); #needed to finish the file with a non-negative assignment our $true_dummy = 1; sub is_asmcmd { return 1; } ######## # NAME # asmcmdambr_init # # DESCRIPTION # This function initializes the asmcmdambr 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, \&asmcmdambr_process_cmd); push (@asmcmdglobal_help_callbacks, \&asmcmdambr_process_help); push (@asmcmdglobal_command_list_callbacks, \&asmcmdambr_get_asmcmd_cmds); push (@asmcmdglobal_is_command_callbacks, \&asmcmdambr_is_cmd); push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdambr_is_wildcard_cmd); push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdambr_syntax_error); push (@asmcmdglobal_no_instance_callbacks, \&asmcmdambr_is_no_instance_cmd); push (@asmcmdglobal_error_message_callbacks, \&asmcmdambr_error_msg); push (@asmcmdglobal_signal_exception_callbacks, \&asmcmdambr_signal_exception); %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdambr_cmds); #Perform ASMCMD consistency check if enabled if($asmcmdglobal_hash{'consistchk'} eq 'y') { if(!asmcmdshare_check_option_consistency(%asmcmdambr_cmds)) { exit 1; } } } ######## # NAME # asmcmdambr_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 asmcmdambr module; 0 if not. # # NOTES # Only asmcmdcore_shell() calls this routine. ######## sub asmcmdambr_process_cmd { my ($dbh) = @_; my ($succ) = 0; # Get current command from global value, which is set by # asmcmdambr_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 ASMCMDAMBR command. my (%cmdhash) = ( md_backup => \&asmcmdambr_process_backup , md_restore => \&asmcmdambr_process_restore ); if (defined ( $cmdhash{ $cmd } )) { # If user specifies a known command, then call routine to process it. # $cmdhash{ $cmd }->($dbh); $succ = 1; } return $succ; } ######## # NAME # asmcmdambr_process_backup # # DESCRIPTION # This function performs AMBR backup operation. It connects to ASM instance # and gathers enough information about the diskgroups we are interested # in, creates an intermediate file, that stores this information to be # processed later by restore operation. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdambr_process_cmd() calls this function. ######## sub asmcmdambr_process_backup { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my (%norm); # See asmcmdshare_normalize_path() return value comments. # my (@eargs); # Array of error arguments. # my ($ret); # asmcmdambr_parse_int_args() return value. # my ($path); # Create directory under this path. # my ($sth, $qry, $row); # SQL query statement. # my (@diskgroup_names); # Name of diskgroup to be backed up # my ($dgnamesstr); # Diskgroup names string as specified by user # my (@diskgroup_set); # Top level hash table that stores all dg info # my ($i); # Iterator # my ($curpos); # my ($FILE); # File handle for intermediate file # my ($intermediate_file_name); my ($max_key); my ($dgname); # current diskgroup name # my ($stmt); # stmt for querying # # Get the supported files in ASM $stmt = 'select description from x$ksfdftyp'; $ret = asmcmdshare_do_select($dbh, $stmt); if (!defined($ret)) { asmcmdshare_error_msg(9352, $DBI::errstr); return; } while (defined ($row = asmcmdshare_fetch($sth))) { $supported_types{uc($row->{'NAME'})} = 1; } # Get option parameters, if any $ret = asmcmdambr_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); # Check if number of non-option parameters are correct. if (@ARGV != 1) { asmcmdambr_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # -b options is deprecated as this argument is mandatory bug#6960868 $intermediate_file_name = shift (@ARGV); # Store the information fetched in intermediate file to be # used later as part of AMBR restore # We open using mode '>' means open for write only, and clobber existing file # so test if a file already exists if ( -e $intermediate_file_name) { asmcmdshare_error_msg(9357, $intermediate_file_name); return; } if (defined($args{'G'})) { # User has specified one or more diskgroups using -g option. # We will perform backup of only those diskgroups. # Process the ',' separated list of diskgroup names $dgnamesstr = uc($args{'G'}); $dgnamesstr =~ s/\s+//g; # stripe off any white spaces @diskgroup_names = split /,/, $dgnamesstr; # delimiter is ',' foreach (@diskgroup_names) { print "Disk group metadata to be backed up: $_\n"; } } else { # By default backup all diskgroups mounted by this instance $qry = 'select NAME from v$asm_diskgroup where state=\'MOUNTED\''; $sth = asmcmdshare_do_select($dbh, $qry); while (defined ($row = asmcmdshare_fetch($sth))) { push (@diskgroup_names, uc($row->{'NAME'})); } if (!@diskgroup_names) { # No mounted diskgroups found, nothing can be done after this, # print error and return asmcmdshare_error_msg(9351, undef); return; } foreach (@diskgroup_names) { print "Disk group metadata to be backed up: $_\n"; } } # Now that we know what diskgroups to work on, define data structure that # will hold information about the diskgroups, then execute SQL commands # to gather information about the diskgroups. foreach $dgname (@diskgroup_names) { my ($current_diskgroup_number); my (%diskgroup_info); # Diskgroup info record # my (%disks_info); # Disks info record # my (%disks_info_array); # Disks info record array # my (%attribute_info_array); # Attribute info record array # my (%alias_info); # Alias info record # my (%alias_info_array); # Alias info record array # my ($alias_info_array_length); # Length of Alias info record array # my (%template_info); # Template info record # my (%template_info_array); # Template info record array # my (%diskgroup_record); # Record to store information for this dg # my ($iter1); my ($iter2); my ($temp); my (%to_delete); # Fetch diskgroup information $qry = 'select GROUP_NUMBER, TYPE, COMPATIBILITY, DATABASE_COMPATIBILITY, ALLOCATION_UNIT_SIZE, STATE from v$asm_diskgroup WHERE NAME=\'' . $dgname . '\''; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); if (!defined($row)) { # Could not find any row for current diskgroup, maybe user specified # incorrect diskgroup name, print error and fail command; syntax error. asmcmdshare_error_msg(9349, $dgname); return; } if (uc($row->{'STATE'}) eq 'DISMOUNTED') { # The diskgroup is dismounted, print error message and fail command; # syntax error. asmcmdshare_error_msg(9350, $dgname); return; } $current_diskgroup_number = $row->{'GROUP_NUMBER'}; %diskgroup_info = ( DGNAME => $dgname, DGTYPE => $row->{'TYPE'}, DGTORESTORE => 0, DGCOMPAT => $row->{'COMPATIBILITY'}, DGDBCOMPAT => $row->{'DATABASE_COMPATIBILITY'}, DGAUSZ => $row->{'ALLOCATION_UNIT_SIZE'}, ); # Fetch Disk information # We fetch information for all disks, not just MEMBER disks, # which means during restore time, we will try to create a diskgroup # with all those disks. If user wants to do some filtering, he always # has the option of creating an output SQL file as part of restore # and editing it. # Should we look only for MEMBER disks ?? $qry = 'select d.NAME, d.PATH, d.TOTAL_MB, d.FAILGROUP from v$asm_disk d, v$asm_diskgroup g where g.GROUP_NUMBER = d.GROUP_NUMBER and g.NAME=\'' .$dgname . '\''; $sth = asmcmdshare_do_select($dbh, $qry); # We group disks based on their failgroups, so we have a complex # structure which has (1) A level 3 Hash table for all disks # (disks_info_array) each entry of which is (2) a level 2 Hash table # of failgroups (disks_in_failgroup) each entry of which is (3) # a level 1 hash table per disk that has the v$asm_disk column names # as its keys and stores the corresponding column values. while (defined ($row = asmcmdshare_fetch($sth))) { my (%disks_in_failgroup_table); my ($disks_in_failgroup); #reference if (defined($disks_info_array{$row->{'FAILGROUP'}})) { $disks_in_failgroup = $disks_info_array{$row->{'FAILGROUP'}}; } else { $disks_in_failgroup = \%disks_in_failgroup_table; } # One table per disk $disks_info{'DGNAME'} = $dgname; $disks_info{'NAME'} = $row->{'NAME'}; $disks_info{'PATH'} = $row->{'PATH'}; $disks_info{'TOTAL_MB'} = $row->{'TOTAL_MB'}; $disks_info{'FAILGROUP'} = $row->{'FAILGROUP'}; # One table per failgroup $disks_in_failgroup->{$row->{'NAME'}} = { %disks_info }; # One table per diskgroup for all failgroups in it $disks_info_array{$row->{'FAILGROUP'}} = { %{$disks_in_failgroup} }; }# end while # # Fetch attribute information # Just for ASM diskgroup compatibility versions 11.1 or newer if ( asmcmdshare_version_cmp($diskgroup_info{'DGCOMPAT'}, "11.1.0.0.0") >= 0 ) { $qry = 'select a.GROUP_NUMBER, a.NAME, a.VALUE from v$asm_attribute a, v$asm_diskgroup g where g.GROUP_NUMBER = a.GROUP_NUMBER and g.NAME=\'' .$dgname . '\''; $sth = asmcmdshare_do_select($dbh, $qry); while (defined($row = asmcmdshare_fetch($sth))) { $attribute_info_array{ uc($row->{'NAME'}) } = $row->{'VALUE'}; } # end while # } # Fetch alias information # We are only interested in user created aliase directories $qry = 'select a.GROUP_NUMBER, a.NAME, a.REFERENCE_INDEX from v$asm_alias a, v$asm_diskgroup g where g.GROUP_NUMBER = a.GROUP_NUMBER and a.ALIAS_DIRECTORY=\'Y\' and g.NAME=\'' .$dgname . '\''; $sth = asmcmdshare_do_select($dbh, $qry); $curpos = 0; # First pass, store only alias name temporarily while (defined ($row = asmcmdshare_fetch($sth))) { $alias_info{'DGNAME'} = $dgname; $alias_info{'ALIASNAME'} = $row->{'NAME'}; $alias_info{'REFERENCE_INDEX'} = $row->{'REFERENCE_INDEX'}; $alias_info{'LEVEL'} = 0; # To be used for sorting entries later $alias_info_array{$curpos} = { %alias_info }; $curpos++; } # end while # $alias_info_array_length = $curpos; # We need to build full path for the alias directory in order to be # able to re-create it at a later point. foreach $curpos (keys %alias_info_array) { my ($current_alias_name) = $alias_info_array{$curpos}{'ALIASNAME'}; my ($current_reference_index) = $alias_info_array{$curpos}{'REFERENCE_INDEX'}; my ($current_parent_index); my ($current_alias_path); my ($level); $qry = 'select PARENT_INDEX from v$asm_alias where group_number= ' . $current_diskgroup_number . ' and REFERENCE_INDEX=' . $current_reference_index . ' and name=\''. $current_alias_name . '\''; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $current_parent_index = $row->{'PARENT_INDEX'}; $current_alias_path = $current_alias_name; # Build full path name for the alias directory starting from alias # name and working upwards towards diskgroup name $level = 0; for (;;) { if ($current_parent_index == ($current_diskgroup_number << 24)) { # We have reached diskgroup level, prepend diskgroup name # to the path and break out of loop last; } $qry = 'select NAME, PARENT_INDEX from v$asm_alias where GROUP_NUMBER= ' . $current_diskgroup_number . ' and REFERENCE_INDEX=' . $current_parent_index; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $current_alias_path = $row->{'NAME'} . '/' . $current_alias_path; $current_parent_index = $row->{'PARENT_INDEX'}; $level++; } # end for # print "Current alias directory path: $current_alias_path\n"; $alias_info_array{$curpos}{'ALIASNAME'} = $current_alias_path; $alias_info_array{$curpos}{'LEVEL'} = $level; } #discard the directories of filetype names #select the entries to delete foreach $curpos (keys %alias_info_array) { my ($path, @path_arr); $path = $alias_info_array{$curpos}{'ALIASNAME'} . "\n"; $path =~ s/\s+$//; @path_arr = split (/\//, $path); if ($#path_arr == 1) { if (defined($supported_types{$path_arr[1]})) { $to_delete{$curpos} = 1; } } } $max_key = $alias_info_array_length; #delete the records foreach (keys %to_delete) { delete ($alias_info_array{$_}); $alias_info_array_length = $alias_info_array_length - 1; } # coalesce the alias_info_array, filling the holes with existing entries for ($iter1 = 1; $iter1 < $max_key; $iter1++) { if (defined($alias_info_array{$iter1})) { next; } for ($iter2 = $iter1+1; !defined($alias_info_array{$iter2}) && $iter2 < $max_key; $iter2++){}; if (defined($alias_info_array{$iter2})) { $alias_info_array{$iter1} = $alias_info_array{$iter2}; } delete($alias_info_array{$iter2}); } # Sort alias info array based on level, we do this because restore # operation needs to restore alias directory based on level since # parent directory needs to be restored before any of its children # are restored. for ($iter1 = 0; $iter1 < $alias_info_array_length; $iter1++) { for ($iter2 = 0; $iter2 < $alias_info_array_length - 1; $iter2++) { if ($alias_info_array{$iter2}{'LEVEL'} > $alias_info_array{$iter2+1}{'LEVEL'} ) { $temp = $alias_info_array{$iter2}; $alias_info_array{$iter2} = $alias_info_array{$iter2+1}; $alias_info_array{$iter2+1} = $temp; } }#end fop iter2 }#end for iter1 # Fetch template information $qry = 'select g.GROUP_NUMBER, t.NAME, t.REDUNDANCY, t.STRIPE, t.SYSTEM from v$asm_template t, v$asm_diskgroup g where g.GROUP_NUMBER = t.GROUP_NUMBER and g.NAME=\'' .$dgname . '\''; $sth = asmcmdshare_do_select($dbh, $qry); $curpos = 0; while (defined ($row = asmcmdshare_fetch($sth))) { $template_info{'DGNAME'} = $dgname; $template_info{'TEMPNAME'} = $row->{'NAME'}; $template_info{'REDUNDANCY'} = $row->{'REDUNDANCY'}; $template_info{'STRIPE'} = $row->{'STRIPE'}; $template_info{'SYSTEM'} = $row->{'SYSTEM'}; $template_info_array{$curpos} = { %template_info }; $curpos++; } #end while # # Create final record for this diskgroup # If the instance version is greater than 11.1, then it has Attributes # that means that attribute_info_array has entries if ( keys(%attribute_info_array) ) { %diskgroup_record = ( DGINFO => { %diskgroup_info }, DISKSINFO => { %disks_info_array }, ATTRINFO => { %attribute_info_array }, ALIASINFO => { %alias_info_array }, TEMPLATEINFO => { %template_info_array }, ); } else { %diskgroup_record = ( DGINFO => { %diskgroup_info }, DISKSINFO => { %disks_info_array }, ALIASINFO => { %alias_info_array }, TEMPLATEINFO => { %template_info_array }, ); } # Store this record in the array containing records for all diskgroups push (@diskgroup_set, \%diskgroup_record); } # end for # # We untaint/tell perl to trust the user specified intermediate file name # by matching it with "match all" regular expression and resetting the # variable to the match. This is not a safe way to untaint outside data # but we already checked that the intermediate file specified by user does not # already exist, thus making sure that we will not accidently overwrite # an existing file/directory, hence it is safe to do so at this point. ($intermediate_file_name) = $intermediate_file_name =~ m/(.*)/; if (!open (FILE, ">", $intermediate_file_name)) { @eargs = ($intermediate_file_name, $!); asmcmdshare_error_msg(9345, \@eargs); return; } # Write string version of diskgroup data to intermediate file print FILE Data::Dumper->Dump([\@diskgroup_set], ['*diskgroup_set']); close (FILE); return; } ######## # NAME # asmcmdambr_process_restore # # DESCRIPTION # This function performs AMBR restore operation. It reads and interprets # the input restore file and version of the diskgroup to be restored, # and then for all diskgroups to be restored, it creates appropriate # restore commands depending on the version. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # # RETURNS # Null. # # NOTES # Only asmcmdambr_process_cmd() can call this routine. ######## sub asmcmdambr_process_restore { my ($dbh) = shift; my (%args); # Argument hash used by getopts(). # my (@eargs); # Array of error arguments. # my ($ret); # asmcmdambr_parse_int_args() return value. # my ($qry); # SQL query statement. # my ($stmt); # SQL command. # my ($defreadmode); # Default file read mode # my (@diskgroup_names); # Name of diskgroup to be restored # my ($dgnamesstr); # Diskgroup names string as specified by user # my (@diskgroup_set); # Record to store information for this diskgroup # my ($i); # Iterator # my ($FILE); # File handle for intermediate file # my ($SQLFILE); # File handle for restore SQL file # my ($intermediate_file_name); # Intermediate file name # my ($restoremode) = 'full'; # Mode of restore operation # my ($overridestr); # Override string # my (@override_options); # Override options : # my (%diskgroups_replace); my ($ignore_errors) = 0; # Whether to proceed with next fiskgroups on error # my ($create_restore_sql) = 0; # Execute restore or dump restore SQL to file # my ($restore_sql_file_name); # SQL file where to write the restore commands # my ($file_content); # Contents of the intermediate file. # my ($asm_instance_version); # ASM instance version # my ($compatible_rdbms); my ($compatible_asm); my ($au_size); my ($sec_size); my ($smart_scan_capable); # Get the ASM version of the instance we are connected to $asm_instance_version = asmcmdshare_get_asm_version($dbh); return unless defined ($asm_instance_version); # Get option parameters, if any. $ret = asmcmdambr_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); # Check if number of non-option parameters are correct. if (@ARGV != 1) { asmcmdambr_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # -b options is deprecated as this argument is mandatory bug#6960868 # Read Intermediate file into memory. $intermediate_file_name = shift (@ARGV); if (!open (FILE, "< $intermediate_file_name")) { # Cannot open intermediate file, nothing can be done, print error # and exit @eargs = ($intermediate_file_name, $!); asmcmdshare_error_msg(9345, \@eargs); return; } FILE->untaint(); # Remember the default read mode # $defreadmode = $/; # Read in file all at once # undef $/; $file_content = ; # Restore the default # $/ = $defreadmode; close (FILE); if ($file_content !~ /^\@diskgroup_set/) { # The intermediate file must begin with the string "@diskgroup_set". # If not, then we must not have a valid intermediate file. asmcmdshare_error_msg(9347, $intermediate_file_name); return; } # Now evaluate the file contents into an array for hashes. @diskgroup_set = eval $file_content; if ($@) { # Error evaluating the intermediate file, print error and return. asmcmdshare_error_msg(9347, $intermediate_file_name); return; } if (!@diskgroup_set) { # Backup file specified by user is empty or cannot be interpreted. asmcmdshare_error_msg(9356, $intermediate_file_name); return; } # The following section checks the sanity of the command parameters. # Errors will not be ignored. if (defined($args{'G'})) { my ($backup_diskgroup_name); # Name of a disk group that was backed up. # my ($found) = 0; # Whether or not a disk group in the list is found. # my ($iter1); my ($iter2); # User has specified one or more diskgroups using -g option. # We will perform backup of only those diskgroups. # Process the ',' separated list of diskgroup names. $dgnamesstr = uc($args{'G'}); $dgnamesstr =~ s/\s+//g; # stripe off any white spaces. # @diskgroup_names = split (/,/, $dgnamesstr); # delimiter is ',' . # # If user has specified list of diskgroups to restore, restore only them. # Make sure that we find information for those diskgroups in backup file, # if any one is missing, we print error and exit. for ($iter1 = 0; $iter1 < @diskgroup_names; $iter1++) { $found = 0; # Now look for the user-specified disk group in the backup intermediate # file. for ($iter2 = 0; $iter2 < @diskgroup_set; $iter2++) { $backup_diskgroup_name = $diskgroup_set[$iter2]{DGINFO}{DGNAME}; if ($diskgroup_names[$iter1] eq $backup_diskgroup_name) { $found = 1; $diskgroup_set[$iter2]{DGINFO}{DGTORESTORE} = 1; last; # break out of loop } } # end for iter2 # if (!$found) { # User specified a diskgroup to be restored but we could not find # information for that diskgroup in backup file. Print an error # and exit. asmcmdshare_error_msg(9355, $diskgroup_names[$iter1]); return; } } # end for iter1 # } else { # No -G flag specified. # my ($iter2); # By default restore all diskgroups specified in backup file. undef $dgnamesstr; for ($iter2 = 0; $iter2 < @diskgroup_set; $iter2++) { $diskgroup_set[$iter2]{DGINFO}{DGTORESTORE} = 1; } } if (!defined($args{'newdg'}) && defined($args{'o'})) { # Override option can only be specified with -t newdg asmcmdshare_error_msg(9348, undef); asmcmdambr_syntax_error($asmcmdglobal_hash{'cmd'}); return; } if (defined($args{'newdg'}) && !defined($args{'o'})) { # Option 'newdg' specified with no override options; # it's a syntax error; fail command. asmcmdshare_error_msg(9358, undef); return; } if (defined($args{'o'})) { # Currently only a replacement diskgroup name can be specified # using override option, only valid syntax is # -o ':,...', check input for syntax $overridestr = uc($args{'o'}); $overridestr =~ s/\s+//g; # stripe off any white spaces. # @override_options = split (/,/, $overridestr); # delimiter is ','. # # Loop through the override options one at a time. for ($i = 0; $i < @override_options; $i++) { my ($olddg); my ($newdg); my ($iter2); my ($found); $override_options[$i] = uc($override_options[$i]); ($olddg, $newdg) = split (/:/, $override_options[$i]); # Make sure the old diskgroup name specified in override options # actually matches some diskgroup to be restored. for ($iter2 = 0; $iter2 < @diskgroup_set; $iter2++) { if (($diskgroup_set[$iter2]{DGINFO}{DGTORESTORE} == 1) && uc($diskgroup_set[$iter2]{DGINFO}{DGNAME}) eq $olddg) { $found = 1; last; } } if (!$found) { # Invalid Diskgroup name specified in override options # Print error and exit asmcmdshare_error_msg(9359, $olddg); return; } $diskgroups_replace{$olddg} = $newdg; } } if(defined($args{'newdg'}) || defined($args{'full'}) || defined($args{'nodg'})) { my ($newdg) = defined($args{'newdg'}); my ($nodg) = defined($args{'nodg'}); my ($full) = defined($args{'full'}); if(($newdg+$nodg+$full) != 1) { # Not a valid value for mode of operation asmcmdambr_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $restoremode = 'newdg' if(defined($args{'newdg'})); $restoremode = 'nodg' if(defined($args{'nodg'})); $restoremode = 'full' if(defined($args{'full'})); } if (defined($args{'silent'})) { # User has asked us to proceed with other diskgroups in case of # error with current one, by default we exit on occurrence of an # error. $ignore_errors = 1; } if (defined($args{'S'})) { # User has asked us to dump SQL commands for ASM restore to an # output SQL file instead of doing the restore. $create_restore_sql = 1; $restore_sql_file_name = $args{'S'}; # The file cannot already exist. We don't overwrite existing files. if ( -e $restore_sql_file_name) { asmcmdshare_error_msg(9357, $restore_sql_file_name); return; } # Untaint the input ($restore_sql_file_name) = $restore_sql_file_name =~ m/(.*)/; if ( !($restore_sql_file_name =~ /([\w]+)(\.[\w+])*/) ) { $restore_sql_file_name = ""; } if (!open (SQLFILE, "> $restore_sql_file_name")) { @eargs = ($restore_sql_file_name, $!); asmcmdshare_error_msg(9345, \@eargs); return; } SQLFILE->untaint(); } # End parameter checking section. # New section begins here: restore the disk groups; errors can be ignored # if the -i flag is set. # We fetched information about diskgroups to work on, now look at them # one at a time and re-create the ones user wants us to re-create. for ($i = 0; $i < @diskgroup_set; $i++) { my $old_diskgroup_name = $diskgroup_set[$i]{DGINFO}{DGNAME}; my $new_diskgroup_name; my $current_redundancy = $diskgroup_set[$i]{DGINFO}{DGTYPE}; my $disks_info_array = $diskgroup_set[$i]{DISKSINFO}; #reference my $attribute_info_array; my $alias_info_array = $diskgroup_set[$i]{ALIASINFO}; #reference my $template_info_array = $diskgroup_set[$i]{TEMPLATEINFO}; #reference my $template; my $alias; my $found; my $iter1; my $attribute_name; my $diskgroup_compatibility = $diskgroup_set[$i]{DGINFO}{DGCOMPAT}; # If the ASM version is smaller than the diskgroup compatibility # of the backup, then we can't restore. if (asmcmdshare_version_cmp($asm_instance_version, $diskgroup_compatibility) < 0) { asmcmdshare_error_msg(9361, $diskgroup_compatibility); return; } # If the diskgroup is version 11.1 or more, it has an # attribute directory to restore if (asmcmdshare_version_cmp($diskgroup_compatibility, "11.1.0.0.0") >= 0) { $attribute_info_array = $diskgroup_set[$i]{ATTRINFO}; #reference } # If the user specified the -g option, then make sure the group # we're trying to restore is actually one of the ones the user # specified to restore using the -g option. if (defined($dgnamesstr)) { $found = 0; for ($iter1 = 0; $iter1 < @diskgroup_names; $iter1++) { if ($diskgroup_names[$iter1] eq $old_diskgroup_name) { $found = 1; last; # break out of loop } } # If the group is not one that the user specified, skip it. if (!$found) { next; } } print "Current Diskgroup metadata being restored: $old_diskgroup_name\n"; if ($restoremode eq 'newdg' && defined($diskgroups_replace{$old_diskgroup_name})) { # Replace diskgroup name and disks/failgroups names with new # values in diskgroup_set # $$$ implement replace disks/failgroups names $new_diskgroup_name = uc($diskgroups_replace{$old_diskgroup_name}); print "Current Diskgroup name replace by: $new_diskgroup_name\n"; } else { $new_diskgroup_name = uc($old_diskgroup_name); } # Recreate the disk group if -t nodg is not specified. if ($restoremode ne 'nodg') { my ($first_disk_in_failgroup); # Boolean to include FAILGROUP keyword. # my ($current_failgroup_disks); # Iterator for failgroups in disk group. # my ($current_disk); # Iterator for disks in a failgroup. # my ($current_disk_info); # Pointer to hash of disk information. # my ($current_failgroup_disks_table); # Pointer to hash of disks in FG. # # We have to create the diskgroup # Create SQL command grouping disks based on failgroups if (uc($current_redundancy) eq 'EXTERN') { $current_redundancy = 'EXTERNAL'; } $stmt = 'create diskgroup ' . $new_diskgroup_name . ' ' . $current_redundancy . ' redundancy '; # Iterate through the list of failgroups in the disk group and # format them into the disk group creation SQL. foreach $current_failgroup_disks (keys %{$disks_info_array}) { # Set to TRUE so that we print the FAILGROUP and DISK keywords. $first_disk_in_failgroup = 1; # Set pointer to hash of disks in current failgroup indexed by # $current_failgroup_disks. $current_failgroup_disks_table = $disks_info_array->{$current_failgroup_disks}; # Iterate through the list of disks in the failgroup and format # them into the disk group creation SQL. foreach $current_disk (keys %{$current_failgroup_disks_table}) { # Set pointer to disk information hash. $current_disk_info = $current_failgroup_disks_table->{$current_disk}; # Include the FAILGROUP and DISK keywords only once--at the # very beginning. if ($first_disk_in_failgroup) { # The FAILGROUP keyword can be included only if the disk group # is not an EXTERNAL redundancy disk group. if ($current_redundancy ne 'EXTERNAL') { $stmt .= 'failgroup ' . $current_disk_info->{'FAILGROUP'} . ' disk '; } else { $stmt .= ' disk '; } $first_disk_in_failgroup = 0; } else { # Comma separate the disk names if not the first disk # in disk group. $stmt .= ', '; } # Now include the disk information in the recreation SQL # statement for the disk in question. $stmt .= '\'' . $current_disk_info->{'PATH'} . '\' name ' . $current_disk_info->{'NAME'} . ' size ' . $current_disk_info->{'TOTAL_MB'} . 'M '; } } # Restore au_size, compatible.asm, and compatible.rdbms # if the diskgroup is version 11.1 or higher if( asmcmdshare_version_cmp($diskgroup_compatibility, "11.1.0.0.0" ) >= 0) { #take the parameters from disk group creation and delete #them from the array $compatible_asm = $attribute_info_array->{'COMPATIBLE.ASM'}; $compatible_rdbms = $attribute_info_array->{'COMPATIBLE.RDBMS'}; $au_size = $attribute_info_array->{'AU_SIZE'}; delete($attribute_info_array->{'COMPATIBLE.ASM'}); delete($attribute_info_array->{'COMPATIBLE.RDBMS'}); delete($attribute_info_array->{'AU_SIZE'}); $stmt .= 'attribute \'compatible.asm\' = \'' . $compatible_asm . '\' , \'compatible.rdbms\' = \'' . $compatible_rdbms . '\' , \'au_size\' = \'' . $au_size . '\''; if (defined($attribute_info_array->{'SECTOR_SIZE'})) { $sec_size = $attribute_info_array->{'SECTOR_SIZE'}; delete($attribute_info_array->{'SECTOR_SIZE'}); $stmt .= ', \'sector_size\' = \''. $sec_size . '\''; } if (defined($attribute_info_array->{'CELL.SMART_SCAN_CAPABLE'})) { $smart_scan_capable = $attribute_info_array->{'CELL.SMART_SCAN_CAPABLE'}; delete($attribute_info_array->{'CELL.SMART_SCAN_CAPABLE'}); $stmt .= ', \'cell.smart_scan_capable\' = \''. $smart_scan_capable . '\''; } } if ($create_restore_sql) { # If we are creating a SQL file, simply print the disk group # creation SQL statement to it. print SQLFILE "$stmt" . ";\n"; } else { # Otherwise, execute the SQL statement to re-create the disk group. $ret = asmcmdshare_do_stmt($dbh, $stmt); if (!defined($ret)) { # Creation of diskgroup failed, nothing can be further done for # this diskgroup; print error and proceed to next diskgroup # if -i flag is specified by user, else exit. asmcmdshare_error_msg(9352, $DBI::errstr); if ($ignore_errors) { next; } else { return; } } print "Diskgroup $new_diskgroup_name created!\n"; } } # end if restoremode ne 'nodg' # # At this point we have either created required diskgroup or mounted # the diskgroup if 'nodg' option is specified, now restore attributes, # alias and template directories # Restore Attributes if( asmcmdshare_version_cmp($diskgroup_compatibility, "11.1.0.0.0" ) >= 0) { foreach $attribute_name (keys %{$attribute_info_array}) { my ($attribute_value)=$attribute_info_array->{$attribute_name}; $stmt = 'alter diskgroup /*ASMCMD AMBR*/' . $new_diskgroup_name . ' set attribute \'' . $attribute_name . '\' = \''. $attribute_value . '\''; if ($create_restore_sql) { # If we are creating a SQL file, simply print the disk group # creation SQL statement to it. print SQLFILE "$stmt" . ";\n"; } else { # Otherwise, execute the SQL statement to modify or re-create the # attributes. $ret = asmcmdshare_do_stmt($dbh, $stmt); if (!defined($ret)) { # Create/alter attribute failed, print error. asmcmdshare_error_msg(9360, $DBI::errstr); if ($ignore_errors) { next; } else { return; } print "Attribute $attribute_name created!\n"; } } } } # Restore Templates foreach $template (keys %{$template_info_array}) { my ($current_template_name) = $template_info_array->{$template}{'TEMPNAME'}; my ($current_template_redundancy) = $template_info_array->{$template}{'REDUNDANCY'}; my ($current_template_stripe) = $template_info_array->{$template}{'STRIPE'}; my ($alter_or_add); if (uc($template_info_array->{$template}{'SYSTEM'}) eq 'Y') { # System template, it should have already been created as part # of 'create diskgroup' command, # execute alter statements for all template attributes $alter_or_add = 'alter'; } else { # User created template, create a new one $alter_or_add = 'add'; } # The keyword for unprotected in the v$ view is different from # the SQL keyword, so modify it. if (uc($current_template_redundancy) eq 'UNPROT') { $current_template_redundancy = 'UNPROTECTED'; } # Now construct the SQL command to modify or recreate the # the templates. $stmt = 'alter diskgroup /*ASMCMD AMBR*/' . $new_diskgroup_name . ' ' . $alter_or_add . ' template ' . $current_template_name . ' attributes (' . $current_template_redundancy . ' ' . $current_template_stripe . ')'; if ($create_restore_sql) { # If we are creating a SQL file, simply print the disk group # creation SQL statement to it. print SQLFILE "$stmt" . ";\n"; } else { # Otherwise, execute the SQL statement to modify or re-create the # templates. $ret = asmcmdshare_do_stmt($dbh, $stmt); if (!defined($ret)) { # Create/alter template failed, print error. asmcmdshare_error_msg(9353, $DBI::errstr); if ($ignore_errors) { next; } else { return; } } if ($alter_or_add eq 'add') { print "User template $current_template_name created!\n"; } else { print "System template $current_template_name modified!\n"; } } } # end for templates # # Restore Alias directories # foreach $alias (keys %{$alias_info_array}) # Note: ambr currently restores only user-created alias directories. # System created directories or filenames obviously cannot # be restored this way. User-created aliases also cannot be # restored because the system-created filename is likely # unknown at this point in time. # if a user directory is created inside a system created directory, # it will be restored through dbms_diskgroup for ($alias = 0; $alias < (keys %{$alias_info_array}); $alias++) { # Get alias directory name. my ($current_alias_directory_name) = $alias_info_array->{$alias}{'ALIASNAME'}; my @path = split(/\//,$current_alias_directory_name); my $type; if (defined($path[1]) && defined($supported_types{uc($path[1])})) { # this is a system created directory # Construct full-path of the directory. $current_alias_directory_name = '+' . $new_diskgroup_name . '/' . $current_alias_directory_name; $stmt = 'begin' . "\n"; $stmt .= 'dbms_diskgroup.createdir(\'' . $current_alias_directory_name . '\');' . "\n"; $stmt .= "end;\n/"; } else { # Construct full-path of the directory. $current_alias_directory_name = '+' . $new_diskgroup_name . '/' . $current_alias_directory_name; # Construct the SQL to re-create the user-specified alias directory. $stmt = 'alter diskgroup /*ASMCMD AMBR */ ' . $new_diskgroup_name . ' add directory \'' . $current_alias_directory_name . '\';'; } if ($create_restore_sql) { print SQLFILE "$stmt" . "\n"; } else { $stmt =~ s/\;$//; $ret = asmcmdshare_do_stmt($dbh, $stmt); if (!defined($ret)) { # Create alias directory failed, print error. asmcmdshare_error_msg(9354, $DBI::errstr); if ($ignore_errors) { next; } else { return; } } print "Directory $current_alias_directory_name re-created!\n"; } } # end for alias # } # end for # # Close restore SQL file if ($create_restore_sql) { close (SQLFILE); } return; } ######## # NAME # asmcmdambr_process_help # # DESCRIPTION # This function is the help function for the ASMCMDAMBR module. # # PARAMETERS # command (IN) - display the help message for this command. # # RETURNS # 1 if command found; 0 otherwise. ######## sub asmcmdambr_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 (asmcmdambr_is_cmd ($command)) { # User specified a command name to look up. # $syntax = asmcmdambr_get_cmd_syntax($command); $desc = asmcmdambr_get_cmd_desc($command); print " $syntax\n" . "$desc\n"; $succ = 1; } return $succ; } ######## # NAME # asmcmdambr_is_cmd # # DESCRIPTION # This routine checks if a user-entered command is one of the known # ASMCMD internal commands that belong to the ASMCMDAMBR module. # # PARAMETERS # arg (IN) - user-entered command name string. # # RETURNS # True if $arg is one of the known commands, false otherwise. ######## sub asmcmdambr_is_cmd { my ($arg) = shift; return defined ($asmcmdambr_cmds{ $arg }); } ######## # NAME # asmcmdambr_is_wildcard_cmd # # DESCRIPTION # This routine determines if an ASMCMDAMBR 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 asmcmdambr_is_wildcard_cmd { my ($arg) = shift; return defined ($asmcmdambr_cmds{ $arg }) && defined ($asmcmdambr_cmds{ $arg }{ wildcard }); } ######## # NAME # asmcmdambr_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 asmcmdambr module currently supports no command that can run # without an ASM instance. ######## sub asmcmdambr_is_no_instance_cmd { my ($arg) = shift; return !defined ($asmcmdambr_cmds{ $arg }) || !defined ($asmcmdambr_cmds{ $arg }{ no_instance }); } ######## # NAME # asmcmdambr_parse_int_args # # DESCRIPTION # This routine parses the arguments for flag options for ASMCMDAMBR # 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 ASMCMDAMBR internal command. ######## sub asmcmdambr_parse_int_args { my ($cmd, $args_ref) = @_; my ($key); my (@string); #build the list of options to parse using GetOptions if($asmcmdambr_cmds{ $cmd }{ flags }) { foreach $key(keys %{$asmcmdambr_cmds{ $cmd }{ flags }}) { push(@string, $key); } } #include deprecated options if any if($asmcmdglobal_deprecated_options{ $cmd }) { foreach my $key(keys %{$asmcmdglobal_deprecated_options{ $cmd }}) { #include only if the option is changed and not discontinued 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 # asmcmdambr_syntax_error($cmd); return undef; } return 0; } ######## # NAME # asmcmdambr_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 ASMCMDAMBR 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 asmcmdambr_syntax_error { my ($cmd) = shift; my ($cmd_syntax); # Correct syntax for $cmd. # my ($succ) = 0; $cmd_syntax = asmcmdambr_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 # asmcmdambr_error_msg # # DESCRIPTION # This function is a wrapper around asmcmdambr_display_msg(), the # function responsible for displaying error messages for the asmcmdambr # 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 asmcmdambr module; 0 # otherwise. # # NOTES # Only asmcmdshare_error_message should call this function. *Do not* # call this function directly; call asmcmdshare_error_message, # instead. ######## sub asmcmdambr_error_msg { my ($err_num, $args_ref) = @_; my ($succ) = 0; my (@eargs); # Assert that $err_num is not within 8000-9400, inclusive. @eargs = ("asmcmdambr_error_msg_05", $err_num); asmcmdshare_assert( (($err_num > 8000) || ($err_num < 9400)) , \@eargs); $succ = asmcmdambr_display_msg($err_num, $args_ref); return $succ; } ######## # NAME # asmcmdambr_display_msg # # DESCRIPTION # This routine prints error and exception messages to STDERR for the # asmcmdambr 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 asmcmdambr. # # If an error is found, this function prints the error message. ######### sub asmcmdambr_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. # 9345-9370 my (%error_messages) = ( 9345 => q!could not open intermediate file '$arg1'! . "\n" . q!$arg2!, 9346 => q!could not close intermediate file '$arg1'! . "\n" . q!$arg2!, 9347 => q!invalid intermediate file '$arg'!, 9348 => q!the '-o' option can be specified only when '-t newdg' is set!, 9349 => q!disk group '$arg' not discovered by ASM instance; skipping...!, 9350 => q!disk group '$arg' not mounted by ASM instance; skipping...!, 9351 => q!ASM instance has no disk group mounted!, 9352 => q!CREATE DISKGROUP failed! . "\n". q!$arg!, 9353 => q!ADD or ALTER TEMPLATE failed! . "\n" . q!$arg!, 9354 => q!ADD ALIAS failed! . "\n" . q!$arg!, 9355 => q!could not find information for diskgroup '$arg' in backup file!, 9356 => q!backup file '$arg' is either empty or cannot be interpreted!, 9357 => q!a file with name '$arg' already exists!, 9358 => q!option '-t newdg' specified without any override options!, 9359 => q!invalid diskgroup name '$arg' specified in override options!, 9360 => q!ADD or ALTER ATTRIBUTE failed! . "\n" . q!$arg!, 9361 => q!Backup version not supported! . "\n" . q!$arg!, ); $errmsg = $error_messages{$err_num}; # Print error only if this module supports this error number. if (defined ($errmsg)) { # Substitute the string '$arg' with the value of $args_ref. if (defined ($args_ref)) { # The interpretation of $args_ref depends on the error message number. if (($err_num == 9345) || ($err_num == 9346)) { $errmsg =~ s,\$arg1,$args_ref->[0],; $errmsg =~ s,\$arg2,$args_ref->[1],; } else { # Standard interpretation of $args_ref assumes one error. $errmsg =~ s,\$arg,$args_ref,; } } print STDERR "ASMCMD-0" . $err_num . ": " . $errmsg . "\n"; $succ = 1; } return $succ; } ######## # NAME # asmcmdambr_signal_exception # # DESCRIPTION # This function is a wrapper around asmcmdambr_display_msg(), the # function responsible for displaying error messages for the asmcmdambr # 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 asmcmdambr 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 asmcmdambr_signal_exception { my ($exception_num, $args_ref) = @_; my ($succ) = 0; # Assert that $exception_num is within 8200-8299, inclusive. if (($exception_num < 8200) || ($exception_num > 8299)) { die "asmcmd: 8202 internal error: [asmcmdambr_signal_exception_05] " . "[$exception_num]\n"; } $succ = asmcmdambr_display_msg($exception_num, $args_ref); return $succ; } ######## # NAME # asmcmdambr_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 asmcmdambr_get_cmd_desc { my ($cmd) = shift; my (%cmd_desc); # Hash storing the description for each internal command. # $cmd_desc{'md_backup'} = ' Perform ASM metadata backup for disk groups. Back up into backup file disk group metadata information including fail groups, disks, attributes, aliases, and templates. Store information into . -G Disk groups to backup. All diskgroups are backed up by default.'; $cmd_desc{'md_restore'} = ' Perform ASM Metadata restore for disk groups. Read metadata information from . --silent Ignore errors. Normally if md_restore encounters an error, it will stop. Specifying this flag ignores that. --full create disk group and restore metadata. --nodg restore metadata only. --newdg create disk group with a different name and restore metadata; -o is required. -S Write SQL commands to instead of executing them. -G Select the disk groups to be restored. If no disk groups defined, all of them will be restored. -o Rename disk group to . '; return $cmd_desc{$cmd}; } ######## # NAME # asmcmdambr_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 asmcmdambr_get_cmd_syntax { my ($cmd) = shift; my (%cmd_syntax); # Hash storing the syntax for each internal command. # $cmd_syntax{md_backup} = q$md_backup [-G ',,...']$; $cmd_syntax{md_restore} = q$md_restore [--silent] [--full|--nodg|--newdg] [-S ] [-G ',,...'] [-o ':,...']$; return $cmd_syntax{$cmd}; } ######## # NAME # asmcmdambr_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 asmcmdambr_get_asmcmd_cmds { return asmcmdshare_print_cmds(sort(keys %asmcmdambr_cmds)); } 1;