MINI MINI MANI MO
# Copyright (c) 2004, 2018, 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 04/11/18 - Backport mnollen_mdbrfs from main
# mnollen 02/24/18 - 27261443: FS md_backup md_restore fix
# prabbala 10/12/17 - 26896659: md_backup: ignore _vam_active
# yilhu 09/20/17 - 26594742: md_backup: ignore compatible.patch.%
# prabbala 08/11/17 - 26537307: md_backup: ignore read-only attributes
# prabbala 07/25/17 - 25895224: Only skip dir creation with sys alias
# prabbala 07/25/17 - 26236295: do not restore ate_conversion_done attr
# 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);
my @attr_to_ignore = (
# 26236295: _vam_active is set automatically while
# creating a sparse DG or while changing compatible.asm
# to >=18.1. Skip the attribute from backup
'_vam_active',
# bug 26594742: Disallow md_backup from backing up
# compatible.patch.% because it is not supposed to be
# changed.
'compatible.patch.rdbms',
'compatible.patch.asm'
);
# 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
if ( asmcmdshare_version_cmp($diskgroup_info{'DGCOMPAT'},
"11.1.0.0.0") >= 0 )
{
# bug 26537307: Query only those attributes that are not read-only,
# with the exception of au_size which is required and used during the
# creation of diskgroups.
$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)))
{
my $attrnm = lc($row->{'NAME'});
# do not save the attribute that has to be ignored (e.g _vam_active)
if (($row->{'F'} ne 'Y') and !grep(/^\b$attrnm\b$/, @attr_to_ignore))
{
$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, a.SYSTEM_CREATED
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{'SYSTEM_CREATED'} = $row->{'SYSTEM_CREATED'};
$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);
# bug 25895224: User created dir can be with same name as supported
# file types (e.g backupset). Only discard system created dir.
if ($#path_arr == 1)
{
if (defined($supported_types{$path_arr[1]}) &&
($alias_info_array{$curpos}{'SYSTEM_CREATED'} eq 'Y'))
{
$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 <odldg>:<newdg> #
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 = <FILE>;
# 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 '<olddgname>:<newdgname>,...', 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.1.0.2. 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;
# bug 25895224: User created dir can be with same name as supported
# file types (e.g backupset). Only discard system created dir.
if (defined($path[1]) && defined($supported_types{uc($path[1])}) &&
($alias_info_array->{$alias}->{'SYSTEM_CREATED'} eq 'Y'))
{
# 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{'size'} = " ";
$fstmt{'blksz'} = $acfs->{$_}{'METADATABLOCKSIZE'};
$fstmt{'compression'}= $acfs->{$_}{'COMPRESSION'} || 0;
$fstmt{'accel'} = " ";
$fstmt{'dirperm'} = (split(' ', $mntpthperm))[0];
$fstmt{'dirgid'} = (split(':', $mntpthperm))[-1];
$fstmt{'diruid'} = (split(':',((split(' ',
$mntpthperm))[-1])))[0];
if ($ostype eq 'Solaris' || $ostype eq 'AIX')
{
$fstmt{'compat'} = "${osprefix}o c=" .
$acfs->{$_}{'ACFSCOMPATIBILITY'};
}
else
{
$fstmt{'compat'} = "${osprefix}c " . $acfs->{$_}{'ACFSCOMPATIBILITY'};
}
if ($ostype eq 'AIX')
{
$acfs->{$_}{'TOTALSIZE'} = int ($acfs->{$_}{'TOTALSIZE'} / 1024);
$fstmt{'size'} = "${osprefix}s" .
$acfs->{$_}{'TOTALSIZE'} . "K";
}
else
{
$fstmt{'size'} = $acfs->{$_}{'TOTALSIZE'};
}
# 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 )
{
if ($ostype eq 'Solaris' || $ostype eq 'AIX')
{
$fstmt{'blksz'} = "${osprefix}o i=" . $fstmt{'blksz'};
}
else
{
$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'};
if ($ostype eq 'Linux' || $ostype eq 'Windows')
{
$fstmt{'accel'} = "${osprefix}a " . $fstmt{'accel'};
}
elsif ($ostype eq 'AIX')
{
$fstmt{'accel'} = "${osprefix}o a=" . $fstmt{'accel'};
}
else
{
$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";
}
elsif ($ostype eq 'Linux')
{
@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";
}
elsif ($ostype eq 'Windows')
{
@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";
}
else
{
@acfs_util = (
"VOLUMENAME",
"TOTALSIZE",
"RESIZEINCREMENT",
"RESIZEMAX",
"METADATABLOCKSIZE",
"COMPRESSION",
"SECURITY",
"ENCRYPTION"
);
$qry = "acfsutil info fs $mountpath ${osprefix}o ".
"primaryvolume,totalspace," .
"autoresizeincrement,autoresizemax,metablocksize," .
"iscompression,issecurity,isencryption 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 = "";
if (!$args)
{
return 0;
}
# 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 <command> 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;
OHA YOOOO