1b8cc3257SManojkiran Eda#!/usr/bin/env python3 2b8cc3257SManojkiran Eda 3b8cc3257SManojkiran Eda"""Tool to visualize PLDM PDR's""" 4b8cc3257SManojkiran Eda 5b8cc3257SManojkiran Edaimport argparse 6b8cc3257SManojkiran Edaimport hashlib 7*c44715dcSPatrick Williamsimport json 89a8192d9SBrad Bishopimport os 9c8906373SBrad Bishopimport shlex 10c8906373SBrad Bishopimport shutil 11c8906373SBrad Bishopimport subprocess 12*c44715dcSPatrick Williamsimport sys 13*c44715dcSPatrick Williamsfrom datetime import datetime 14*c44715dcSPatrick Williams 15*c44715dcSPatrick Williamsimport paramiko 16*c44715dcSPatrick Williamsfrom graphviz import Digraph 17*c44715dcSPatrick Williamsfrom tabulate import tabulate 18b8cc3257SManojkiran Eda 19b8cc3257SManojkiran Eda 20241e0683SBrad Bishopclass Process: 21241e0683SBrad Bishop """Interface definition for interacting with a process created by an 22241e0683SBrad Bishop Executor.""" 23241e0683SBrad Bishop 24241e0683SBrad Bishop def __init__(self, stdout, stderr): 25241e0683SBrad Bishop """Construct a Process object. Process object clients can read the 26241e0683SBrad Bishop process stdout and stderr with os.read(), and can wait for the 27241e0683SBrad Bishop process to exit. 28241e0683SBrad Bishop 29241e0683SBrad Bishop Parameters: 30241e0683SBrad Bishop stdout: os.read()able stream representing stdout 31241e0683SBrad Bishop stderr: os.read()able stream representing stderr 32241e0683SBrad Bishop """ 33241e0683SBrad Bishop 34241e0683SBrad Bishop self.stdout = stdout 35241e0683SBrad Bishop self.stderr = stderr 36241e0683SBrad Bishop 37241e0683SBrad Bishop def wait(self): 38241e0683SBrad Bishop """Wait for the process to finish, and return its exit status.""" 39241e0683SBrad Bishop 40241e0683SBrad Bishop raise NotImplementedError 41241e0683SBrad Bishop 42241e0683SBrad Bishop 437dc416f6SBrad Bishopclass Executor: 447dc416f6SBrad Bishop """Interface definition for interacting with executors. An executor is an 457dc416f6SBrad Bishop object that can run a program.""" 46b8cc3257SManojkiran Eda 477dc416f6SBrad Bishop def exec_command(self, cmd): 487dc416f6SBrad Bishop raise NotImplementedError 497dc416f6SBrad Bishop 507dc416f6SBrad Bishop def close(self): 517dc416f6SBrad Bishop pass 527dc416f6SBrad Bishop 537dc416f6SBrad Bishop 54241e0683SBrad Bishopclass ParamikoProcess(Process): 55241e0683SBrad Bishop """Concrete implementation of the Process interface that adapts Paramiko 56241e0683SBrad Bishop interfaces to the Process interface requirements.""" 57241e0683SBrad Bishop 58241e0683SBrad Bishop def __init__(self, stdout, stderr): 59241e0683SBrad Bishop super(ParamikoProcess, self).__init__(stdout, stderr) 60241e0683SBrad Bishop 61241e0683SBrad Bishop def wait(self): 62241e0683SBrad Bishop return self.stderr.channel.recv_exit_status() 63241e0683SBrad Bishop 64241e0683SBrad Bishop 657dc416f6SBrad Bishopclass ParamikoExecutor(Executor): 667dc416f6SBrad Bishop """Concrete implementation of the Executor interface that uses 677dc416f6SBrad Bishop Paramiko to connect to a remote BMC to run the program.""" 687dc416f6SBrad Bishop 697dc416f6SBrad Bishop def __init__(self, hostname, uname, passwd, port, **kw): 707dc416f6SBrad Bishop """This function is responsible for connecting to the BMC via 717dc416f6SBrad Bishop ssh and returning an executor object. 72b8cc3257SManojkiran Eda 73b8cc3257SManojkiran Eda Parameters: 74b8cc3257SManojkiran Eda hostname: hostname/IP address of BMC 75b8cc3257SManojkiran Eda uname: ssh username of BMC 76b8cc3257SManojkiran Eda passwd: ssh password of BMC 77b8cc3257SManojkiran Eda port: ssh port of BMC 78b8cc3257SManojkiran Eda """ 79b8cc3257SManojkiran Eda 807dc416f6SBrad Bishop super(ParamikoExecutor, self).__init__() 817dc416f6SBrad Bishop self.client = paramiko.SSHClient() 827dc416f6SBrad Bishop self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 837dc416f6SBrad Bishop self.client.connect( 84*c44715dcSPatrick Williams hostname, username=uname, password=passwd, port=port, **kw 85*c44715dcSPatrick Williams ) 867dc416f6SBrad Bishop 877dc416f6SBrad Bishop def exec_command(self, cmd): 88241e0683SBrad Bishop _, stdout, stderr = self.client.exec_command(cmd) 89241e0683SBrad Bishop return ParamikoProcess(stdout, stderr) 907dc416f6SBrad Bishop 917dc416f6SBrad Bishop def close(self): 927dc416f6SBrad Bishop self.client.close() 93b8cc3257SManojkiran Eda 94b8cc3257SManojkiran Eda 95c8906373SBrad Bishopclass SubprocessProcess(Process): 96c8906373SBrad Bishop def __init__(self, popen): 97c8906373SBrad Bishop self.popen = popen 98c8906373SBrad Bishop super(SubprocessProcess, self).__init__(popen.stdout, popen.stderr) 99c8906373SBrad Bishop 100c8906373SBrad Bishop def wait(self): 101c8906373SBrad Bishop self.popen.wait() 102c8906373SBrad Bishop return self.popen.returncode 103c8906373SBrad Bishop 104c8906373SBrad Bishop 105c8906373SBrad Bishopclass SubprocessExecutor(Executor): 106c8906373SBrad Bishop def __init__(self): 107c8906373SBrad Bishop super(SubprocessExecutor, self).__init__() 108c8906373SBrad Bishop 109c8906373SBrad Bishop def exec_command(self, cmd): 110c8906373SBrad Bishop args = shlex.split(cmd) 111c8906373SBrad Bishop args[0] = shutil.which(args[0]) 112c8906373SBrad Bishop p = subprocess.Popen( 113*c44715dcSPatrick Williams args, stdout=subprocess.PIPE, stderr=subprocess.PIPE 114*c44715dcSPatrick Williams ) 115c8906373SBrad Bishop return SubprocessProcess(p) 116c8906373SBrad Bishop 117c8906373SBrad Bishop 118b8cc3257SManojkiran Edadef prepare_summary_report(state_sensor_pdr, state_effecter_pdr): 119b8cc3257SManojkiran Eda """This function is responsible to parse the state sensor pdr 120b8cc3257SManojkiran Eda and the state effecter pdr dictionaries and creating the 121b8cc3257SManojkiran Eda summary table. 122b8cc3257SManojkiran Eda 123b8cc3257SManojkiran Eda Parameters: 124b8cc3257SManojkiran Eda state_sensor_pdr: list of state sensor pdrs 125b8cc3257SManojkiran Eda state_effecter_pdr: list of state effecter pdrs 126b8cc3257SManojkiran Eda 127b8cc3257SManojkiran Eda """ 128b8cc3257SManojkiran Eda 129b8cc3257SManojkiran Eda summary_table = [] 130b8cc3257SManojkiran Eda headers = ["sensor_id", "entity_type", "state_set", "states"] 131b8cc3257SManojkiran Eda summary_table.append(headers) 132b8cc3257SManojkiran Eda for value in state_sensor_pdr.values(): 133b8cc3257SManojkiran Eda summary_record = [] 134*c44715dcSPatrick Williams sensor_possible_states = "" 135b8cc3257SManojkiran Eda for sensor_state in value["possibleStates[0]"]: 136b8cc3257SManojkiran Eda sensor_possible_states += sensor_state + "\n" 137*c44715dcSPatrick Williams summary_record.extend( 138*c44715dcSPatrick Williams [ 139*c44715dcSPatrick Williams value["sensorID"], 140*c44715dcSPatrick Williams value["entityType"], 141b8cc3257SManojkiran Eda value["stateSetID[0]"], 142*c44715dcSPatrick Williams sensor_possible_states, 143*c44715dcSPatrick Williams ] 144*c44715dcSPatrick Williams ) 145b8cc3257SManojkiran Eda summary_table.append(summary_record) 146b8cc3257SManojkiran Eda print("Created at : ", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 147b8cc3257SManojkiran Eda print(tabulate(summary_table, tablefmt="fancy_grid", headers="firstrow")) 148b8cc3257SManojkiran Eda 149b8cc3257SManojkiran Eda summary_table = [] 150b8cc3257SManojkiran Eda headers = ["effecter_id", "entity_type", "state_set", "states"] 151b8cc3257SManojkiran Eda summary_table.append(headers) 152b8cc3257SManojkiran Eda for value in state_effecter_pdr.values(): 153b8cc3257SManojkiran Eda summary_record = [] 154*c44715dcSPatrick Williams effecter_possible_states = "" 155b8cc3257SManojkiran Eda for state in value["possibleStates[0]"]: 156b8cc3257SManojkiran Eda effecter_possible_states += state + "\n" 157*c44715dcSPatrick Williams summary_record.extend( 158*c44715dcSPatrick Williams [ 159*c44715dcSPatrick Williams value["effecterID"], 160*c44715dcSPatrick Williams value["entityType"], 161b8cc3257SManojkiran Eda value["stateSetID[0]"], 162*c44715dcSPatrick Williams effecter_possible_states, 163*c44715dcSPatrick Williams ] 164*c44715dcSPatrick Williams ) 165b8cc3257SManojkiran Eda summary_table.append(summary_record) 166b8cc3257SManojkiran Eda print(tabulate(summary_table, tablefmt="fancy_grid", headers="firstrow")) 167b8cc3257SManojkiran Eda 168b8cc3257SManojkiran Eda 169b8cc3257SManojkiran Edadef draw_entity_associations(pdr, counter): 170b8cc3257SManojkiran Eda """This function is responsible to create a picture that captures 171b8cc3257SManojkiran Eda the entity association hierarchy based on the entity association 172b8cc3257SManojkiran Eda PDR's received from the BMC. 173b8cc3257SManojkiran Eda 174b8cc3257SManojkiran Eda Parameters: 175b8cc3257SManojkiran Eda pdr: list of entity association PDR's 176b8cc3257SManojkiran Eda counter: variable to capture the count of PDR's to unflatten 177b8cc3257SManojkiran Eda the tree 178b8cc3257SManojkiran Eda 179b8cc3257SManojkiran Eda """ 180b8cc3257SManojkiran Eda 181*c44715dcSPatrick Williams dot = Digraph( 182*c44715dcSPatrick Williams "entity_hierarchy", 183*c44715dcSPatrick Williams node_attr={"color": "lightblue1", "style": "filled"}, 184*c44715dcSPatrick Williams ) 185*c44715dcSPatrick Williams dot.attr( 186*c44715dcSPatrick Williams label=r"\n\nEntity Relation Diagram < " 187*c44715dcSPatrick Williams + str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 188*c44715dcSPatrick Williams + ">\n" 189*c44715dcSPatrick Williams ) 190*c44715dcSPatrick Williams dot.attr(fontsize="20") 191b8cc3257SManojkiran Eda edge_list = [] 192b8cc3257SManojkiran Eda for value in pdr.values(): 193*c44715dcSPatrick Williams parentnode = str(value["containerEntityType"]) + str( 194*c44715dcSPatrick Williams value["containerEntityInstanceNumber"] 195*c44715dcSPatrick Williams ) 196*c44715dcSPatrick Williams dot.node( 197*c44715dcSPatrick Williams hashlib.md5( 198*c44715dcSPatrick Williams ( 199*c44715dcSPatrick Williams parentnode + str(value["containerEntityContainerID"]) 200*c44715dcSPatrick Williams ).encode() 201*c44715dcSPatrick Williams ).hexdigest(), 202*c44715dcSPatrick Williams parentnode, 203*c44715dcSPatrick Williams ) 204b8cc3257SManojkiran Eda 205b8cc3257SManojkiran Eda for i in range(1, value["containedEntityCount"] + 1): 206*c44715dcSPatrick Williams childnode = str(value[f"containedEntityType[{i}]"]) + str( 207*c44715dcSPatrick Williams value[f"containedEntityInstanceNumber[{i}]"] 208*c44715dcSPatrick Williams ) 209b8cc3257SManojkiran Eda cid = str(value[f"containedEntityContainerID[{i}]"]) 210*c44715dcSPatrick Williams dot.node( 211*c44715dcSPatrick Williams hashlib.md5((childnode + cid).encode()).hexdigest(), childnode 212*c44715dcSPatrick Williams ) 213b8cc3257SManojkiran Eda 214*c44715dcSPatrick Williams if [ 215*c44715dcSPatrick Williams hashlib.md5( 216*c44715dcSPatrick Williams ( 217*c44715dcSPatrick Williams parentnode + str(value["containerEntityContainerID"]) 218*c44715dcSPatrick Williams ).encode() 219*c44715dcSPatrick Williams ).hexdigest(), 220*c44715dcSPatrick Williams hashlib.md5((childnode + cid).encode()).hexdigest(), 221*c44715dcSPatrick Williams ] not in edge_list: 222*c44715dcSPatrick Williams edge_list.append( 223*c44715dcSPatrick Williams [ 224*c44715dcSPatrick Williams hashlib.md5( 225*c44715dcSPatrick Williams ( 226*c44715dcSPatrick Williams parentnode 227*c44715dcSPatrick Williams + str(value["containerEntityContainerID"]) 228*c44715dcSPatrick Williams ).encode() 229*c44715dcSPatrick Williams ).hexdigest(), 230*c44715dcSPatrick Williams hashlib.md5((childnode + cid).encode()).hexdigest(), 231*c44715dcSPatrick Williams ] 232*c44715dcSPatrick Williams ) 233*c44715dcSPatrick Williams dot.edge( 234*c44715dcSPatrick Williams hashlib.md5( 235*c44715dcSPatrick Williams ( 236*c44715dcSPatrick Williams parentnode 237*c44715dcSPatrick Williams + str(value["containerEntityContainerID"]) 238*c44715dcSPatrick Williams ).encode() 239*c44715dcSPatrick Williams ).hexdigest(), 240*c44715dcSPatrick Williams hashlib.md5((childnode + cid).encode()).hexdigest(), 241*c44715dcSPatrick Williams ) 242b8cc3257SManojkiran Eda unflattentree = dot.unflatten(stagger=(round(counter / 3))) 243*c44715dcSPatrick Williams unflattentree.render( 244*c44715dcSPatrick Williams filename="entity_association_" 245*c44715dcSPatrick Williams + str(datetime.now().strftime("%Y-%m-%d_%H-%M-%S")), 246*c44715dcSPatrick Williams view=False, 247*c44715dcSPatrick Williams cleanup=True, 248*c44715dcSPatrick Williams format="pdf", 249*c44715dcSPatrick Williams ) 250b8cc3257SManojkiran Eda 251b8cc3257SManojkiran Eda 252260f75a6SBrad Bishopclass PLDMToolError(Exception): 253260f75a6SBrad Bishop """Exception class intended to be used to hold pldmtool invocation failure 254260f75a6SBrad Bishop information such as exit status and stderr. 255260f75a6SBrad Bishop 256260f75a6SBrad Bishop """ 257260f75a6SBrad Bishop 258260f75a6SBrad Bishop def __init__(self, status, stderr): 259260f75a6SBrad Bishop msg = "pldmtool failed with exit status {}.\n".format(status) 260260f75a6SBrad Bishop msg += "stderr: \n\n{}".format(stderr) 261260f75a6SBrad Bishop super(PLDMToolError, self).__init__(msg) 26298cb3efbSBrad Bishop self.status = status 26398cb3efbSBrad Bishop 26498cb3efbSBrad Bishop def get_status(self): 26598cb3efbSBrad Bishop return self.status 266260f75a6SBrad Bishop 267260f75a6SBrad Bishop 268241e0683SBrad Bishopdef process_pldmtool_output(process): 269260f75a6SBrad Bishop """Ensure pldmtool runs without error and if it does fail, detect that and 270260f75a6SBrad Bishop show the pldmtool exit status and it's stderr. 271260f75a6SBrad Bishop 27298cb3efbSBrad Bishop A simpler implementation would just wait for the pldmtool exit status 27398cb3efbSBrad Bishop prior to attempting to decode it's stdout. Instead, optimize for the 27498cb3efbSBrad Bishop no error case and allow the json decoder to consume pldmtool stdout as 27598cb3efbSBrad Bishop soon as it is available (in parallel). This results in the following 27698cb3efbSBrad Bishop error scenarios: 27798cb3efbSBrad Bishop - pldmtool fails and the decoder fails 27898cb3efbSBrad Bishop Ignore the decoder fail and throw PLDMToolError. 27998cb3efbSBrad Bishop - pldmtool fails and the decoder doesn't fail 28098cb3efbSBrad Bishop Throw PLDMToolError. 28198cb3efbSBrad Bishop - pldmtool doesn't fail and the decoder does fail 28298cb3efbSBrad Bishop This is a pldmtool bug - re-throw the decoder error. 28398cb3efbSBrad Bishop 284260f75a6SBrad Bishop Parameters: 285241e0683SBrad Bishop process: A Process object providing process control functions like 286241e0683SBrad Bishop wait, and access functions such as reading stdout and 287241e0683SBrad Bishop stderr. 288260f75a6SBrad Bishop 289260f75a6SBrad Bishop """ 290260f75a6SBrad Bishop 29198cb3efbSBrad Bishop status = 0 29298cb3efbSBrad Bishop try: 293241e0683SBrad Bishop data = json.load(process.stdout) 29498cb3efbSBrad Bishop # it's unlikely, but possible, that pldmtool failed but still wrote a 29598cb3efbSBrad Bishop # valid json document - so check for that. 296241e0683SBrad Bishop status = process.wait() 297260f75a6SBrad Bishop if status == 0: 29898cb3efbSBrad Bishop return data 29998cb3efbSBrad Bishop except json.decoder.JSONDecodeError: 30098cb3efbSBrad Bishop # pldmtool wrote an invalid json document. Check to see if it had 30198cb3efbSBrad Bishop # non-zero exit status. 302241e0683SBrad Bishop status = process.wait() 30398cb3efbSBrad Bishop if status == 0: 30498cb3efbSBrad Bishop # pldmtool didn't have non zero exit status, so it wrote an invalid 30598cb3efbSBrad Bishop # json document and the JSONDecodeError is the correct error. 30698cb3efbSBrad Bishop raise 307260f75a6SBrad Bishop 30898cb3efbSBrad Bishop # pldmtool had a non-zero exit status, so throw an error for that, possibly 30998cb3efbSBrad Bishop # discarding a spurious JSONDecodeError exception. 310241e0683SBrad Bishop raise PLDMToolError(status, "".join(process.stderr)) 311260f75a6SBrad Bishop 312260f75a6SBrad Bishop 3137dc416f6SBrad Bishopdef get_pdrs_one_at_a_time(executor): 3147dc416f6SBrad Bishop """Using pldmtool, generate (record handle, PDR) tuples for each record in 3157dc416f6SBrad Bishop the PDR repository. 31691523137SBrad Bishop 31791523137SBrad Bishop Parameters: 3187dc416f6SBrad Bishop executor: executor object for running pldmtool 31991523137SBrad Bishop 32091523137SBrad Bishop """ 32191523137SBrad Bishop 322*c44715dcSPatrick Williams command_fmt = "pldmtool platform getpdr -d {}" 32391523137SBrad Bishop record_handle = 0 32491523137SBrad Bishop while True: 325241e0683SBrad Bishop process = executor.exec_command(command_fmt.format(str(record_handle))) 326241e0683SBrad Bishop pdr = process_pldmtool_output(process) 32791523137SBrad Bishop yield record_handle, pdr 32891523137SBrad Bishop record_handle = pdr["nextRecordHandle"] 32991523137SBrad Bishop if record_handle == 0: 33091523137SBrad Bishop break 33191523137SBrad Bishop 33291523137SBrad Bishop 3337dc416f6SBrad Bishopdef get_all_pdrs_at_once(executor): 3347dc416f6SBrad Bishop """Using pldmtool, generate (record handle, PDR) tuples for each record in 3357dc416f6SBrad Bishop the PDR repository. Use pldmtool platform getpdr --all. 33698cb3efbSBrad Bishop 33798cb3efbSBrad Bishop Parameters: 3387dc416f6SBrad Bishop executor: executor object for running pldmtool 33998cb3efbSBrad Bishop 34098cb3efbSBrad Bishop """ 34198cb3efbSBrad Bishop 342*c44715dcSPatrick Williams process = executor.exec_command("pldmtool platform getpdr -a") 343241e0683SBrad Bishop all_pdrs = process_pldmtool_output(process) 34498cb3efbSBrad Bishop 34598cb3efbSBrad Bishop # Explicitly request record 0 to find out what the real first record is. 346*c44715dcSPatrick Williams process = executor.exec_command("pldmtool platform getpdr -d 0") 347241e0683SBrad Bishop pdr_0 = process_pldmtool_output(process) 34898cb3efbSBrad Bishop record_handle = pdr_0["recordHandle"] 34998cb3efbSBrad Bishop 35098cb3efbSBrad Bishop while True: 35198cb3efbSBrad Bishop for pdr in all_pdrs: 35298cb3efbSBrad Bishop if pdr["recordHandle"] == record_handle: 35398cb3efbSBrad Bishop yield record_handle, pdr 35498cb3efbSBrad Bishop record_handle = pdr["nextRecordHandle"] 35598cb3efbSBrad Bishop if record_handle == 0: 35698cb3efbSBrad Bishop return 35798cb3efbSBrad Bishop raise RuntimeError( 358*c44715dcSPatrick Williams "Dangling reference to record {}".format(record_handle) 359*c44715dcSPatrick Williams ) 36098cb3efbSBrad Bishop 36198cb3efbSBrad Bishop 3627dc416f6SBrad Bishopdef get_pdrs(executor): 3637dc416f6SBrad Bishop """Using pldmtool, generate (record handle, PDR) tuples for each record in 3647dc416f6SBrad Bishop the PDR repository. Use pldmtool platform getpdr --all or fallback on 3657dc416f6SBrad Bishop getting them one at a time if pldmtool doesn't support the --all 3667dc416f6SBrad Bishop option. 36798cb3efbSBrad Bishop 36898cb3efbSBrad Bishop Parameters: 3697dc416f6SBrad Bishop executor: executor object for running pldmtool 37098cb3efbSBrad Bishop 37198cb3efbSBrad Bishop """ 37298cb3efbSBrad Bishop try: 3737dc416f6SBrad Bishop for record_handle, pdr in get_all_pdrs_at_once(executor): 37498cb3efbSBrad Bishop yield record_handle, pdr 37598cb3efbSBrad Bishop return 37698cb3efbSBrad Bishop except PLDMToolError as e: 37798cb3efbSBrad Bishop # No support for the -a option 37898cb3efbSBrad Bishop if e.get_status() != 106: 37998cb3efbSBrad Bishop raise 38098cb3efbSBrad Bishop except json.decoder.JSONDecodeError as e: 38198cb3efbSBrad Bishop # Some versions of pldmtool don't print valid json documents with -a 38298cb3efbSBrad Bishop if e.msg != "Extra data": 38398cb3efbSBrad Bishop raise 38498cb3efbSBrad Bishop 3857dc416f6SBrad Bishop for record_handle, pdr in get_pdrs_one_at_a_time(executor): 38698cb3efbSBrad Bishop yield record_handle, pdr 38798cb3efbSBrad Bishop 38898cb3efbSBrad Bishop 3897dc416f6SBrad Bishopdef fetch_pdrs_from_bmc(executor): 3907dc416f6SBrad Bishop """This is the core function that would fire the getPDR pldmtool command 3917dc416f6SBrad Bishop and it then agreegates the data received from all the calls into the 3927dc416f6SBrad Bishop respective dictionaries based on the PDR Type. 393b8cc3257SManojkiran Eda 394b8cc3257SManojkiran Eda Parameters: 3957dc416f6SBrad Bishop executor: executor object for running pldmtool 396b8cc3257SManojkiran Eda 397b8cc3257SManojkiran Eda """ 398b8cc3257SManojkiran Eda 399b8cc3257SManojkiran Eda entity_association_pdr = {} 400b8cc3257SManojkiran Eda state_sensor_pdr = {} 401b8cc3257SManojkiran Eda state_effecter_pdr = {} 402b8cc3257SManojkiran Eda state_effecter_pdr = {} 403b8cc3257SManojkiran Eda numeric_pdr = {} 404b8cc3257SManojkiran Eda fru_record_set_pdr = {} 405b8cc3257SManojkiran Eda tl_pdr = {} 4067dc416f6SBrad Bishop for handle_number, my_dic in get_pdrs(executor): 40781c04518SBrad Bishop if sys.stdout.isatty(): 40881c04518SBrad Bishop sys.stdout.write( 409*c44715dcSPatrick Williams "Fetching PDR's from BMC : %8d\r" % (handle_number) 410*c44715dcSPatrick Williams ) 411b8cc3257SManojkiran Eda sys.stdout.flush() 412b8cc3257SManojkiran Eda if my_dic["PDRType"] == "Entity Association PDR": 413b8cc3257SManojkiran Eda entity_association_pdr[handle_number] = my_dic 414b8cc3257SManojkiran Eda if my_dic["PDRType"] == "State Sensor PDR": 415b8cc3257SManojkiran Eda state_sensor_pdr[handle_number] = my_dic 416b8cc3257SManojkiran Eda if my_dic["PDRType"] == "State Effecter PDR": 417b8cc3257SManojkiran Eda state_effecter_pdr[handle_number] = my_dic 418b8cc3257SManojkiran Eda if my_dic["PDRType"] == "FRU Record Set PDR": 419b8cc3257SManojkiran Eda fru_record_set_pdr[handle_number] = my_dic 420b8cc3257SManojkiran Eda if my_dic["PDRType"] == "Terminus Locator PDR": 421b8cc3257SManojkiran Eda tl_pdr[handle_number] = my_dic 422b8cc3257SManojkiran Eda if my_dic["PDRType"] == "Numeric Effecter PDR": 423b8cc3257SManojkiran Eda numeric_pdr[handle_number] = my_dic 4247dc416f6SBrad Bishop executor.close() 425b8cc3257SManojkiran Eda 426*c44715dcSPatrick Williams total_pdrs = ( 427*c44715dcSPatrick Williams len(entity_association_pdr.keys()) 428*c44715dcSPatrick Williams + len(tl_pdr.keys()) 429*c44715dcSPatrick Williams + len(state_effecter_pdr.keys()) 430*c44715dcSPatrick Williams + len(numeric_pdr.keys()) 431*c44715dcSPatrick Williams + len(state_sensor_pdr.keys()) 432*c44715dcSPatrick Williams + len(fru_record_set_pdr.keys()) 433*c44715dcSPatrick Williams ) 434*c44715dcSPatrick Williams print("\nSuccessfully fetched " + str(total_pdrs) + " PDR's") 435b8cc3257SManojkiran Eda print("Number of FRU Record PDR's : ", len(fru_record_set_pdr.keys())) 436b8cc3257SManojkiran Eda print("Number of TerminusLocator PDR's : ", len(tl_pdr.keys())) 437b8cc3257SManojkiran Eda print("Number of State Sensor PDR's : ", len(state_sensor_pdr.keys())) 438b8cc3257SManojkiran Eda print("Number of State Effecter PDR's : ", len(state_effecter_pdr.keys())) 439b8cc3257SManojkiran Eda print("Number of Numeric Effecter PDR's : ", len(numeric_pdr.keys())) 440*c44715dcSPatrick Williams print( 441*c44715dcSPatrick Williams "Number of Entity Association PDR's : ", 442*c44715dcSPatrick Williams len(entity_association_pdr.keys()), 443*c44715dcSPatrick Williams ) 444*c44715dcSPatrick Williams return ( 445*c44715dcSPatrick Williams entity_association_pdr, 446*c44715dcSPatrick Williams state_sensor_pdr, 447*c44715dcSPatrick Williams state_effecter_pdr, 448*c44715dcSPatrick Williams len(fru_record_set_pdr.keys()), 449*c44715dcSPatrick Williams ) 450b8cc3257SManojkiran Eda 451b8cc3257SManojkiran Eda 452b8cc3257SManojkiran Edadef main(): 453b8cc3257SManojkiran Eda """Create a summary table capturing the information of all the PDR's 454b8cc3257SManojkiran Eda from the BMC & also create a diagram that captures the entity 455b8cc3257SManojkiran Eda association hierarchy.""" 456b8cc3257SManojkiran Eda 457*c44715dcSPatrick Williams parser = argparse.ArgumentParser(prog="pldm_visualise_pdrs.py") 458*c44715dcSPatrick Williams parser.add_argument("--bmc", type=str, help="BMC IPAddress/BMC Hostname") 459*c44715dcSPatrick Williams parser.add_argument("--user", type=str, help="BMC username") 460*c44715dcSPatrick Williams parser.add_argument("--password", type=str, help="BMC Password") 461*c44715dcSPatrick Williams parser.add_argument("--port", type=int, help="BMC SSH port", default=22) 462b8cc3257SManojkiran Eda args = parser.parse_args() 4639a8192d9SBrad Bishop 4649e0265b5SBrad Bishop extra_cfg = {} 465c8906373SBrad Bishop if args.bmc: 4669a8192d9SBrad Bishop try: 4679a8192d9SBrad Bishop with open(os.path.expanduser("~/.ssh/config")) as f: 4689a8192d9SBrad Bishop ssh_config = paramiko.SSHConfig() 4699a8192d9SBrad Bishop ssh_config.parse(f) 4709a8192d9SBrad Bishop host_config = ssh_config.lookup(args.bmc) 4719a8192d9SBrad Bishop if host_config: 472*c44715dcSPatrick Williams if "hostname" in host_config: 473*c44715dcSPatrick Williams args.bmc = host_config["hostname"] 474*c44715dcSPatrick Williams if "user" in host_config and args.user is None: 475*c44715dcSPatrick Williams args.user = host_config["user"] 476*c44715dcSPatrick Williams if "proxycommand" in host_config: 477*c44715dcSPatrick Williams extra_cfg["sock"] = paramiko.ProxyCommand( 478*c44715dcSPatrick Williams host_config["proxycommand"] 479*c44715dcSPatrick Williams ) 4809a8192d9SBrad Bishop except FileNotFoundError: 4819a8192d9SBrad Bishop pass 4829a8192d9SBrad Bishop 4837dc416f6SBrad Bishop executor = ParamikoExecutor( 484*c44715dcSPatrick Williams args.bmc, args.user, args.password, args.port, **extra_cfg 485*c44715dcSPatrick Williams ) 486*c44715dcSPatrick Williams elif shutil.which("pldmtool"): 487c8906373SBrad Bishop executor = SubprocessExecutor() 488c8906373SBrad Bishop else: 489*c44715dcSPatrick Williams sys.exit( 490*c44715dcSPatrick Williams "Can't find any PDRs: specify remote BMC with --bmc or " 491*c44715dcSPatrick Williams "install pldmtool." 492*c44715dcSPatrick Williams ) 493c8906373SBrad Bishop 494*c44715dcSPatrick Williams ( 495*c44715dcSPatrick Williams association_pdr, 496*c44715dcSPatrick Williams state_sensor_pdr, 497*c44715dcSPatrick Williams state_effecter_pdr, 498*c44715dcSPatrick Williams counter, 499*c44715dcSPatrick Williams ) = fetch_pdrs_from_bmc(executor) 500b8cc3257SManojkiran Eda draw_entity_associations(association_pdr, counter) 501b8cc3257SManojkiran Eda prepare_summary_report(state_sensor_pdr, state_effecter_pdr) 502b8cc3257SManojkiran Eda 503b8cc3257SManojkiran Eda 504b8cc3257SManojkiran Edaif __name__ == "__main__": 505b8cc3257SManojkiran Eda main() 506