# Copyright (c) 2004, 2017, 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) # apfwkr 08/28/17 - Backport prabbala_mdrestore_ro_26537307 from main # prabbala 08/11/17 - 26537307: md_backup: ignore read-only attributes # apfwkr 08/02/17 - Backport anovelo_bug-25425760 from main # anovelo 01/30/17 - 25425760: Don't set default cell.sparse_dg # diguzman 05/30/16 - 19654070 Little change at _no_instance_cmd routine # mnollen 08/13/15 - #21569440 - Add backup and restore ACFS metadata # aramacha 04/17/15 - #20772945: decrement alias array lens on delete # saklaksh 04/06/15 - #20835241 - fix md_backup coalesce bug # ykatada 10/03/14 - #19617921: use bind_param for strings in WHERE # aramacha 08/07/14 - #19017309 - fix ORA-936 & cursor leak - md_backup # pvenkatr 11/03/13 - #17604066 - Added QUORUM support for md_backup & # md_restore commands. # manuegar 05/08/13 - Bug13951456 Support bind parameters # pvenkatr 02/01/13 - bug 16240652, 16240319 - excluded # PHYS_META_REPLICATED attribute while restore. # pvenkatr 08/24/11 - Removed flags hash table - using from XML # adileepk 06/20/11 - Connection Pooling. # adileepk 10/25/10 - Changes made to integrate the parser module with # asmcmd. # moreddy 05/06/10 - bug 8667038 NLS for error messages # pvenkatr 03/31/10 - Syntax, description, example - all from xml # moreddy 03/22/10 - Adding more tracing # canuprem 02/11/10 - bug 9327020 # moreddy 01/18/10 - Adding trace messages # pvenkatr 09/03/09 - Help message from xml file # 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 DBI qw(:sql_types); use Getopt::Long qw(:config no_ignore_case bundling); use Data::Dumper; use IO::Handle; use Config; use File::stat; use File::Path; use File::Copy; use File::Spec::Functions qw(catfile splitdir); use File::Basename; use List::Util qw[min max]; use asmcmdglobal; use asmcmdshare; use asmcmdparser; use asmcmdexceptions; use asmcmdvol; ####################### ASMCMDAMBR Global Constants ###################### # # This list is used for is_cmd call back primarily. All other data from XML # our (%asmcmdambr_cmds) = ('md_backup' => {}, 'md_restore' => {} ); ####################### ASMCMDAMBR Global Variables ###################### our (%supported_types) = (); #needed to finish the file with a non-negative assignment our $true_dummy = 1; ###### # Globals used for xml ###### my ($xmlparser) =""; my $xmlparsed = 0; my ($text)= ""; my ($optName)=""; my ($optNode) = "option"; # hash table of data for each command my (%aOptTxt) = (); my (%aOptOpt1) = (); my (%aOptOpt2) = (); my (%aOptEnd) = (); 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); %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, $sth1, $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 # my (@diskgroup_drl); # DRL Diskgroup names to be backed up # my ($acfs_secencr_save) = 0 ; # Flag save security # my ($acfs_loaded) = 0; # Flag to identify acfs # my (@arroutput, $output); my ($ostype, $osprefix); # Get the supported files in ASM $stmt = 'select description from x$ksfdftyp'; eval { $sth = asmcmdshare_do_select($dbh, $stmt); }; if(asmcmdexceptions::catch()) { @eargs = (""); asmcmdshare_error_msg(9352, \@eargs); asmcmdexceptions::throw("asmcmdexceptions"); } while (defined ($row = asmcmdshare_fetch($sth))) { $supported_types{uc($row->{'DESCRIPTION'})} = 1; } asmcmdshare_finish($sth); # 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); # -b options is deprecated as this argument is mandatory bug#6960868 ($intermediate_file_name) = @{$args{'md_backup'}}; # 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) { (@eargs) = ($intermediate_file_name); asmcmdshare_error_msg(9357, \@eargs); 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 @diskgroup_names = (); foreach (@{$args{'G'}}) { $_ = uc($_); push(@diskgroup_names, $_); asmcmdshare_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'})); } asmcmdshare_finish($sth); 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) { asmcmdshare_print "Disk group metadata to be backed up: $_\n"; } } asmcmdshare_trace(5, "asmcmdambr_process_backup(): ". "Diskgroups: @diskgroup_names ", 'y', 'n'); # Check if ACFS is loaded for backup $output = catfile ("$ENV{ORACLE_HOME}","bin"); $output = catfile ($output,"acfsdriverstate"); $output = asmcmdambr_acfs_execCmd("$output loaded"); @arroutput = @$output; if (grep(/ACFS-9203.*/, @arroutput)) # ACFS-9203: true { $acfs_loaded = 1; ($ostype, $osprefix) = asmcmdambr_acfs_getOSType(); # Enable security backup if (defined($args{'acfs_sec_encr'}) && ($ostype ne 'AIX')) { $acfs_secencr_save = $intermediate_file_name; } } else { asmcmdshare_trace(4, "asmcmdambr_process_backup(): Skipping ". "backup of ACFS files, as ACFS: is not loaded ", 'y', '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 (%advm_acfs_info); # USM info record # my (@drlvolname) = (); # DRL list # 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 = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$dgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); $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. (@eargs) = ($dgname); asmcmdshare_error_msg(9349, \@eargs); return; } asmcmdshare_finish($sth); if (uc($row->{'STATE'}) eq 'DISMOUNTED') { # The diskgroup is dismounted, print error message and fail command; # syntax error. (@eargs) = ($dgname); asmcmdshare_error_msg(9350, \@eargs); 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'}, ); asmcmdshare_trace(5, "asmcmdambr_process_backup(): diskgroup_info: ". Dumper(\%diskgroup_info), 'y', 'n'); # 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, d.FAILGROUP_TYPE from v$asm_disk d, v$asm_diskgroup g where g.GROUP_NUMBER = d.GROUP_NUMBER and g.NAME = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$dgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); # 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'}; $disks_info{'QUORUM'} = $row->{'FAILGROUP_TYPE'}; asmcmdshare_trace(5, "asmcmdambr_process_backup(): disks_info: ". Dumper(\%disks_info), 'y', 'n'); # 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 # asmcmdshare_finish($sth); # Fetch attribute information # Just for ASM diskgroup compatibility versions 11.1 or newer # bug 26537307: query read-only attributes only. au_size is exception # since it is needed and used during diskgroup creation time. if ( asmcmdshare_version_cmp($diskgroup_info{'DGCOMPAT'}, "11.1.0.0.0") >= 0 ) { $qry = 'select a.GROUP_KFENV, a.NAME_KFENV as NAME, a.VALUE_KFENV as VALUE, decode(bitand(a.flags_kfenv, 2), 2, \'Y\', \'N\') as F from x$kfenv a, v$asm_diskgroup g where g.GROUP_NUMBER = a.GROUP_KFENV and (a.readonly_kfenv = 0 OR lower(a.name_kfenv) = \'au_size\') and g.name = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$dgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); while (defined($row = asmcmdshare_fetch($sth))) { if($row->{'F'} ne 'Y') { $attribute_info_array{ uc($row->{'NAME'}) } = $row->{'VALUE'}; } } # end while # asmcmdshare_finish($sth); } # 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 = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$dgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); $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 }; asmcmdshare_trace(5, "asmcmdambr_process_backup(): alias_info: ". Dumper(\%alias_info), 'y', 'n'); $curpos++; } # end while # asmcmdshare_finish($sth); $alias_info_array_length = $curpos; asmcmdshare_trace(5, "asmcmdambr_process_backup(): arr_len: ". $alias_info_array_length, 'y', 'n'); # 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); my ($skip_index); $qry = 'select PARENT_INDEX from v$asm_alias where group_number = ?' . ' and REFERENCE_INDEX = ?' . ' and name = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$current_diskgroup_number); $sth->bind_param(2,$current_reference_index); $sth->bind_param(3,$current_alias_name,SQL_VARCHAR); asmcmdshare_do_execute($sth); $row = asmcmdshare_fetch($sth); if (defined ($row)) { $current_parent_index = $row->{'PARENT_INDEX'}; $current_alias_path = $current_alias_name; asmcmdshare_trace(5, "asmcmdambr_process_backup(): paren_idx: ". $current_parent_index ." cur_alias_p: ". $current_alias_path, 'y', 'n'); # Build full path name for the alias directory starting from alias # name and working upwards towards diskgroup name $level = 0; $skip_index = 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 if ($level) { asmcmdshare_finish($sth1); } last; } $qry = 'select NAME, PARENT_INDEX from v$asm_alias where GROUP_NUMBER = ?' . ' and REFERENCE_INDEX = ?'; $sth1 = asmcmdshare_do_prepare($dbh, $qry); $sth1->bind_param(1,$current_diskgroup_number,SQL_INTEGER); $sth1->bind_param(2,$current_parent_index,SQL_INTEGER); asmcmdshare_do_execute($sth1); $row = asmcmdshare_fetch($sth1); if (defined ($row)) { $current_alias_path = $row->{'NAME'} . '/' . $current_alias_path; $current_parent_index = $row->{'PARENT_INDEX'}; asmcmdshare_trace(5, "asmcmdambr_process_backup(): paren_idx: ". $current_parent_index ." cur_alias_p: ". $current_alias_path, 'y', 'n'); $level++; asmcmdshare_finish($sth1); } else { # No matching rows, so we can't traverse the parent index any # further, skip the current index undergoing metadata # changes. $skip_index = 1; asmcmdshare_trace(5, "asmcmdambr_process_backup(): skip index ". $current_parent_index ." in directory path ". $current_alias_path, 'y', 'n'); asmcmdshare_finish($sth1); last; } } # end for # if (! $skip_index) { asmcmdshare_print "Current alias directory path: ". "$current_alias_path\n"; $alias_info_array{$curpos}{'ALIASNAME'} = $current_alias_path; $alias_info_array{$curpos}{'LEVEL'} = $level; } } asmcmdshare_finish($sth); } #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 = 0; $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}); $alias_info_array_length = $alias_info_array_length - 1; } # 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 = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$dgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); $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 # asmcmdshare_finish($sth); ############################################################### # ACFS backup ############################################################### if ($acfs_loaded) { my ($drldg, $advm, $acfs); ($advm, $acfs, $drldg) = asmcmdambr_acfs_backup($dbh, $dgname, $ostype, $osprefix, $acfs_secencr_save); $advm_acfs_info{'ADVM'} = $advm if ($advm); $advm_acfs_info{'ACFS'} = $acfs if ($acfs); push (@diskgroup_drl, $drldg) if ($drldg && $advm); } # Add DRL DG for backup # The ADVM DRL is a special case. In 12.2, this can live on a # separate DG. To restore ADVM, we need to back up both DG if (($acfs_loaded) && ($dgname eq $diskgroup_names[-1]) && (@diskgroup_drl > 0)) { my %lastdg = map {$_ => 1} @diskgroup_names; my %hash = map {$_,1} @diskgroup_drl; @diskgroup_drl = keys %hash; foreach (@diskgroup_drl) { if (!exists($lastdg{$_})) { push (@diskgroup_names, $_); asmcmdshare_print "Disk group metadata to be backed up: ". "$_\n"; } } @diskgroup_drl = (); } # end if drl # # Create final record for this diskgroup # If the instance version is greater than 11.1, check the attributes. $diskgroup_record{'DGINFO'} = {%diskgroup_info}; $diskgroup_record{'DISKSINFO'} = {%disks_info_array}; $diskgroup_record{'ATTRINFO'} = {%attribute_info_array} if (%attribute_info_array); $diskgroup_record{'ALIASINFO'} = {%alias_info_array}; $diskgroup_record{'TEMPLATEINFO'} = {%template_info_array}; $diskgroup_record{'USMINFO'} = {%advm_acfs_info} if (%advm_acfs_info); # 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 ($sth, $sth1, $qry, $row); # 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 name for restore SQL file # my ($PERLFILE); # File name for restore acfs perl file # my ($SNAPFILE); # Snap file handle # 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); my (@arroutput, $output); # Capture cmd exec output # my ($acfs_loaded) = 1; # Flag specifies if acfs drivers are loaded # my ($acfs_sec_encr) = 0; # Flag to restore security and encryption # my ($acfs_audit) = 0; # Flag specifies to restore acfs audit # my ($secuser, $secgrp); # Store given security user and group acfs # my ($aumgruser, $aumgrgrp, $augrpditor); # Store given audit opts acfs # my (@advmA); # Save volume meta data to restore # my (@acfsA); # Save file system for restore # my (@dgs); # Save diskgroup new:old name for acfs restore # # 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); # -b options is deprecated as this argument is mandatory bug#6960868 # Read Intermediate file into memory. ($intermediate_file_name) = @{$args{'md_restore'}}; 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. @eargs = ($intermediate_file_name); asmcmdshare_error_msg(9347, \@eargs); 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. @eargs = ($intermediate_file_name); asmcmdshare_error_msg(9347, \@eargs); return; } if (!@diskgroup_set) { # Backup file specified by user is empty or cannot be interpreted. @eargs = ($intermediate_file_name); asmcmdshare_error_msg(9356, \@eargs); 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. @diskgroup_names = (); foreach (@{$args{'G'}}) { $_ = uc($_); push(@diskgroup_names, $_); } $dgnamesstr = join(',', @diskgroup_names); # 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. @eargs = ($diskgroup_names[$iter1]); asmcmdshare_error_msg(9355, \@eargs); 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{'o'})) { # Currently only a replacement diskgroup name can be specified # using override option, only valid syntax is # -o ':,...', check input for syntax @override_options = @{$args{'o'}}; # 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 @eargs = ($olddg); asmcmdshare_error_msg(9359, \@eargs); return; } $diskgroups_replace{$olddg} = $newdg; } } $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) { @eargs = ($restore_sql_file_name); asmcmdshare_error_msg(9357, \@eargs); return; } $PERLFILE = (split(/\.sql$/, $restore_sql_file_name))[0] . ".pl"; if ( -e $PERLFILE) { @eargs = ($PERLFILE); asmcmdshare_error_msg(9357, \@eargs); 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; } if (!open (PERLFILE, "> $PERLFILE")) { @eargs = ($PERLFILE, $!); asmcmdshare_error_msg(9345, \@eargs); return; } $SQLFILE = $restore_sql_file_name; SQLFILE->untaint(); PERLFILE->untaint(); } else { $PERLFILE = $intermediate_file_name . ".pl"; if (!open (PERLFILE, "> $PERLFILE")) { @eargs = ($PERLFILE, $!); asmcmdshare_error_msg(9345, \@eargs); return; } PERLFILE->untaint(); } if (defined($args{'acfs_sec_encr'})) { # User has asked us to proceed with security and/or encryption restore $acfs_sec_encr = 1; ($secuser, $secgrp) = split(":", $args{'acfs_sec_encr'}); # Check if given user and group exist; if (!$create_restore_sql) { if (!getpwnam($secuser)) { # invalid user name '%s' specified for %s @eargs = ($secuser,"security"); asmcmdshare_error_msg(9366, \@eargs); return; } if (!getgrnam($secgrp)) { # invalid group name '%s' specified for %s @eargs = ($secgrp,"security"); asmcmdshare_error_msg(9367, \@eargs); return; } } # User has asked us to proceed with audit restore if (defined($args{'acfs_audit'})) { $acfs_audit = 1; ($aumgruser, $aumgrgrp, $augrpditor) = split(":", $args{'acfs_audit'}); # Check if given user and groups exist if (!$create_restore_sql) { if (!getpwnam($aumgruser)) { # invalid user name '%s' specified for %s @eargs = ($aumgruser,"audit"); asmcmdshare_error_msg(9366, \@eargs); return; } if (!getgrnam($aumgrgrp)) { # invalid group name '%s' specified for %s @eargs = ($aumgrgrp,"audit"); asmcmdshare_error_msg(9367, \@eargs); return; } if (!getgrnam($augrpditor)) { # invalid group name '%s' specified for %s @eargs = ($augrpditor,"audit"); asmcmdshare_error_msg(9367, \@eargs); return; } } } } # 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 $diskgroup_compatibility= $diskgroup_set[$i]{DGINFO}{DGCOMPAT}; my $disks_info_array = $diskgroup_set[$i]{DISKSINFO}; #reference my $alias_info_array = $diskgroup_set[$i]{ALIASINFO}; #reference my $template_info_array = $diskgroup_set[$i]{TEMPLATEINFO}; #reference my $advm_acfs_info = $diskgroup_set[$i]{USMINFO}; #reference my $attribute_info_array; my $template; my $alias; my $found; my $iter1; my $attribute_name; # 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) { @eargs = ($diskgroup_compatibility); asmcmdshare_error_msg(9361, \@eargs); 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; } } asmcmdshare_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}); asmcmdshare_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') { # if QUORUM is specified add it, REGULAR is default. if ($current_disk_info->{'QUORUM'} eq "QUORUM") { $stmt .= ' QUORUM '; } $stmt .= 'failgroup ' . $current_disk_info->{'FAILGROUP'} . ' disk '; } else { if($current_disk_info->{'QUORUM'} eq "QUORUM") { $stmt .= ' QUORUM '; } $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 if(defined($attribute_info_array->{'COMPATIBLE.ASM'})) { $compatible_asm = $attribute_info_array->{'COMPATIBLE.ASM'}; $stmt .= 'attribute \'compatible.asm\' = \'' . $compatible_asm. '\'' ; delete($attribute_info_array->{'COMPATIBLE.ASM'}); } if(defined($attribute_info_array->{'COMPATIBLE.RDBMS'})) { $compatible_rdbms = $attribute_info_array->{'COMPATIBLE.RDBMS'}; $stmt .= ', \'compatible.rdbms\' = \'' . $compatible_rdbms . '\''; delete($attribute_info_array->{'COMPATIBLE.RDBMS'}); } if(defined($attribute_info_array->{'AU_SIZE'})) { $au_size = $attribute_info_array->{'AU_SIZE'}; $stmt .= ', \'au_size\' = \'' . $au_size . '\''; delete($attribute_info_array->{'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. eval { $ret = asmcmdshare_do_stmt($dbh, $stmt); }; if (asmcmdexceptions::catch()) { # 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. @eargs = ($DBI::errstr); asmcmdshare_error_msg(9352, \@eargs); if ($ignore_errors) { next; } else { # Propogate the exception upward till it is handled by # asmcmdcore_main() asmcmdexceptions::throw("asmcmdexceptions"); } } asmcmdshare_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}; # bug25425760: 'cell.sparse_dg' appears in a diskgroup (even if it is # not Exadata) when the attribute 'compatible.asm' is updated to # 12.2.0.1. However, 'cell.sparse_dg' cannot be set using 'set # attribute'. If the default value for this attribute is used, # skip adding it since it should already have it. if (lc($attribute_name) eq "cell.sparse_dg" && lc($attribute_value) eq "allnonsparse") { next; } $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. eval { $ret = asmcmdshare_do_stmt($dbh, $stmt); }; if (asmcmdexceptions::catch()) { # Create/alter attribute failed, print error. @eargs = ($DBI::errstr); asmcmdshare_error_msg(9360, \@eargs); if ($ignore_errors) { next; } else { # Propogate the exception upwards asmcmdexceptions::throw("asmcmdexceptions"); } asmcmdshare_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. eval { $ret = asmcmdshare_do_stmt($dbh, $stmt); }; if (asmcmdexceptions::catch()) { # Create/alter template failed, print error. @eargs = ($DBI::errstr); asmcmdshare_error_msg(9353, \@eargs); if ($ignore_errors) { next; } else { # Propogate the exception upwards asmcmdexceptionms::throw("asmcmdexceptions"); } } if ($alter_or_add eq 'add') { asmcmdshare_print "User template $current_template_name created!\n"; } else { asmcmdshare_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/\;$//; eval { $ret = asmcmdshare_do_stmt($dbh, $stmt); }; if (asmcmdexceptions::catch()) { # Create alias directory failed, print error. @eargs = ($DBI::errstr); asmcmdshare_error_msg(9354, \@eargs); if ($ignore_errors) { next; } else { # propogate the exception upwards asmcmdexceptions::throw("asmcmdexceptions"); } } asmcmdshare_print "Directory $current_alias_directory_name re-created!\n"; } } # end for alias # if ($advm_acfs_info) { push (@advmA, $advm_acfs_info->{'ADVM'}); push (@acfsA, $advm_acfs_info->{'ACFS'}); push (@dgs, "$new_diskgroup_name:$old_diskgroup_name"); } } # end for # # Check if ACFS drivers are loaded $output = catfile ("$ENV{ORACLE_HOME}","bin"); $output = catfile ($output,"acfsdriverstate"); $output = asmcmdambr_acfs_execCmd("$output loaded"); @arroutput = @$output; if (!grep(/ACFS-9203.*/, @arroutput)) # ACFS-9203: true { $acfs_loaded = 0; # ACFS is not loaded asmcmdshare_error_msg(9368) if ( 0 < @advmA); } my $perl_has_contents = 0; # Flag used to know perl file has content if ($acfs_loaded || $create_restore_sql) { my (%fstmt); my (%secencr); my ($advm, $acfs); my (@stmts, @perlstmts); my ($dg); my ($ostype, $osprefix) = asmcmdambr_acfs_getOSType(); # The following loop will search and verify required disk groups are # created before trying to restore. my @drlmissing; foreach $advm (@advmA) { foreach (keys %{$advm}) { my ($drldgname) = $advm->{$_}{DRLDGNAME}; if ($drldgname) { if ($dg = (grep(/:$drldgname$/i, @dgs))[0]) { $drldgname = (split(':', $dg))[0]; # get new dg name } # Query DRLDG to ensure it exists. if (!$create_restore_sql) { $qry = 'select NAME from v$asm_diskgroup where NAME = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$drldgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); if (!defined ($row = asmcmdshare_fetch($sth))) { push (@drlmissing, $drldgname); } asmcmdshare_finish($sth); } elsif (! grep(/^$drldgname:.*/i, @dgs)) { # DRLDG is not listed on command for restore push (@drlmissing, $drldgname); } } } if (($advm eq $advmA[-1]) && (@drlmissing > 0)) { # Create a unique array of diskgroups my %hashdrl = map { $_, 1 } @drlmissing; @drlmissing = keys %hashdrl; asmcmdshare_print " * The following DRL Disk group(s)" . " are not restored:\n"; foreach (@drlmissing) { asmcmdshare_print " $_\n"; } if (!$ignore_errors) { # disk group containing auxiliary volume not found asmcmdshare_error_msg(9363); goto DO_EXCEPTION; } else { asmcmdshare_print "Creating DRL volumes as non-DRL volume" . "\n"; } } } # ADVM recovery foreach $advm (@advmA) { foreach (keys %{$advm}) { my ($volname) = $advm->{$_}{'NAME'}; my ($voldg) = $advm->{$_}{'DGNAME'}; my ($volsize) = $advm->{$_}{'SIZE'}; my ($redundancy) = $advm->{$_}{'REDUNDANCY'}; my ($stripe_width) = $advm->{$_}{'WIDTH'}; my ($stripe_columns) = $advm->{$_}{'COLUMNS'}; my ($primary_zone) = $advm->{$_}{'PRIMREG'}; my ($mirror_zone) = $advm->{$_}{'MIRRREG'}; my ($volstate) = $advm->{$_}{'STATE'}; my ($drldgname) = $advm->{$_}{'DRLDGNAME'}; @stmts = (); $dg = (grep(/:$voldg$/i, @dgs))[0]; if ($dg) { $voldg = (split(':', $dg))[0]; } else { asmcmdshare_print "Failed to find Diskgroup: $voldg " . "for volume $volname\n"; goto DO_EXCEPTION if (!$ignore_errors); next; } # Set values for sql statement $volsize = $volsize . "M"; $mirror_zone = "MIRROR" . $mirror_zone; # Error code 15462 prevents stripe width greater than 1024 $stripe_width = 'default' if ($stripe_width > 1024); $stripe_width = $stripe_width . "k" if ($stripe_width ne "default"); # Error code 15336 prevents stripe width if stripe columns = 1 $stripe_columns = 'default' if ($stripe_width ne "default" && $stripe_columns == 1); # Construct SQL statement to re-create volume. $stmt = asmcmdvol_volcreate_sql( $dbh, $voldg, $volname, $volsize, $redundancy, $stripe_columns, $stripe_width, $primary_zone, $mirror_zone, 1 ); # Construct statement for DRL if ($drldgname) { if ($dg = (grep(/:$drldgname$/i, @dgs))[0]) { $drldgname = (split(':', $dg))[0]; # get new dg name } my $tmpstmt = (split('ATTRIBUTE', $stmt))[0] . " RECOVERY LOG IN GROUP \'" . $drldgname . "\' ATTRIBUTE" . (split('ATTRIBUTE', $stmt))[-1]; # Query DRLDG to ensure it exists. if (!$create_restore_sql) { $qry = 'select NAME from v$asm_diskgroup where NAME = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$drldgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); if (defined ($row = asmcmdshare_fetch($sth))) { asmcmdshare_print "Restoring ADVM $volname on DG ". "$voldg DRL $drldgname\n"; $stmt = $tmpstmt; } else { asmcmdshare_print "Restoring ADVM $volname on DG ". "$voldg no DRL\n"; } asmcmdshare_finish($sth); } elsif (grep(/^$drldgname:.*/i, @dgs)) { asmcmdshare_print "Restoring ADVM $volname on DG ". "$voldg DRL $drldgname\n"; $stmt = $tmpstmt; push (@stmts, "prompt Creating volume: $volname " . "in Diskgroup $voldg with DRL $drldgname"); } else { # DRLDG does not exist, restore as no DRL vol. asmcmdshare_print "Restoring ADVM $volname on DG ". "$voldg no DRL\n"; } } else { asmcmdshare_print "Restoring ADVM $volname on DG $voldg\n"; push (@stmts, "prompt Creating volume: $volname in" . " Diskgroup $voldg")if ($create_restore_sql); } # Add Volume creation statement to the poll push (@stmts, $stmt); if ($volstate eq 'DISABLED') { push (@stmts, "alter diskgroup $voldg disable " . "volume $volname"); } foreach $stmt (@stmts) { $stmt = $stmt . ';'; if ($create_restore_sql) { print SQLFILE "$stmt" . "\n"; } else { $stmt =~ s/\;$//; eval { $ret = asmcmdshare_do_stmt($dbh, $stmt); }; if (asmcmdexceptions::catch()) { # Create alias directory failed, print error. asmcmdshare_print ":: ERROR\n"; # ADD or ALTER VOLUME failed\n%s @eargs = ($DBI::errstr); asmcmdshare_error_msg(9362, \@eargs); goto DO_EXCEPTION if (!$ignore_errors); next; } } } asmcmdshare_print "$volname created!\n"; } # end for advm # if ($create_restore_sql && ($advm eq $advmA[-1])) { my $spoolfile = (split(/\.pl$/, $PERLFILE))[0] . "fspool.txt"; $fstmt{'option'} = 'sqlfile_end'; $fstmt{'spool'} = $spoolfile; print SQLFILE asmcmdambr_acfs_getStmt({%fstmt}) . "\n"; } } # ACFS recovery foreach $acfs (@acfsA) { # Restore Filesystem @perlstmts = (); $fstmt{'osprefix'} = $osprefix; $fstmt{'ostype'} = $ostype; if ($create_restore_sql && (!$secencr{'perlinit'})) { $fstmt{'option'} = 'perlinit'; $fstmt{'sqlfile'} = $SQLFILE; push (@perlstmts,asmcmdambr_acfs_getStmt({%fstmt})); $secencr{'perlinit'} = "YES"; } foreach (keys %{$acfs}) { my ($mntpthperm) = $acfs->{$_}{'MNTPTHPERM'}; my ($arzmax) = $acfs->{$_}{'RESIZEMAX'}; my ($arzinc) = $acfs->{$_}{'RESIZEINCREMENT'}; my ($fsplugin) = $acfs->{$_}{PLUGIN}; my ($snapshots) = $acfs->{$_}{SNAPSHOT}; my ($accelinfo) = $acfs->{$_}{AUXVOLUME}; my ($fssec) = $acfs->{$_}{SECURITY} || 0; my ($fsencr) = $acfs->{$_}{ENCRYPTION} || 0; my ($fsaudit) = $acfs->{$_}{AUDIT}; my ($cmd); $fstmt{'mountpath'} = $acfs->{$_}{'MOUNTPATH'}; $fstmt{'volname'} = $acfs->{$_}{'VOLUMENAME'}; $fstmt{'dg'} = $acfs->{$_}{'DGNAME'}; $fstmt{'compat'} = $acfs->{$_}{'ACFSCOMPATIBILITY'}; $fstmt{'size'} = $acfs->{$_}{'TOTALSIZE'}; $fstmt{'blksz'} = $acfs->{$_}{'METADATABLOCKSIZE'}; $fstmt{'compression'}= $acfs->{$_}{'COMPRESSION'}; $fstmt{'accel'} = ""; $fstmt{'dirperm'} = (split(' ', $mntpthperm))[0]; $fstmt{'dirgid'} = (split(':', $mntpthperm))[-1]; $fstmt{'diruid'} = (split(':',((split(' ', $mntpthperm))[-1])))[0]; # Get current DG name if ($dg = (grep(/:$fstmt{'dg'}$/i, @dgs))[0]) { $fstmt{'dg'} = (split(':', $dg))[0]; } else { @eargs = ($fstmt{'mountpath'}, $fstmt{'dg'}, $fstmt{'volname'}); asmcmdshare_error_msg(9355, \@eargs); goto DO_EXCEPTION if (!$ignore_errors); next; } if ($accelinfo) { ($fstmt{'acceldg'}, $fstmt{'accelname'}) = split(':', $accelinfo); if ($dg = (grep(/:$fstmt{'acceldg'}$/i, @dgs))[0]) { # Get new DG name $fstmt{'acceldg'} = (split(':', $dg))[0]; } } $stmt = "Restoring ACFS $fstmt{'mountpath'} on VOL ". "$fstmt{'volname'}"; $stmt .= " ACCEL $fstmt{'accelname'} "if ($accelinfo); $stmt .= "\n"; asmcmdshare_print "$stmt"; # 1. Check all FS values # Metadata block size only 4096 value is accepted since new # system may have a bigger sector size than current machine. if ( $fstmt{'blksz'} == 4096 ) { $fstmt{'blksz'} = "${osprefix}i " . $fstmt{'blksz'}; } else { $fstmt{'blksz'} = ""; } # Initialize perl when no -S option is provided if ((!$secencr{'perlinit'}) && (! $create_restore_sql)) { $fstmt{'option'} = 'perl'; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); $secencr{'perlinit'} = "YES"; } # Initialize Security, Encryption and Audit if ($fsencr ne '0' && $acfs_sec_encr && !$secencr{'ENCRYPTION'}) { # Encryption will be initialized with SSO wallet only, # if not initialized previously. This is done once. $stmt = "acfsutil encr info ${osprefix}t"; $fstmt{'option'} ='encrinit'; $fstmt{'arg'} = $stmt; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); $secencr{'ENCRYPTION'} = 'ON'; } if ($fssec ne "0" && $acfs_sec_encr && !$secencr{'SECURITY'}) { # Security will be initialized with given user and group, # if not initialized previously. This is done once $stmt = "acfsutil sec info ${osprefix}i"; $fstmt{'user'} = $secuser; $fstmt{'group'} = $secgrp; $fstmt{'arg'} = $stmt; $fstmt{'option'} = 'secinit'; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); $secencr{'SECURITY'} = 'ON'; } if ($fsaudit && $acfs_audit && !$secencr{'AUDIT'} && ($secencr{'ENCRYPTION'} || $secencr{'SECURITY'})) { # Audit will be initialized with given manager group and # audit group if not initialized previously. This is done once $stmt = "acfsutil audit info ${osprefix}i"; $fstmt{'user'} = $aumgruser; $fstmt{'group'} = $aumgrgrp; $fstmt{'auopts'} = $augrpditor; $fstmt{'option'} = 'auditinit'; $fstmt{'arg'} = $stmt; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); $secencr{'AUDIT'} = 'ON'; } # 2. Make file system if (!$create_restore_sql) { # Retrieve volume device information $qry = 'select device_kfvol from x$kfvol ' . 'where name_kfvol = ? ' . 'and dgname_kfvol = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$fstmt{'volname'}); $sth->bind_param(2,$fstmt{'dg'},SQL_VARCHAR); asmcmdshare_do_execute($sth); if (defined ($row = asmcmdshare_fetch($sth))) { $fstmt{'volume'} = $row->{'DEVICE_KFVOL'}; } else { asmcmdshare_print ":: ERROR \n"; # volume device for VOLUME: '%s' in DG: '%s' does not exist @eargs = ($fstmt{'volname'}, $fstmt{'dg'}); asmcmdshare_error_msg(9364, \@eargs); goto DO_EXCEPTION if (!$ignore_errors); next; } asmcmdshare_finish($sth); # Retrieve accelerator volume device information if ($accelinfo) { $qry = 'select device_kfvol from x$kfvol ' . 'where name_kfvol = ?' . ' and dgname_kfvol = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$fstmt{'accelname'}); $sth->bind_param(2,$fstmt{'acceldg'},SQL_VARCHAR); asmcmdshare_do_execute($sth); if (defined ($row = asmcmdshare_fetch($sth))) { $fstmt{'accel'} = $row->{'DEVICE_KFVOL'}; $fstmt{'accel'} = "${osprefix}a " . $fstmt{'accel'}; } else { asmcmdshare_print ":: ERROR \n"; # volume device for VOLUME: '%s' in DG: '%s' does not exist @eargs = ("$fstmt{'accelname'}","$fstmt{'acceldg'}"); asmcmdshare_error_msg(9364, \@eargs); goto DO_EXCEPTION if (!$ignore_errors); next; } asmcmdshare_finish($sth); } # Create ACFS $fstmt{'option'} = 'mkfs'; $stmt = asmcmdambr_acfs_getStmt({%fstmt}); $output = asmcmdambr_acfs_execCmd($stmt); @arroutput = @$output; if (grep (/ACFS-0.*/, @arroutput)) { asmcmdshare_print ":: ERROR \n"; # failed to make file system\n%s @eargs = ($stmt); asmcmdshare_error_msg(9365,\@eargs); goto DO_EXCEPTION if (!$ignore_errors); next; } } else { $fstmt{'volume'} = "\$voldev"; if ($accelinfo) { if (! grep(/^$fstmt{'acceldg'}:/i, @dgs)) { # Fail when Accel DG is not listed for restore asmcmdshare_print ":: ERROR \n"; # volume device for VOLUME: '%s' in DG: '%s' does not exist @eargs = ("$fstmt{'accelname'}","$fstmt{'acceldg'}"); asmcmdshare_error_msg(9364, \@eargs); goto DO_EXCEPTION if (!$ignore_errors); next; } $fstmt{'accel'} = "\$acceldev"; $fstmt{'option'} = 'getacceldev'; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); } $fstmt{'option'} = 'getdev'; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); $fstmt{'option'} = 'mkfs'; $stmt = asmcmdambr_acfs_getStmt({%fstmt}); $stmt =~ s/^ \s+|\s+$//g; # Remove trailing spaces push (@perlstmts, " \$status = runCmd(\" $stmt \");\n" . "Tlog (\"to make file system\",\$status,\"" . "$fstmt{'mountpath'}\");\n if (!\$status){\n"); } # Create MountPath $fstmt{'option'} = 'mountpoint'; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); # Mount ACFS $fstmt{'option'} = 'mount'; push (@perlstmts,asmcmdambr_acfs_getStmt({%fstmt})); # Set compression if ($fstmt{'compression'} ne '0') { $fstmt{'option'} = 'compression'; push (@perlstmts,asmcmdambr_acfs_getStmt({%fstmt})); } # Set resize if ($arzmax ne "N/A" || $arzinc ne "N/A") { $stmt = "acfsutil size "; $stmt = $stmt . "${osprefix}a $arzinc "if ($arzinc ne "N/A"); $stmt = $stmt . "${osprefix}x $arzmax "if ($arzmax ne "N/A"); $stmt = $stmt . "$fstmt{'mountpath'}"; $stmt = " \$status = runCmd(\" $stmt\" );\n" . " Tlog (\"setup autoresize\",\$status,\"" . "$fstmt{'mountpath'}\");\n"; push (@perlstmts, $stmt); } # Set plug in if ($fsplugin) { my ($metric, $tag, $interval); ($metric, $tag, $interval) = split(':', $fsplugin); $stmt = "acfsutil plugin enable ${osprefix}m $metric "; $stmt = $stmt . "${osprefix}t $tag " if ($tag); $stmt = $stmt . "${osprefix}i ${interval}s "if ($interval>0); $stmt = $stmt . "$fstmt{'mountpath'}"; push (@perlstmts," \$status = runCmd(\" $stmt\");\n" . " Tlog (\"setup plugin\",\$status,\"" . "$fstmt{'mountpath'}\");\n"); } # Set Encryption if (($fsencr ne '0') && $acfs_sec_encr) { # 1.- encr set # 2.- encr on $fstmt{'option'} = 'encr'; $fstmt{'encr'} = $fsencr; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); } # Set Security if ($fssec ne '0' && $acfs_sec_encr) { # 0.- request password; # 1.- prepare file system # 2.- copy file from backup to $mntpath/.Security/backup # 3.- sec load backup file $fstmt{'option'} = 'sec'; $fstmt{'user'} = $secuser; $fstmt{'group'} = $secgrp; $fstmt{'file'} = $fssec; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); } # Set Audit if ($fsaudit && $acfs_audit) { # 1.- change to audit user # 2.- audit enable sec # 3.- audit enable encr $fstmt{'option'} = 'audit'; $fstmt{'user'} = $aumgruser; $fstmt{'group'} = $aumgrgrp; $fstmt{'auopts'} = $fsaudit; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); } # Create a file with snapshots if ($snapshots) { if (!$SNAPFILE) { $SNAPFILE = (split(/\.pl$/, $PERLFILE))[0] . "snap.txt"; } # open snap file handle my $snapH; if (! open ($snapH, ">>", $SNAPFILE)) { asmcmdshare_print ":: ERROR\n"; @eargs = ($SNAPFILE, $!); asmcmdshare_error_msg(9345, \@eargs); goto DO_EXCEPTION if (!$ignore_errors); } else { push ( my @snapshots, \%{$snapshots}); print $snapH Data::Dumper->Dump([\@snapshots], ["$fstmt{'mountpath'}"]); print $snapH "\n"; close $snapH; } } # ADD closing statements to perl file push (@perlstmts, "\n } #mount\n } #mkdir \n"); if ($create_restore_sql) { push (@perlstmts," } #mkfs\n"); $fstmt{'option'} = 'getdevend'; push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt})); } # Dump calls to file if (@perlstmts > 0) { # Check file handle is still open if (tell(PERLFILE) != -1) { print PERLFILE "@perlstmts"; $perl_has_contents = 1; } else { @eargs = ($PERLFILE, $!); asmcmdshare_error_msg(9345, \@eargs); return; } } @perlstmts = (); asmcmdshare_print "$fstmt{'mountpath'} modified!\n"; } # end for acfs FS lvl # } # end for acfs DG lvl# } # Close restore SQL file if ($create_restore_sql) { close (SQLFILE); } if ($perl_has_contents && (tell(PERLFILE) != -1)) { # Dump closing stmt to perl file when file has contents, !-z and -s # are not used for check since writing may be slower than size check. $stmt = "\nclose (\$fh) if (\$verbose);\nprint \"" . "\\n\\n***************************************\\n Summary" . "\\n***************************************\\n\\n\";\n"; $stmt = $stmt . "foreach my \$num (0..1){\n" . " if (\$dolog{\$num}) {\n" . " print \"\\nError -The following errors were encountered\\n\\n\" " . "if(\$num != 0);\n" . " foreach (keys %{\$dolog{\$num}}){\n" . " print \"For file system: \$_\\n\";\n" . " print \" \$_\\n\" foreach (\@{\$dolog{\$num}{\$_}});\n" . " }\n }\n}\n"; print PERLFILE "$stmt"; } close (PERLFILE); unlink $PERLFILE if (-z $PERLFILE); if ($SNAPFILE) { if (-z $SNAPFILE) { unlink $SNAPFILE ; } else { asmcmdshare_print "Snapshot info can be found in \'$SNAPFILE" . "\'\n"; } } if (-e $PERLFILE) { if (!$create_restore_sql) { asmcmdshare_print "Execute \"\# perl $PERLFILE\" to mount " . "ACFS\n"; } else { asmcmdshare_print "ACFS restore file \'$PERLFILE\' created " . "successfully\n"; } } return; DO_EXCEPTION: # Close Files and do exception if ($create_restore_sql) { close (SQLFILE); } close (PERLFILE); unlink $PERLFILE; unlink $SNAPFILE if ($SNAPFILE); asmcmdexceptions::throw("asmcmdexceptions"); } ######## # 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 ($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. # $desc = asmcmdshare_get_help_desc($command); asmcmdshare_print "$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 }) && (asmcmdshare_get_cmd_wildcard ($arg) eq "true" ) ; } ######## # 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 # 1 if $arg is a command that can run without an ASM instance or it does not # belong to this module # 0 if $arg is a command that needs to connect to an ASM instance # -1 if $arg is a command that may use an ASM instance. # # NOTES # The asmcmdambr module currently supports no command that can run # without an ASM instance. ######## sub asmcmdambr_is_no_instance_cmd { my ($arg) = shift; my ($rc); return 1 unless defined($asmcmdambr_cmds{$arg}); $rc = asmcmdshare_get_cmd_noinst($arg); if ($rc eq "true") { return 1; } elsif ($rc eq "undef") { return -1; } return 0; } ######## # 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); #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 asmcmdparser_parse_issued_command() from the asmcmdparser package to parse arguments for # internal commands. These arguments are stored in @ARGV. if (!asmcmdparser_parse_issued_command($cmd, $args_ref, \@string)) { # Print correct command format if syntax error # 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; # display syntax only for commands from this module. if ( asmcmdambr_is_cmd($cmd)) { $cmd_syntax = asmcmdshare_get_help_syntax($cmd); # Get syntax for $cmd. # $cmd_syntax = asmcmdshare_trim_str ($cmd_syntax); # Trim blank spaces # asmcmdshare_printstderr ':usage: ' . $cmd_syntax . "\n"; asmcmdshare_printstderr ':help: help ' . $cmd . "\n"; if ($asmcmdglobal_hash{'mode'} eq 'n') { $asmcmdglobal_hash{'e'} = -1; } } return $succ; } ######## # 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_filter_invisible_cmds (%asmcmdambr_cmds); } ######## # NAME # asmcmdambr_acfs_backup # # DESCRIPTION # This routine constructs 2 hashes one hash has ADVM backup metadata # and the second has ACFS metadata. # # PARAMETERS # dbh (IN) - initialized database handle, non-null. # dgname (IN) - Current DG name to backup. # ostype (IN) - OS name which we are running. # osprefix (IN) - Command prefix for OS. # acfs_secencr_save (IN) - Security backup path. # # RETURNS # %advm (OUT) - Volume metadata that belongs to given diskgroup. # %acfs (OUT) - File system metadata that belongs to given diskgroup # @drldg (OUT) - Returns a DRL diskgroup's name if DRLDG exists. # # NOTES # Used by asmcmdambr_backup command to get volume and # file system meta-data for backup. # ######## sub asmcmdambr_acfs_backup { my ($dbh, $dgname, $ostype, $osprefix, $acfs_secencr_save) = @_; my ($sth, $sth1, $qry, $row); # SQL query statement. # my ($output, @arroutput); # Command execution variables # my @drldg; # List container of DRL DG # # Volume variables # my $advm = ""; my (@volume_name); # Container for list of volumes # my ($volume_name); # Current volume # my (%advm_info); # Hash containing all volume meta-data # # File System variables # my $acfs = ""; my @mountpath; # Container for list of mountpaths # my $mountpath; # Current mountpath # my (%acfs_info); # Hash containing all file system data # ################################################################ # Backup volume ################################################################ # 1. Filter volumes per diskgroup $qry = 'select NAME_KFVOL from x$kfvol where ' . 'DGNAME_KFVOL = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1, $dgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); while (defined ($row = asmcmdshare_fetch($sth))) { if ($row->{'NAME_KFVOL'}){ push (@volume_name, uc($row->{'NAME_KFVOL'})); } } asmcmdshare_finish($sth); foreach $volume_name (@volume_name) { my (%vol_info); # Volume info record # my ($vol_info); # Volume info record # # 2. Query each volume metadata $qry = 'select a.VOLUME_NAME, a.SIZE_MB, a.STRIPE_COLUMNS, ' . 'a.STATE, a.REDUNDANCY, a.STRIPE_WIDTH_K, ' . 'a.PRIMARY_REGION, a.MIRROR_REGION, g.DRLDGNAME_KFVOL ' . 'from v$asm_volume a, x$kfvol g ' . 'where a.VOLUME_NAME = ? ' . 'and g.NAME_KFVOL = a.VOLUME_NAME ' . 'and DGNAME_KFVOL = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$volume_name); $sth->bind_param(2,$dgname,SQL_VARCHAR); asmcmdshare_do_execute($sth); if (defined ($row = asmcmdshare_fetch($sth))) { # 3. Add DRL diskgroup if ((defined $row->{'DRLDGNAME_KFVOL'}) && ($row->{'DRLDGNAME_KFVOL'} ne "")) { $vol_info{'DRLDGNAME'} = $row->{'DRLDGNAME_KFVOL'}; push (@drldg, $row->{'DRLDGNAME_KFVOL'}); } # 4. Dump each volume metadata in a hash $vol_info{'DGNAME'} = $dgname; $vol_info{'NAME'} = $row->{'VOLUME_NAME'}; $vol_info{'SIZE'} = $row->{'SIZE_MB'}; $vol_info{'REDUNDANCY'} = $row->{'REDUNDANCY'}; $vol_info{'COLUMNS'} = $row->{'STRIPE_COLUMNS'}; $vol_info{'WIDTH'} = $row->{'STRIPE_WIDTH_K'}; $vol_info{'PRIMREG'} = $row->{'PRIMARY_REGION'}; $vol_info{'MIRRREG'} = $row->{'MIRROR_REGION'}; $vol_info{'STATE'} = $row->{'STATE'}; $advm_info{ $volume_name } = { %vol_info }; asmcmdshare_print "ADVM metadata to be backed up: " . "$volume_name\n"; asmcmdshare_trace(5, "asmcmdambr_process_backup(): vol_info: " . Dumper(\%vol_info), 'y', 'n'); } # end while # asmcmdshare_finish($sth); } # end foreach volume # $advm = {%advm_info} if (%advm_info); ############################################################## # Backup File system ############################################################## # 1. Get all existing mount paths $qry = "acfsutil info fs ${osprefix}o mountpoints"; $output = asmcmdambr_acfs_execCmd ($qry); if ( grep (/ACFS-.*/, @$output)) { # No filesystem mounted return ( $advm, $acfs, (@drldg) ); } foreach (@$output) { $_ =~ s/^ \s+|\s+$//g; push (@mountpath, $_); } foreach $mountpath (@mountpath) { my (%fs_info); # Mountpath info record # my ($fs_info); my (@acfs_util); # 2. Get Basic file system information if ($ostype eq 'AIX') { @acfs_util = ( "VOLUMENAME", "TOTALSIZE", "RESIZEINCREMENT", "RESIZEMAX", "METADATABLOCKSIZE", "COMPRESSION", "AUXVOLUME" ); $qry = "acfsutil info fs $mountpath ${osprefix}o " . "primaryvolume,totalspace," . "autoresizeincrement,autoresizemax,metablocksize," . "iscompression,auxvolume" . " 2>/dev/null"; } else { @acfs_util = ( "VOLUMENAME", "TOTALSIZE", "RESIZEINCREMENT", "RESIZEMAX", "METADATABLOCKSIZE", "COMPRESSION", "SECURITY", "ENCRYPTION", "AUXVOLUME" ); $qry = "acfsutil info fs $mountpath ${osprefix}o " . "primaryvolume,totalspace," . "autoresizeincrement,autoresizemax,metablocksize," . "iscompression,issecurity,isencryption,auxvolume" . " 2>/dev/null"; } $output = asmcmdambr_acfs_execCmd ($qry); my $iter = 0; foreach (@$output) { my ($key) = $acfs_util[$iter] || "" ; $_ =~ s/^ \s+|\s+$//g; $iter++; # 3. Check file system volumes belong to given diskgroup # Back-up is done per diskgroup if (($key eq 'VOLUMENAME')|| ($key eq 'AUXVOLUME' && $_ ne " ")) { $qry = 'select NAME_KFVOL, DGNAME_KFVOL ' . 'from x$kfvol ' . 'where DEVICE_KFVOL = ?'; $sth = asmcmdshare_do_prepare($dbh, $qry); $sth->bind_param(1,$_,SQL_VARCHAR); asmcmdshare_do_execute($sth) if $sth; if (defined ($row = asmcmdshare_fetch($sth))) { if ($key eq 'VOLUMENAME') { last if ($row->{'DGNAME_KFVOL'} ne $dgname); $fs_info{$key} = $row->{'NAME_KFVOL'}; } else { $fs_info{$key} = $row->{'DGNAME_KFVOL'} . ":" . $row->{'NAME_KFVOL'}; } } asmcmdshare_finish($sth); } else { $fs_info{$key} = $_; } } # 4. Skip file system if it does not belong to current dg next if (!$fs_info{'VOLUMENAME'}); # Save diskgroup name $fs_info{'DGNAME'} = $dgname; # Save mountpath $fs_info{'MOUNTPATH'} = $mountpath; # 5. Get acfs specific attribute info # Get ACFS compatibility $qry = "acfsutil compat get $mountpath"; $output = asmcmdambr_acfs_execCmd ($qry); @arroutput = @$output; $output = (split(':', $arroutput[-1]))[-1]; $output =~ s/^ \s+|\s+$//g; # Remove trailing spaces $fs_info{'ACFSCOMPATIBILITY'} = $output if (! grep (/ACFS-1.*/, @arroutput)); # Get current mount path permissions $output = stat($mountpath); $output = sprintf("%04o %s:%s", $output->mode & 07777, $output->uid, $output->gid); $fs_info{'MNTPTHPERM'} = $output; # Get plugin info $qry = "acfsutil plugin info $mountpath 2>&1"; $output = asmcmdambr_acfs_execCmd($qry); @arroutput = @$output; if ( @arroutput == 5 && (! grep (/ACFS-1.*/, @arroutput)) ) { my $metric = (split(':', $arroutput[1]))[-1]; my $tag = (split(':', $arroutput[2]))[-1] // ""; my $interval = (split(':', $arroutput[-1]))[-1] // ""; $metric =~ s/^ \s+|\s+$//g; $tag =~ s/^ \s+|\s+$//g if ($tag); $interval =~ s/^ \s+|\s+$//g if ($interval); $fs_info{'PLUGIN'} = $metric . ":" . $tag . ":" . $interval; } ### # Backup Snapshots # # The output received from acfsutil has the following format # Each snap has 7 lines of attributes, # calculate the given attributes position # it's done this way to avoid multi language problems ### $qry = "acfsutil snap info " . $mountpath; $output = asmcmdambr_acfs_execCmd($qry); @arroutput = @$output; if (@arroutput > 4 && (! grep (/ACFS-1.*/, @arroutput)) ) { my %acfs_snap; my %snap_info; my $lineref=0; my $str; $str = (split(':', $arroutput[-2]))[-1]; $str =~ s/^\s+|\s+$//g; my ($snapnumber); my $snapcounter =1; for ($snapnumber = $str; $snapnumber > 0 ; $snapnumber--) { $str = (split(':', $arroutput[$lineref]))[-1]; # Get value $str =~ s/^\s+|\s+$//g; $snap_info{'NAME'} = $str; # snapone $lineref = $lineref + 2; $str = (split(':', $arroutput[$lineref]))[-1]; # Get value $str =~ s/^\s+|\s+$//g; $snap_info{'ACCESS'} = $str; # RO || RW $lineref++; $str = (split(':', $arroutput[$lineref]))[-1]; # Get value $str =~ s/^\s+|\s+$//g; $snap_info{'PARENT'} = $str; # mountpoint || snap $lineref = $lineref + 5; # Go to next snap first line $acfs_snap{$snapcounter} = { %snap_info }; $snapcounter++; } $fs_info{'SNAPSHOT'} = { %acfs_snap }; } # Get Security if ($acfs_secencr_save && $fs_info{'SECURITY'}) { SECURITY: { if ($fs_info{'SECURITY'} == 1) { # 1. Check for permissions my $secdir = catfile ($mountpath ,'.Security'); $secdir = catfile ($secdir , 'backup'); $secdir = catfile ($secdir,'secbackup.xml'); # User must be sec admin or # part of backup operators realm # in order to see the file. if (! -e $secdir) { asmcmdshare_print "* ACFS Not enough permissions " . "to backup $mountpath security \n"; last SECURITY; } # 2. Set new security file name my $nwsecfile = $dgname . "." . $fs_info{'VOLUMENAME'} . ".secbackup.xml"; my $secbkuppth = dirname($acfs_secencr_save); $secbkuppth = catfile ($secbkuppth,"security"); # 3. Create directory to store security backup files if (! -d $secbkuppth) { mkdir $secbkuppth; if (! -d $secbkuppth) { asmcmdshare_print "* ACFS Failed to create " . "${secbkuppth} directory\n"; last SECURITY; } } # 4. Copy file to new location $secbkuppth = catfile ($secbkuppth, $nwsecfile); copy ($secdir, $secbkuppth); if (!-e $secbkuppth ) { asmcmdshare_print "*ACFS failed to copy $secdir to" . " ${secbkuppth}${$nwsecfile}\n"; last SECURITY; } $fs_info{'SECURITY'} = $nwsecfile; } } } else { $fs_info{'SECURITY'} = 0; } # Encryption has the following output # # File system: /tkfv_mounts/TKFVVOL1 # Encryption status: OFF # Algorithm: AES 128-bits # Key length: 16 bytes if ($acfs_secencr_save && $fs_info{'ENCRYPTION'}) { if ($fs_info{'ENCRYPTION'} == 1) { $qry = "acfsutil encr info ${osprefix}m $mountpath"; $output = asmcmdambr_acfs_execCmd($qry); @arroutput = @$output; if ( ! grep (/ACFS-1.*/, @arroutput) ) { my $str = ""; $str = (split(':', $arroutput[2]))[-1]; # AES 128-bits $str =~ s/^\s+|\s+$//g; # Remove trailing spaces ($fs_info{'ENCRYPTION'}, $str) = split(' ', $str); $str = ":" . (split('-', $str))[0]; # 128-bits $fs_info{'ENCRYPTION'} = $fs_info{'ENCRYPTION'} . $str; } else { asmcmdshare_print "*ACFS ERROR \n @arroutput \n"; } } } elsif ($fs_info{'ENCRYPTION'}) { $fs_info{'ENCRYPTION'}=0; } # Audit command output # # Auditing information for mount point 'mountpath': # Maximum Audit trail size : 10 MB # Archive file : 'NOT PRESENT' # Audit sources: # Security : 'ENABLED' # Encryption : 'DISABLED' if ($acfs_secencr_save && ($fs_info{'ENCRYPTION'} || $fs_info{'SECURITY'})) { # 1. Get audit information $qry = "acfsutil audit info ${osprefix}m $mountpath"; $output = asmcmdambr_acfs_execCmd($qry); @arroutput = @$output; if (! grep (/ACFS-1.*/, @arroutput)) { my %fs_audit; my $str; # 2. Verify if audit is enabled for security $str = (split(':', $arroutput[-2]))[-1]; $str =~ s/^\s+|\s+$//g; if ($str eq '\'ENABLED\'') { # 2.1 Verify security file is in backup location if ($fs_info{'SECURITY'} =~ $dgname) { $fs_audit{'SECURITY'} = 'ENABLED'; } } # 3. Verify if audit is enabled for encryption $str = (split(':', $arroutput[-1]))[-1]; $str =~ s/^\s+|\s+$//g; # Remove trailing spaces if ($str eq '\'ENABLED\'') { if ($fs_info{'ENCRYPTION'} ne "0") { $fs_audit{'ENCRYPTION'} = 'ENABLED'; } } # 4. Dump audit to mount path hash $fs_info{'AUDIT'} = {%fs_audit} if (%fs_audit); } } # 6. Dump file system metadata $acfs_info{ $mountpath } = { %fs_info }; asmcmdshare_print "ACFS metadata to be backed up:$mountpath\n"; asmcmdshare_trace(5, "asmcmdambr_process_backup():acfs_info: " . Dumper(\%acfs_info), 'y', 'n'); } # end foreach # $acfs = {%acfs_info} if (%acfs_info); return ($advm, $acfs, (@drldg)); } ######## # NAME # asmcmdambr_acfs_getStmt # # DESCRIPTION # This routine returns requested statement required # to create perl file for File system restore. # # PARAMETERS # option (IN) - Option to request of xml file. # mountpath (IN) - Mount path of file system. # dg (IN) - DG name that belongs to volume. # acceldg (IN) - DG name that belongs to accelerator volume. # ostype (IN) - OS name. # osprefix (IN) - Command prefix for current OS. # security flag (IN) - Security backup path. # spool (IN) - Spool file name. # sqlfile (IN) - Sql file name. # compat (IN) - Acfs compatibility version. # size (IN) - File system size. # accel (IN) - Accelerator device value. # blksz (IN) - Acfs metadata block size. # volume (IN) - Volume device value # dirperm (IN) - Mount path permissions # diruid (IN) - Mount path uid # dirgid (IN) - Mount path gid # user (IN) - User id for security and/or audit # group (IN) - Group id for security and/or audit # auopts (IN) - Audit backup metadata # encr (IN) - Encryption backup metadata # file (IN) - Security backup file name # arg (IN) - Argument provided in restore # # RETURNS # A string for the given option. # # NOTES # ######## sub asmcmdambr_acfs_getStmt { my ($args) = @_; my $string = ""; # Parse xml file. if (!$xmlparsed) { asmcmdambr_acfs_parse_xml (); } # Get statements if ($args->{'option'} eq 'sqlfile_end') { # Add arguments to String $_ = $aOptTxt{$args->{'option'}}; $_ =~ s/args\{spool}/$args->{'spool'}/g; $string = $_; } elsif ($args->{'option'} eq 'perlinit') { # Add arguments to String $_ = $aOptTxt{'header'}; $_ .= $aOptOpt1{'header'}; $_ .= $aOptOpt2{'header'}; $_ =~ s/args\{spool}/$args->{'spool'}/g; $_ =~ s/args\{sqlfile}/$args->{'sqlfile'}/g; $string = $_; } elsif ($args->{'option'} eq 'perl') { # Add arguments to String $_ = $aOptTxt{'header'}; $_ .= $aOptOpt2{'header'}; $_ =~ s/args\{spool}/$args->{'spool'}/g; $_ =~ s/args\{sqlfile}/$args->{'sqlfile'}/g; $string = $_; } elsif ($args->{'option'} eq 'mkfs') { # Add arguments to String if ($args->{'ostype'} eq "Linux") { $_ = $aOptTxt{$args->{'option'}}; } elsif ($args->{'ostype'} eq "Solaris") { $args->{'volume'} =~ s/asm\//asm\/r/; $_ = $aOptOpt1{$args->{'option'}}; } elsif ($args->{'ostype'} eq "AIX") { $_ = $aOptOpt2{$args->{'option'}}; } elsif ($args->{'ostype'} eq "Windows") { $_ = $aOptEnd{$args->{'option'}}; } $_ =~ s/^ \s+|\s+$//g; $_ =~ s/args\{compat}/$args->{'compat'}/g; $_ =~ s/args\{size}/$args->{'size'}/g; $_ =~ s/args\{accel}/$args->{'accel'}/g; $_ =~ s/args\{blksz}/$args->{'blksz'}/g; $_ =~ s/args\{volume}/$args->{'volume'}/g; $string = $_; } elsif ($args->{'option'} eq 'mount') { # Add arguments to String if ($args->{'ostype'} eq "Linux") { $_ = $aOptTxt{$args->{'option'}}; } elsif ($args->{'ostype'} eq "Solaris") { $_ = $aOptOpt1{$args->{'option'}}; } elsif ($args->{'ostype'} eq "AIX") { $_ = $aOptOpt2{$args->{'option'}}; } elsif ($args->{'ostype'} eq "Windows") { $_ = $aOptEnd{$args->{'option'}}; } $_ =~ s/args\{volume}/$args->{'volume'}/g; $string = $_; } elsif ($args->{'option'} eq 'getdev') { # Add arguments to String $_ = $aOptTxt{$args->{'option'}}; if ($args->{'accel'}) { # Get accelerator if exists $_ .= $aOptOpt1{$args->{'option'}}; } $_ =~ s/args\{dg}/$args->{'dg'}/g; $_ =~ s/args\{volname}/$args->{'volname'}/g; $_ =~ s/args\{acceldg}/$args->{'acceldg'}/g; $_ =~ s/args\{accelname}/$args->{'accelname'}/g; $string = $_; } elsif ($args->{'option'} eq 'getdevend') { # Add arguments to String $_ = ""; if ($args->{'accel'}) { # Get accelerator if exists $_ .= $aOptOpt2{'getdev'}; } $_ .= $aOptEnd{'getdev'}; $_ =~ s/args\{dg}/$args->{'dg'}/g; $_ =~ s/args\{volname}/$args->{'volname'}/g; $_ =~ s/args\{acceldg}/$args->{'acceldg'}/g; $_ =~ s/args\{accelname}/$args->{'accelname'}/g; $string = $_; } elsif ($args->{'option'} eq 'mountpoint') { # Add arguments to String $_ = $aOptTxt{$args->{'option'}}; $_ =~ s/args\{dirperm}/$args->{'dirperm'}/g; $_ =~ s/args\{diruid}/$args->{'diruid'}/g; $_ =~ s/args\{dirgid}/$args->{'dirgid'}/g; $string = $_; } elsif ($args->{'option'} eq 'compression') { # Add arguments to String $_ = $aOptTxt{$args->{'option'}}; $string = $_; } elsif ($args->{'option'} eq 'encrinit') { $_ = $aOptTxt{$args->{'option'}}; $_ =~ s/args\{info}/$args->{'arg'}/g; $string = $_; } elsif ($args->{'option'} eq 'encr') { my ($algorithm, $keyln) = split(':', $args->{'encr'}); $_ = $aOptOpt1{'encrinit'}; $_ =~ s/args\{algorithm}/$algorithm/g; $_ =~ s/args\{keyln}/$keyln/g; $string = $_; } elsif ($args->{'option'} eq 'secinit') { $_ = $aOptTxt{$args->{'option'}}; $_ =~ s/args\{info}/$args->{'arg'}/g; $_ =~ s/args\{secuser}/$args->{'user'}/g; $_ =~ s/args\{secgrp}/$args->{'group'}/g; $string = $_; } elsif ($args->{'option'} eq 'sec') { my $dest = catfile ($args->{'mountpath'},".Security"); $dest = catfile ($dest,"backup"); $_ = $aOptOpt1{'secinit'}; $_ =~ s/args\{secfile}/$args->{'file'}/g; $_ =~ s/args\{user}/$args->{'user'}/g; $_ =~ s/args\{grp}/$args->{'group'}/g; $_ =~ s/args\{dest}/$dest/g; $string = $_; my $fssec = $args->{'file'}; my $secgrp = $args->{'group'}; my $secuser = $args->{'user'}; } elsif ($args->{'option'} eq 'auditinit') { $_ = $aOptTxt{$args->{'option'}}; $_ =~ s/args\{info}/$args->{'arg'}/g; $_ =~ s/args\{mgruser}/$args->{'user'}/g; $_ =~ s/args\{mgrgrp}/$args->{'group'}/g; $_ =~ s/args\{grpditor}/$args->{'auopts'}/g; $string = $_; } elsif ($args->{'option'} eq 'audit') { foreach (keys %{$args->{'auopts'}}) { $_ = "encr" if ($_ eq 'ENCRYPTION'); $_ = "sec" if ($_ eq 'SECURITY'); $string = $string . $aOptOpt1{'auditinit'}; $string =~ s/args\{_}/$_/g; $string =~ s/args\{user}/$args->{'user'}/g; $string =~ s/args\{grp}/$args->{'group'}/g; } } $string =~ s/args\{mountpath}/$args->{'mountpath'}/g; $string =~ s/args\{prefix}/$args->{'osprefix'}/g; return ($string); } ######## # NAME # asmcmdambr_acfs_execCmd # # DESCRIPTION # This routine will run a command with qx and # it will return STDOUT,STDERR and exit status. # # PARAMETERS # cmd (IN) - Command to execute. # # RETURNS # $output: returns command STDOUT and STDERR output. # # NOTES # ######## sub asmcmdambr_acfs_execCmd { my $command = join ' ', @_; my @output = qx{$command 2>&1}; return (\@output); } ######## # NAME # asmcmdambr_acfs_getOSType # # DESCRIPTION # This routine will return which OS we are running on # it will return OSTYPE,OSPREFIX # # PARAMETERS # none # # RETURNS # $ostype: returns the OS name in which we are running. # $osprefix: returns osprefix type to run acfs commands. # # NOTES # This is needed since acfs works with OS specific commands. # ######## sub asmcmdambr_acfs_getOSType { my $osprefix; my $ostype = $Config{'osname'}; $ostype = "Linux" ; # if ($Config{'osname'} =~ /linux/i ); $ostype = "Windows" if ($Config{'osname'} =~ /MSWin/i ); $ostype = "Windows" if ($Config{'osname'} =~ /Windows_NT/i ); $ostype = "Solaris" if ($Config{'osname'} =~ /solaris/i ); $ostype = "Solaris" if ($Config{'osname'} =~ /SunOS/i ); $ostype = "AIX" if ($Config{'osname'} =~ /aix/i ); $osprefix = "-"; # command line option osprefix $osprefix = "/" if ( $ostype eq "Windows"); return ($ostype, $osprefix); } ############## # NAME # asmcmdshare_parse_xml_help # # DESCRIPTION # This function parses the asmcmdambr.xml file # # PARAMETERS # none # # RETURNS # none ############## sub asmcmdambr_acfs_parse_xml { $text = "" ; # clear the text. # The file asmcmdambracfs.xml is parsed only if it exists. my @xmlfiles = ("$ENV{'ORACLE_HOME'}/rdbms" . "/src/client/tools/asmcmd/modules/asmcmdambracfs.xml" ); #set the handlers # xmlStartNodeHandler -> will be called whenever a new node is started # xmlTextNodeHandler -> will be called whenever text node (multi line) # xmlEndNodeHandler -> will be called whenever a node ends. $xmlparser = XML::Parser->new( Handlers => { Start => \&asmcmdambr_xmlStartNodeHandler, Char => \&asmcmdambr_xmlTextNodeHandler, End => \&asmcmdambr_xmlEndNodeHandler } ) ; # parse the xml files foreach (@xmlfiles) { $xmlparser->parsefile ($_) if (-r $_); } $xmlparsed = 1 ; # to avoid parsing xml file repeatedly. } ############## # NAME # asmcmdambr_xmlStartNodeHandler # # DESCRIPTION # This function is a callback - called whenever a start-node occurs # # PARAMETERS # expat (IN) - XML parser # element (IN) - Element to parse from XML # attrs (IN) - Attributes from element in XML file # # RETURNS # none ############## sub asmcmdambr_xmlStartNodeHandler { my ($expat, $element, %attrs)=@_; # only interested in nodes if ($element eq $optNode) { # need to have cmdName attribute if (defined($attrs{'name'})) { $optName = $attrs{'name'} ; $text = "" ; } } } ############## # NAME # asmcmdambr_xmlTextNodeHandler # # DESCRIPTION # This function is a callback - called on text node data . # # PARAMETERS # e (IN) - Expat XML parser # string (IN) - Append multi lines to string # # RETURNS # none ############## sub asmcmdambr_xmlTextNodeHandler { my($e, $string)=@_; # in case of multi-line, this callback is called for each line, # append and collect all lines together. $text .= $string ; } ############## # NAME # asmcmdambr_xmlEndNodeHandler # # DESCRIPTION # This function is a callback - called on endnode. # # PARAMETERS # expat (IN) - XML parser # element (IN) - Element to parse from XML # attrs (IN) - Attributes from element in XML file # # RETURNS # none ############## sub asmcmdambr_xmlEndNodeHandler { my ($expat, $element, %attrs)=@_; # Text node if ( $element eq "text" ) { $aOptTxt{$optName} = $text ; } # Option1 command node. if ($element eq "opt1") { $aOptOpt1{$optName} = $text ; } # Option 2 node if ($element eq "opt2") { $aOptOpt2{$optName} = $text ; } # End node if ($element eq "end") { $aOptEnd{$optName} = $text ; } $text = ""; } 1;