#!/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 <viewname>" 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=`symaccess list -type storage -output XML 2> 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=`symaccess list view -output XML 2> 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=`symaccess show view $name -output XML 2> 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 "<h1>Masking View Report</h1>\n<h2>$date $ENV{SYMCLI_SID}</h2>\n<table border cellpadding=5>\n";
print "<tr><th>view <th>init group <th>port group <th>storage group <th>capacity</tr>\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 "<tr><td><a href=\"#%s\">%s</a><td>%s<td>%s<td>%s<td align=right>%d</tr>\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 "<tr><td> <td> <td> <td>total MB<td>$total</table>\n<br>\n";
print "<hr>\n";
print "\n<h2><a href=#unused-sglist>Link to Unmasked Storage Group List</a></h2>\n";
print "<hr>\n";
print "\n<h2>Masking View Details</h2>\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> 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=`symaccess show view $view -output XML 2>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=`symaccess show view $view 2> NULL` ;
if ( $html == 1 ) {
print "<a name=\"$view\">\n";
print "<a href=#top>[top]</a>\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 "<html>\n<pre>\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 "<hr>\n";
print "<a name=\"unused-sglist\">\n";
print "\n<h2>Unmasked Storage Groups</h2>\n";
print "<a href=#top>[top]</a>\n";
} else {
print "\n\nUnmasked Storage Groups\n";
};
foreach $sg (sort keys (%sglist)) {
if ( $sglist{$sg} eq "" ) {
if ( $html == 1 ) {
print "<a href=\"#sg-$sg\">$sg</a>\n";
} else {
print "$sg\n";
};
};
};
# show details of each unused sg
foreach $sg (sort keys (%sglist)) {
if ( $sglist{$sg} eq "" ) {
my $cmdout=`symaccess show $sg -type storage 2> NULL` ;
if ( $html == 1 ) {
print "<a name=\"sg-$sg\">\n";
print "<h2>$sg</h2>\n";
print "<a href=#unused-sglist>[unmasked list]</a>\n";
print "$cmdout"
} else {
print "\n$cmdout";
};
};
};
};
if ( $html == 1 ) {
print "</pre>\n</html>\n";
};