import sys
import pprint
import logging
import collections
import argparse
import subprocess
import prettytable
import xml.etree.ElementTree as ET
VERSION = '1.0'
COLORS = 1
def setup_logging():
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(module)s - %(funcName)s -\
%(levelname)s - %(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p')
return logging.getLogger(__name__)
def run_command(cmd):
log.debug("Executing command: %s", cmd)
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
shell=True)
stdout, stderr = p.communicate()
if p.returncode:
message("red", "Error running command: " + cmd, 0)
message("red", stderr, 1)
return stdout
def message(color, msg, exitcode=0):
if color == 'blue' : color = '\033[1;34m'
elif color == 'red' : color = '\033[1;31m'
elif color == 'green' : color = '\033[0;32m'
elif color == 'yellow' : color = '\033[0;33m'
elif color == 'p_color' : color = '\033[01;37;44m'
elif color == 'nocolor' : color = '\033[0m'
else:
print("Error: message function: invalid color:" , color)
sys.exit(1)
nocolor ='\033[0m'
if COLORS:
print(color + msg + nocolor)
else:
print(msg)
if exitcode:
sys.exit(exitcode)
def nested_dict():
return collections.defaultdict(nested_dict)
def get_xml_text(elem, tag):
try:
x = elem.find(tag).text
except:
message("red", "Error: XML tag '" + tag + "' not found", 1)
return x
def sg_to_dict(symmid):
sgs = nested_dict()
xml_string = run_command("symsg -sid " + symmid + " list -v -output xml_e")
sgtree = ET.fromstring(xml_string)
for elem in sgtree.iterfind('SG'):
sg_name = get_xml_text(elem, 'SG_Info/name')
sgs[sg_name]['symmid'] = get_xml_text(elem, 'SG_Info/symid')
sgs[sg_name]['slo_name'] = get_xml_text(elem, 'SG_Info/SLO_name')
sgs[sg_name]['workload'] = get_xml_text(elem, 'SG_Info/Workload')
sgs[sg_name]['srp_name'] = get_xml_text(elem, 'SG_Info/SRP_name')
sgs[sg_name]['hostlimit'] = get_xml_text(elem, 'SG_Info/HostIOLimit_status')
sgs[sg_name]['dynamic'] = get_xml_text(elem, 'SG_Info/Dynamic_Distribution')
sgs[sg_name]['iops'] = get_xml_text(elem, 'SG_Info/HostIOLimit_max_io_sec')
sgs[sg_name]['mbs'] = get_xml_text(elem, 'SG_Info/HostIOLimit_max_mb_sec')
sgs[sg_name]['mv'] = get_xml_text(elem, 'SG_Info/Masking_views')
sgs[sg_name]['child'] = list()
sgs[sg_name]['parent'] = list()
for elem_sg_group in elem.iterfind('SG_Info/SG_group_info/SG'):
relation = get_xml_text(elem_sg_group, 'Cascade_Status')
sg_group_name = get_xml_text(elem_sg_group, 'name')
if relation == 'IsChild':
sgs[sg_name]['child'].append(sg_group_name)
elif relation == 'IsParent':
sgs[sg_name]['parent'].append(sg_group_name)
if not sgs[sg_name]['parent'] and not sgs[sg_name]['child']:
sgs[sg_name]['relation'] = 'S'
elif sgs[sg_name]['parent']:
sgs[sg_name]['relation'] = 'C'
elif sgs[sg_name]['child']:
sgs[sg_name]['relation'] = 'P'
for lun in elem.iterfind('DEVS_List/Device'):
lun_id = get_xml_text(lun, 'dev_name')
lun_mb = get_xml_text(lun, 'megabytes')
sgs[sg_name]['luns'][lun_id] = int(lun_mb)
sgs[sg_name]['num_luns'] = len(sgs[sg_name]['luns'])
sgs[sg_name]['total_size'] = sum(sgs[sg_name]['luns'].values()) // 1024
if not log.disabled:
log.debug("sg dict:")
pprint.pprint(sgs)
return sgs
def get_sgs_relationship(sgs):
sg_relation = list()
for sg in sgs:
sg_name = sg.title().upper()
if not sgs[sg]['parent'] and not sgs[sg]['child']:
sg_relation.append([sg_name])
elif not sgs[sg]['parent']:
x = ",".join(sgs[sg]['child'])
x = sg_name + ',' + x
sg_relation.append(x.split(","))
else:
pass
if not log.disabled:
log.debug("sg_relation list:")
pprint.pprint(sg_relation)
return sg_relation
def print_sg_info(sgs_relationship, sgs):
p_color = '\033[01;37;44m'
nocolor = '\033[0m'
header = list()
header.append("SG_Name")
header.append("Relation")
header.append("SymmID")
header.append("# LUNs")
header.append("Total_Size (GB)")
header.append("SLO")
header.append("Workload")
header.append("Host_Limit")
header.append("Host_Limit_IOPS")
header.append("Host_Limit_MBs")
header.append("Dynamic_Distr")
header.append("Masking")
output = prettytable.PrettyTable(header)
output.format = True
for i in sorted(sgs_relationship):
for sg_name in i:
row = list()
if sgs[sg_name]['relation'] == 'P' and COLORS:
row.append(p_color + sg_name)
elif sgs[sg_name]['relation'] == 'S' and COLORS:
row.append(p_color + sg_name)
else:
row.append(sg_name)
row.append(sgs[sg_name]['relation'])
row.append(sgs[sg_name]['symmid'])
row.append(str(sgs[sg_name]['num_luns']))
row.append(str(sgs[sg_name]['total_size']))
row.append(sgs[sg_name]['slo_name'])
row.append(sgs[sg_name]['workload'])
row.append(sgs[sg_name]['hostlimit'])
row.append(sgs[sg_name]['iops'])
row.append(sgs[sg_name]['mbs'])
row.append(sgs[sg_name]['dynamic'])
if sgs[sg_name]['relation'] == 'P' and COLORS:
row.append(sgs[sg_name]['mv'] + nocolor)
elif sgs[sg_name]['relation'] == 'S' and COLORS:
row.append(sgs[sg_name]['mv'] + nocolor)
else:
row.append(sgs[sg_name]['mv'])
output.add_row(row)
output.align["SG_Name"] = "l"
print(output)
def parse_parameters():
epilog = '''
Example of use:
%s -sid 001
%s -v -sid 002
''' % (sys.argv[0],sys.argv[0])
parser = argparse.ArgumentParser(description='Script to show Storage Group details',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=epilog)
parser.add_argument('--version',
action='version',
version=VERSION)
parser.add_argument('--verbose', '-v',
action='store_true',
help='verbose flag',
dest='verbose')
parser.add_argument('-sid',
type=str,
required=True,
help='Symmetrix ID')
if len(sys.argv) < 2:
parser.print_help()
sys.exit(0)
return parser.parse_args()
def main():
global log
log = setup_logging()
args = parse_parameters()
if not args.verbose:
log.disabled = True
log.debug('CMD line args: %s', vars(args))
if not args.sid.isdigit():
message("red", "Error: SymmID must be a number", 1)
sgs = sg_to_dict(args.sid)
sgs_relationship = get_sgs_relationship(sgs)
print_sg_info(sgs_relationship, sgs)
if __name__ == '__main__':
main()