#!/usr/bin/perl # # symaccess masking view report # # Produces a report summarizing the masking views and storage groups for an EMC VMax. # This script uses the symaccess command with the "-output XML" option to obtain its # information and requires the use of the perl XML module to do the parsing. # # The script can produce plain text, or a simple HTML page. The HTML page has # intra document links to make it easier to navigate around. The script can also # produce a "light" or "list only" version of it's output, where it just prints the # devices and the masking views that the devices are in. # # In a nut shell, the full output of this script consists of a prettied up version of # "symaccess list view" with the disk space used by each view listed. Then the script # runs "symaccess show view " for each view. It also runs a symaccess show # for any storage groups that are not included in any masking views. # # The script does not print information about old style mapping/masking commands, it # only documents symaccess structures. # # The script can take a while to run if a system has many masking views. Running # the script with a -v for verbose gives feedback on what the script is doing, so # it can be handy to use that option when initially testing the script in a new # environment. # # This script has been tested on Linux and AIX. It requires SYMCLI. # # Options: # -s sid - specifies the SID of the VMax. Otherwise we depend on SYMCLI_SID # -v - Verbose. Prints diag messages as it runs various symcli commands. # -l - light or list output. # prints device ids and the masking view that they are in. # -h - prints usage statement # -H - HTML output # # Andy Welter # # V1.0 March 2011, initial version. # V1.1 May 2011, cleanup unneeded code. # V1.2 May 2011, add "light" option that only lists devices with their masking view. # V1.2 Aug 2011, add reporting for storage groups that are not in views. # V1.3 Dec 2011, added usage statement and some comments. # # use module use XML::Simple; use Data::Dumper; use Getopt::Std; $usage="USAGE: emc-maskrep <-s sid> <-l> <-H> <-v>\n-s specify SID if not using SYMCLI_SID env variable\n-l sg list only, aka light output\n-H html output\n-v verbose output\n"; getopts ('s:Hhlv') || die "$usage"; if ( $opt_s ne "" ) { $ENV{SYMCLI_SID}=$opt_s; }; if ( $ENV{SYMCLI_SID} eq "" ) { print "ERROR: must specify an array with SYMCLI_SID or -s\n"; exit 1; }; if ( $opt_H ne "" ) { $html=1; }; # # Note: RDF info not currently supported. if ( $opt_r ne "" ) { # get RDF info with symrdf list $rdf=1; }; if ( $opt_l ne "" ) { # "list" output, only list devices by storage view $light=1; }; if ( $opt_v ne "" ) { # verbose output... print info to std err to track progress. $verbose=1; }; if ( $opt_h ne "" ) { # help and exit print "$usage"; exit 0; }; sub refToList { # when we have one item in a list in the XML, it # gets parsed as a scalar instead of an array. So we sometimes # want to be able to force that to be one item list to make # coding easier. # # This is complicated, so I'll break it down: # @_ is the array of parameters to the function. # @_[0] is the first parameter, which in this case is a reference # The ref test then checks to see whether that reference is to a scalar # or is a reference to an array. my @list; my $item; if ( ref (@_[0]) eq "ARRAY" ) { # The first parameter is a reference to an array. But what I want # is the array itself, not the reference to it. So that is what the @{} gets me. #@list=@{@_[0]}; foreach $item (@{@_[0]}) { if ($item ne "") { push (@list, $item); }; }; } else { # the reference is to a scalar, and that is easier to decode. I then turn # it into an array. my ($item)=(@_); @list=($item); }; # and now I return the array. return @list; }; sub getSgList() { my $xml = new XML::Simple (KeyAttr=>[]); $verbose && print STDERR "list -type storage...\n"; my $cmdout=`/usr/symcli/bin/symaccess list -type storage -output XML 2> /dev/null`; if ( $cmdout eq "" ) { print "ERROR symaccess list -type storge \n"; return; }; my $sym=$xml->XMLin($cmdout); my @sglist=refToList ($sym->{Symmetrix}{Storage_Group}); my $sg; my %sgdb; my $name; foreach $sg (@sglist) { $name=$sg->{Group_Info}{group_name}; $sgdb{$name}=""; }; return %sgdb; }; sub getMaskViews() { my $xml = new XML::Simple (KeyAttr=>[]); $verbose && print STDERR "list view...\n"; my $cmdout=`/usr/symcli/bin/symaccess list view -output XML 2> /dev/null`; if ( $cmdout eq "" ) { print "ERROR symaccess list view \n"; return; }; my $sym=$xml->XMLin($cmdout); my @viewlist=refToList ($sym->{Symmetrix}{Masking_View}); my $view; my $viewXML,$showViewOut; my %viewdb; my $name,$ig,$pg,$sg,$size; foreach $view (@viewlist) { $name= $view->{View_Info}{view_name}; $ig= $view->{View_Info}{init_grpname}; $pg= $view->{View_Info}{port_grpname}; $sg= $view->{View_Info}{stor_grpname}; my $size=0; if ( $light != 1 ) { $verbose && print STDERR "get size view $name...\n"; $showViewOut=`/usr/symcli/bin/symaccess show view $name -output XML 2> /dev/null`; $viewXML=$xml->XMLin($showViewOut); my $device,@devlist; @devlist=refToList($viewXML->{Symmetrix}{Masking_View}{View_Info}{Device}); my @cap; foreach $device (@devlist) { @cap=refToList($device->{capacity}); $size+=$cap[0]; }; }; $viewdb{$name}="$ig,$pg,$sg,$size"; }; return %viewdb; }; sub printListViews { my (%maskviews)=@_; my $view,@values; $date=`date`; if ( $html == 1 ) { print "

Masking View Report

\n

$date $ENV{SYMCLI_SID}

\n\n"; print "\n"; } else { print "#########################################################################################\n"; print "# Masking View Report $date $ENV{SYMCLI_SID}\n"; print "# view init group port group storage group capacity\n"; print "################# ################# ################# ################## #########\n"; }; my $total=0; foreach $view (sort (keys (%maskviews))) { @values=split /,/,$maskviews{$view}; $total+=$values[3]; if ( $html == 1 ) { printf "\n", $view,$view,$values[0],$values[1],$values[2],$values[3]; } else { printf "%-19s %-19s %-19s %-19s %9d\n", $view,$values[0],$values[1],$values[2],$values[3]; }; }; if ( $html == 1 ) { print "
view init group port group storage group capacity
%s%s%s%s%d
   total MB$total
\n
\n"; print "
\n"; print "\n

Link to Unmasked Storage Group List

\n"; print "
\n"; print "\n

Masking View Details

\n"; } else { print "%80s%10d\n"," ", $total; }; }; # # NOTE: rdf info option not currently supported. sub getRDF { my $xml = new XML::Simple (KeyAttr=>[]); $verbose && print STDERR "run symrdf list...\n"; return; my $cmdout=`/usr/symcli/bin/symrdf list -output XML 2> /dev/null`; my $sym=$xml->XMLin($cmdout); my @devlist=refToList ($sym->{Symmetrix}{Device}); my $dev; my %rdfhash; my $local,$remote,$type,$group; foreach $dev (@devlist) { $local=$dev->{Local}{dev_name}; $type=$dev->{Local}{type}; $group=$dev->{Local}{ra_group_num}; $remote=$dev->{Remote}{dev_name}; $rdfhash{$local}="$type:$group\t$remote"; }; return %rdfhash }; sub getLightView { my ($view)=@_; my $xml = new XML::Simple (KeyAttr=>[]); $verbose && print STDERR "show view $view...\n"; my $cmdout=`/usr/symcli/bin/symaccess show view $view -output XML 2>/dev/null`; my $sym=$xml->XMLin($cmdout); my @devlist=refToList ($sym->{Symmetrix}{Masking_View}{View_Info}{Device}); my @devs,$dev; foreach $dev (@devlist) { push (@devs,$dev->{dev_name}); }; return @devs; }; sub showLightView { my ($view)=@_; my @list=getLightView($view); foreach $dev (@list) { print "$dev\t$view\n"; }; }; sub showView { my ($view)=@_; my $xml = new XML::Simple (KeyAttr=>[]); $verbose && print STDERR "show view $view...\n"; my $cmdout=`/usr/symcli/bin/symaccess show view $view 2> /dev/null` ; if ( $html == 1 ) { print "\n"; print "[top]\n"; }; print "\n\n############################################################################\n"; print "## View $view\n"; print "############################################################################\n"; print "$cmdout"; }; ############################################################################ # Main routine ############################################################################ my (%sglist)=&getSgList(); my (%maskviews)=&getMaskViews(); if ( $html == 1 ) { print "\n
\n";
};
if ( $light != 1 ) {
	printListViews(%maskviews);
};
my @masklist=sort keys (%maskviews);
my %rdfhash;
if ( $rdf ne "" ) {
	%rdfhash=getRDF();
};
foreach $view (@masklist) {
    if ( $light != 1 ) {
	showView($view);
    } else {
	showLightView($view);
    };
    # mark the storage group for this view as used
    my @mvparms=split/,/,$maskviews{$view};
    $sglist{$mvparms[2]}=$view; 
};

if ( $light != 1 ) {
# print out the list of unused storage groups
my $sg;
if ( $html == 1 ) {
	print "
\n"; print "\n"; print "\n

Unmasked Storage Groups

\n"; print "
[top]\n"; } else { print "\n\nUnmasked Storage Groups\n"; }; foreach $sg (sort keys (%sglist)) { if ( $sglist{$sg} eq "" ) { if ( $html == 1 ) { print "$sg\n"; } else { print "$sg\n"; }; }; }; # show details of each unused sg foreach $sg (sort keys (%sglist)) { if ( $sglist{$sg} eq "" ) { my $cmdout=`/usr/symcli/bin/symaccess show $sg -type storage 2> /dev/null` ; if ( $html == 1 ) { print "\n"; print "

$sg

\n"; print "
[unmasked list]\n"; print "$cmdout" } else { print "\n$cmdout"; }; }; }; }; if ( $html == 1 ) { print "
\n\n"; };