#!/usr/bin/python3.6 # # IBM Disclaimer # # IBM grants you a nonexclusive copyright license to use all programming code examples from which you can generate similar  # function tailored to your own specific needs. # SUBJECT TO ANY STATUTORY WARRANTIES WHICH CANNOT BE EXCLUDED, IBM, ITS PROGRAM DEVELOPERS AND SUPPLIERS MAKE NO  # WARRANTIES OR CONDITIONS EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS  # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT, REGARDING THE PROGRAM OR TECHNICAL SUPPORT, IF ANY. # # UNDER NO CIRCUMSTANCES IS IBM, ITS PROGRAM DEVELOPERS OR SUPPLIERS LIABLE FOR ANY OF THE FOLLOWING, EVEN IF INFORMED OF THEIR POSSIBILITY: # # 1. LOSS OF, OR DAMAGE TO, DATA; # 2. DIRECT, SPECIAL, INCIDENTAL, OR INDIRECT DAMAGES, OR FOR ANY ECONOMIC CONSEQUENTIAL DAMAGES; OR # 3. LOST PROFITS, BUSINESS, REVENUE, GOODWILL, OR ANTICIPATED SAVINGS. # # SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF DIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, SO SOME OR ALL  # OF THE ABOVE LIMITATIONS OR EXCLUSIONS MAY NOT APPLY TO YOU. # # All due acknowledgments to the IBM team who wrote ansible.ibm_power.hmc # much of this code is re-used # # all due acknowledments to the IBM team who wrote ansible.ibm_power.hmc # much of this code is re-used # because I hope to turn this into ansilibic module/extention of ansible.ibm_power.hmc # # Who Ver What # D Lancaster v1.0 allows you to get PS and FAN status for all systems # -i hmc or hmc,hmc2 eg a list of hmcs # -s sysnames or sysname1,sysname2 a list of systems to look at (else all is implied) from __future__ import absolute_import, division, print_function __metaclass__ = type import time import json import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning import getpass import re import xml.etree.ElementTree as ET NEED_LXML = False try: from lxml import etree, objectify except ImportError: NEED_LXML = True import logging LOG_FILENAME = "/tmp/power_hmc.log" #logging.basicConfig( # filename=LOG_FILENAME, # level=logging.DEBUG) logger = logging.getLogger(__name__) import argparse #from hmc_exeptions class Error(Exception): """ Abstract base class that serves as a common exception superclass for the hmc ansible module. """ def __init__(self, *args): if args: self.message = args[0] else: self.message = None def __repr__(self): if self.message: return "Error: {0}".format(self.message) def __str__(self): if self.message: return self.message def xml_strip_namespace(xml_str): parser = etree.XMLParser(recover=True, encoding='utf-8') root = etree.fromstring(xml_str, parser) for elem in root.getiterator(): if not hasattr(elem.tag, 'find'): continue i = elem.tag.find('}') if i >= 0: elem.tag = elem.tag[i + 1:] objectify.deannotate(root, cleanup_namespaces=True) return root def parse_error_response(error): if isinstance(error, urllib_error.HTTPError): xml_str = error.read().decode() if not xml_str: logger.debug(error.url) error_msg = "HTTP Error {0}: {1}".format(error.code, error.reason) else: dom = xml_strip_namespace(xml_str) error_msg_l = dom.xpath("//Message") if error_msg_l: error_msg = error_msg_l[0].text if "Failed to unmarshal input payload" in error_msg: error_msg = "Current HMC version might not support some of input settings or invalid input" else: error_msg = "Unknown http error" else: error_msg = repr(error) logger.debug(error_msg) return error_msg def _logonPayload(user, password): root = ET.Element("LogonRequest") root.attrib = {"schemaVersion": "V1_0", "xmlns": "http://www.ibm.com/xmlns/systems/power/firmware/web/mc/2012_10/", "xmlns:mc": "http://www.ibm.com/xmlns/systems/power/firmware/web/mc/2012_10/"} ET.SubElement(root, "UserID").text = user ET.SubElement(root, "Password").text = password return ET.tostring(root) class HmcRestClient: def __init__(self, hmc_ip, username, password): if NEED_LXML: raise Error("Missing prerequisite lxml package. Hint pip install lxml") self.hmc_ip = hmc_ip self.username = username self.password = password self.session = self.logon() logger.debug(self.session) def logon(self): requests.packages.urllib3.disable_warnings(InsecureRequestWarning) header = {'Content-Type': 'application/vnd.ibm.powervm.web+xml; type=LogonRequest'} url = "https://{0}/rest/api/web/Logon".format(self.hmc_ip) resp = requests.put(url, headers=header, data=_logonPayload(self.username, self.password), verify=False) logger.debug(resp.status_code) if resp.status_code != 200: logger.debug('Logon failed - ? check your password') exit(1) #print("DEBUG: Returned:{}".format(resp.text)) respxml = resp.text.encode('utf-8') #doc = xml_strip_namespace(resp.text) doc = xml_strip_namespace(respxml) session = doc.xpath('X-API-Session')[0].text return session def logoff(self): header = {'Content-Type': 'application/vnd.ibm.powervm.web+xml; type=LogonRequest', 'Authorization': 'Basic Og==', 'X-API-Session': self.session} url = "https://{0}/rest/api/web/Logon".format(self.hmc_ip) resp = requests.delete(url, headers=header, verify=False) def getManagedSystems(self): url = "https://{0}/rest/api/uom/ManagedSystem".format(self.hmc_ip) header = {'X-API-Session': self.session, 'Accept': 'application/vnd.ibm.powervm.uom+xml; type=ManagedSystem'} response = requests.get(url, headers=header, data=_logonPayload(self.username, self.password), timeout=300, verify=False) if response.status_code == 204: return None respxml = response.text.encode('utf-8') #managedsystems_root = xml_strip_namespace(response.text) managedsystems_root = xml_strip_namespace(respxml) return managedsystems_root def getManagedSystemsQuick(self): url = "https://{0}/rest/api/uom/ManagedSystem/quick/All".format(self.hmc_ip) header = {'X-API-Session': self.session, 'Accept': '*/*'} response = requests.get(url, headers=header, timeout=300, verify=False) if response.status_code != 200: return None return response.text # this is a new function def getManagedSystemHWInventory(self, system_uuid): url = "https://{0}/rest/api/uom/ManagedSystem/{1}?hwinventory=true".format(self.hmc_ip, system_uuid) header = {'X-API-Session': self.session, 'Accept': '*/*'} response = requests.get(url, headers=header, timeout=300, verify=False) if response.status_code != 200: logger.debug("Get of Managed System failed. Respsonse code: %d", response.status_code) return None respxml = response.text.encode('utf-8') #managedsystems_root = xml_strip_namespace(response.text) managedsystems_root = xml_strip_namespace(respxml) return managedsystems_root.xpath("//ManagedSystem")[0] # # New functions (with a nod to ansible.ibm.power_hmc team for the lxml xpath # examples # # Get the HW elements from the managed_systems xml # uses for both PowerSupply and FANs at the moment def getHW(hw_element_xml, top_tag, sub_tag): hw_temp = [] msys_hw_units = hw_element_xml.xpath(top_tag)[0] hw_units = etree.ElementTree(msys_hw_units) hw_items = hw_units.xpath(sub_tag) for each_item in hw_items: hw_dict = {} hw_dict['Location'] = each_item.xpath("LocationCode")[0].text hw_dict['FruNumber'] = each_item.xpath("FruNumber")[0].text hw_dict['SerialNumber'] = each_item.xpath("SerialNumber")[0].text hw_dict['State'] = each_item.xpath("State")[0].text hw_dict['Health'] = each_item.xpath("Health")[0].text if 'FAN' in sub_tag: hw_dict['Description'] = 'Fan' else: hw_dict['Description'] = each_item.xpath("Description")[0].text hw_dict['MemberId'] = each_item.xpath("MemberId")[0].text hw_temp.append(hw_dict) return hw_temp # process PowerSupply and FANs for one UUID/AtomID def process_system(atomID, sysname): print("SystemName:{}".format(sysname)) # get the HWInventory msys_hw = rest_conn.getManagedSystemHWInventory(atomID) #if we got some xml back if len(msys_hw) > 0: #process PowerSupplies ps_tag = '//PowerSupplies' ps_sub_tag = '//PowerSupply' # more tags can be added later - may be a better pythonic was later - left to the viewer ... hw = [] # returns a json list of dicts hw = getHW(msys_hw, ps_tag, ps_sub_tag) #process FANs ps_tag = '//FANs' ps_sub_tag = '//FAN' hw.extend(getHW(msys_hw, ps_tag, ps_sub_tag)) if len(hw) > 0: # Title line print(" {:22s}{:8s}{:13s}{:10}{:7}{:21}{}".format("Location","FruNum","SerialNum","State","Health","Description","MemberId")) for item in hw: loc = item.get("Location") Fru = item.get("FruNumber").lstrip() SN = item.get("SerialNumber") St = item.get("State") Health = item.get("Health") Description = item.get("Description") MemberId = item.get("MemberId") # detail line print(" {:22s}{:8s}{:13s}{:10}{:7}{:21}{}".format(loc,Fru,SN,St,Health,Description,MemberId)) else: print(" hmmm - no HwInventory was obtained") # process PowerSupply and FANs for one UUID/AtomID def process_system_debug(atomID, sysname): print("Name:{} {}".format(sysname, atomID)) wr_debug=False msys_hw = rest_conn.getManagedSystemHWInventory(atomID) if wr_debug==True: filename = sysname + '_msys.xml' obj_xml = etree.tostring(msys_hw, pretty_print=True, xml_declaration=True) try: with open(filename,"wb") as xml_writer: xml_writer.write(obj_xml) except IOError: pass msys_hw_powersupplies = msys_hw.xpath("//PowerSupplies") msys_hw_fans = msys_hw.xpath("//FANs") for each_hw in msys_hw_powersupplies: hw_unit = etree.ElementTree(each_hw) hw_location = hw_unit.xpath('//PowerSupply') print("LOC:{} count:{}".format(hw_location,len(hw_location))) for each_ps in hw_location: loc = each_ps.xpath("LocationCode")[0].text print("LOC_ps:{} ".format(loc)) #check if system is in whitelist def check_system_req(sysname, whitelisted_systems): for w_sys in whitelisted_systems: if w_sys in sysname: return True return False # # Enter the scene, my MAIN man # if __name__ == '__main__': hmc_user = '' hmc_password = '' whitelisted_systems = [] hmc_hosts = [] # is -p will prompt else will take the input as password class Password(argparse.Action): def __call__(self, parser, namespace, values, option_string): if values is None: values = getpass.getpass(prompt='Password:') setattr(namespace, self.dest, values) # arguments parser = argparse.ArgumentParser(description="Get HW Inventory from hmcs") parser.add_argument('-s', '--sysnames', type=str, help='system name(s),...', dest='sysnames') # -i used cause later this will be -i when this goes up to ansible parser.add_argument('-i', '--hmc', type=str, help='hmc name(s),...', dest='hmcnames', required=True) parser.add_argument('-u', '--user', type=str, help='hmc user', required=True, dest='hmcuser') parser.add_argument('-p', '--password', action=Password, nargs='?', help='hmc password', dest='hmcpassword') args = parser.parse_args() hmc_hosts = args.hmcnames.split(",") hmc_user = args.hmcuser hmc_password = args.hmcpassword if args.sysnames: whitelisted_systems = args.sysnames.split(",") # insanity checking if len(hmc_user) == 0 or len(hmc_password) == 0 or len(hmc_hosts) == 0: print("prog: -i | --hmc hmc name (or comma seperated list)") print(" -u | --user hmc user ") print(" -p | --password hmc password ") print(" -s | --sysnames system name (or comma seperated list)") exit(0) #password = getpass.getpass(prompt='Password:') # for each HMC we have been given for hmc in hmc_hosts: # create a connection rest_conn = HmcRestClient(hmc, hmc_user, hmc_password) # get the list of managed systems managed_systems = json.loads(rest_conn.getManagedSystemsQuick()) # for each msys on this HMC for system in managed_systems: sysname = system.get("SystemName") atomID = system.get("UUID") #is there a white list ? if len(whitelisted_systems) > 0: if check_system_req(sysname, whitelisted_systems) == True: process_system(atomID, sysname) else: # process all systems names (NO whitelist provided) process_system(atomID, sysname) # important step rest_conn.logoff()