blob: 4c901839cc2fbd4aa1c9f7e3e5fa89ec82e56fa9 [file] [log] [blame]
Manojkiran Edab8cc3252021-05-24 11:29:36 +05301#!/usr/bin/env python3
2
3"""Tool to visualize PLDM PDR's"""
4
5import argparse
Manojkiran Edab8cc3252021-05-24 11:29:36 +05306import hashlib
Patrick Williamsc44715d2022-12-08 06:18:18 -06007import json
Brad Bishop9a8192d2021-10-04 19:58:11 -04008import os
Brad Bishopc8906372021-10-19 15:44:38 -04009import shlex
10import shutil
11import subprocess
Patrick Williamsc44715d2022-12-08 06:18:18 -060012import sys
13from datetime import datetime
14
15import paramiko
16from graphviz import Digraph
17from tabulate import tabulate
Manojkiran Edab8cc3252021-05-24 11:29:36 +053018
19
Brad Bishop241e0682021-10-19 15:03:39 -040020class Process:
Patrick Williamsc44715d2022-12-08 06:18:18 -060021 """Interface definition for interacting with a process created by an
22 Executor."""
Brad Bishop241e0682021-10-19 15:03:39 -040023
24 def __init__(self, stdout, stderr):
Patrick Williamsc44715d2022-12-08 06:18:18 -060025 """Construct a Process object. Process object clients can read the
26 process stdout and stderr with os.read(), and can wait for the
27 process to exit.
Brad Bishop241e0682021-10-19 15:03:39 -040028
Patrick Williamsc44715d2022-12-08 06:18:18 -060029 Parameters:
30 stdout: os.read()able stream representing stdout
31 stderr: os.read()able stream representing stderr
Brad Bishop241e0682021-10-19 15:03:39 -040032 """
33
34 self.stdout = stdout
35 self.stderr = stderr
36
37 def wait(self):
Patrick Williamsc44715d2022-12-08 06:18:18 -060038 """Wait for the process to finish, and return its exit status."""
Brad Bishop241e0682021-10-19 15:03:39 -040039
40 raise NotImplementedError
41
42
Brad Bishop7dc416f2021-10-20 11:45:08 -040043class Executor:
Patrick Williamsc44715d2022-12-08 06:18:18 -060044 """Interface definition for interacting with executors. An executor is an
45 object that can run a program."""
Manojkiran Edab8cc3252021-05-24 11:29:36 +053046
Brad Bishop7dc416f2021-10-20 11:45:08 -040047 def exec_command(self, cmd):
48 raise NotImplementedError
Manojkiran Edab8cc3252021-05-24 11:29:36 +053049
Brad Bishop7dc416f2021-10-20 11:45:08 -040050 def close(self):
51 pass
Manojkiran Edab8cc3252021-05-24 11:29:36 +053052
Manojkiran Edab8cc3252021-05-24 11:29:36 +053053
Brad Bishop241e0682021-10-19 15:03:39 -040054class ParamikoProcess(Process):
Patrick Williamsc44715d2022-12-08 06:18:18 -060055 """Concrete implementation of the Process interface that adapts Paramiko
56 interfaces to the Process interface requirements."""
Brad Bishop241e0682021-10-19 15:03:39 -040057
58 def __init__(self, stdout, stderr):
59 super(ParamikoProcess, self).__init__(stdout, stderr)
60
61 def wait(self):
62 return self.stderr.channel.recv_exit_status()
63
64
Brad Bishop7dc416f2021-10-20 11:45:08 -040065class ParamikoExecutor(Executor):
Patrick Williamsc44715d2022-12-08 06:18:18 -060066 """Concrete implementation of the Executor interface that uses
67 Paramiko to connect to a remote BMC to run the program."""
Brad Bishop7dc416f2021-10-20 11:45:08 -040068
69 def __init__(self, hostname, uname, passwd, port, **kw):
Patrick Williamsc44715d2022-12-08 06:18:18 -060070 """This function is responsible for connecting to the BMC via
71 ssh and returning an executor object.
Brad Bishop7dc416f2021-10-20 11:45:08 -040072
Patrick Williamsc44715d2022-12-08 06:18:18 -060073 Parameters:
74 hostname: hostname/IP address of BMC
75 uname: ssh username of BMC
76 passwd: ssh password of BMC
77 port: ssh port of BMC
Brad Bishop7dc416f2021-10-20 11:45:08 -040078 """
79
80 super(ParamikoExecutor, self).__init__()
81 self.client = paramiko.SSHClient()
82 self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
83 self.client.connect(
Patrick Williamsc44715d2022-12-08 06:18:18 -060084 hostname, username=uname, password=passwd, port=port, **kw
85 )
Brad Bishop7dc416f2021-10-20 11:45:08 -040086
87 def exec_command(self, cmd):
Brad Bishop241e0682021-10-19 15:03:39 -040088 _, stdout, stderr = self.client.exec_command(cmd)
89 return ParamikoProcess(stdout, stderr)
Brad Bishop7dc416f2021-10-20 11:45:08 -040090
91 def close(self):
92 self.client.close()
Manojkiran Edab8cc3252021-05-24 11:29:36 +053093
94
Brad Bishopc8906372021-10-19 15:44:38 -040095class SubprocessProcess(Process):
96 def __init__(self, popen):
97 self.popen = popen
98 super(SubprocessProcess, self).__init__(popen.stdout, popen.stderr)
99
100 def wait(self):
101 self.popen.wait()
102 return self.popen.returncode
103
104
105class SubprocessExecutor(Executor):
106 def __init__(self):
107 super(SubprocessExecutor, self).__init__()
108
109 def exec_command(self, cmd):
110 args = shlex.split(cmd)
111 args[0] = shutil.which(args[0])
112 p = subprocess.Popen(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600113 args, stdout=subprocess.PIPE, stderr=subprocess.PIPE
114 )
Brad Bishopc8906372021-10-19 15:44:38 -0400115 return SubprocessProcess(p)
116
117
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530118def prepare_summary_report(state_sensor_pdr, state_effecter_pdr):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600119 """This function is responsible to parse the state sensor pdr
120 and the state effecter pdr dictionaries and creating the
121 summary table.
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530122
Patrick Williamsc44715d2022-12-08 06:18:18 -0600123 Parameters:
124 state_sensor_pdr: list of state sensor pdrs
125 state_effecter_pdr: list of state effecter pdrs
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530126
127 """
128
129 summary_table = []
130 headers = ["sensor_id", "entity_type", "state_set", "states"]
131 summary_table.append(headers)
132 for value in state_sensor_pdr.values():
133 summary_record = []
Patrick Williamsc44715d2022-12-08 06:18:18 -0600134 sensor_possible_states = ""
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530135 for sensor_state in value["possibleStates[0]"]:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600136 sensor_possible_states += sensor_state + "\n"
137 summary_record.extend(
138 [
139 value["sensorID"],
140 value["entityType"],
141 value["stateSetID[0]"],
142 sensor_possible_states,
143 ]
144 )
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530145 summary_table.append(summary_record)
146 print("Created at : ", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
147 print(tabulate(summary_table, tablefmt="fancy_grid", headers="firstrow"))
148
149 summary_table = []
150 headers = ["effecter_id", "entity_type", "state_set", "states"]
151 summary_table.append(headers)
152 for value in state_effecter_pdr.values():
153 summary_record = []
Patrick Williamsc44715d2022-12-08 06:18:18 -0600154 effecter_possible_states = ""
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530155 for state in value["possibleStates[0]"]:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600156 effecter_possible_states += state + "\n"
157 summary_record.extend(
158 [
159 value["effecterID"],
160 value["entityType"],
161 value["stateSetID[0]"],
162 effecter_possible_states,
163 ]
164 )
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530165 summary_table.append(summary_record)
166 print(tabulate(summary_table, tablefmt="fancy_grid", headers="firstrow"))
167
168
169def draw_entity_associations(pdr, counter):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600170 """This function is responsible to create a picture that captures
171 the entity association hierarchy based on the entity association
172 PDR's received from the BMC.
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530173
Patrick Williamsc44715d2022-12-08 06:18:18 -0600174 Parameters:
175 pdr: list of entity association PDR's
176 counter: variable to capture the count of PDR's to unflatten
177 the tree
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530178
179 """
180
Patrick Williamsc44715d2022-12-08 06:18:18 -0600181 dot = Digraph(
182 "entity_hierarchy",
183 node_attr={"color": "lightblue1", "style": "filled"},
184 )
185 dot.attr(
186 label=r"\n\nEntity Relation Diagram < "
187 + str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
188 + ">\n"
189 )
190 dot.attr(fontsize="20")
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530191 edge_list = []
192 for value in pdr.values():
Patrick Williamsc44715d2022-12-08 06:18:18 -0600193 parentnode = str(value["containerEntityType"]) + str(
194 value["containerEntityInstanceNumber"]
195 )
196 dot.node(
197 hashlib.md5(
198 (
199 parentnode + str(value["containerEntityContainerID"])
200 ).encode()
201 ).hexdigest(),
202 parentnode,
203 )
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530204
Patrick Williamsc44715d2022-12-08 06:18:18 -0600205 for i in range(1, value["containedEntityCount"] + 1):
206 childnode = str(value[f"containedEntityType[{i}]"]) + str(
207 value[f"containedEntityInstanceNumber[{i}]"]
208 )
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530209 cid = str(value[f"containedEntityContainerID[{i}]"])
Patrick Williamsc44715d2022-12-08 06:18:18 -0600210 dot.node(
211 hashlib.md5((childnode + cid).encode()).hexdigest(), childnode
212 )
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530213
Patrick Williamsc44715d2022-12-08 06:18:18 -0600214 if [
215 hashlib.md5(
216 (
217 parentnode + str(value["containerEntityContainerID"])
218 ).encode()
219 ).hexdigest(),
220 hashlib.md5((childnode + cid).encode()).hexdigest(),
221 ] not in edge_list:
222 edge_list.append(
223 [
224 hashlib.md5(
225 (
226 parentnode
227 + str(value["containerEntityContainerID"])
228 ).encode()
229 ).hexdigest(),
230 hashlib.md5((childnode + cid).encode()).hexdigest(),
231 ]
232 )
233 dot.edge(
234 hashlib.md5(
235 (
236 parentnode
237 + str(value["containerEntityContainerID"])
238 ).encode()
239 ).hexdigest(),
240 hashlib.md5((childnode + cid).encode()).hexdigest(),
241 )
242 unflattentree = dot.unflatten(stagger=(round(counter / 3)))
243 unflattentree.render(
244 filename="entity_association_"
245 + str(datetime.now().strftime("%Y-%m-%d_%H-%M-%S")),
246 view=False,
247 cleanup=True,
248 format="pdf",
249 )
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530250
251
Brad Bishop260f75a2021-10-15 12:10:29 -0400252class PLDMToolError(Exception):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600253 """Exception class intended to be used to hold pldmtool invocation failure
254 information such as exit status and stderr.
Brad Bishop260f75a2021-10-15 12:10:29 -0400255
256 """
257
258 def __init__(self, status, stderr):
259 msg = "pldmtool failed with exit status {}.\n".format(status)
260 msg += "stderr: \n\n{}".format(stderr)
261 super(PLDMToolError, self).__init__(msg)
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400262 self.status = status
263
264 def get_status(self):
265 return self.status
Brad Bishop260f75a2021-10-15 12:10:29 -0400266
267
Brad Bishop241e0682021-10-19 15:03:39 -0400268def process_pldmtool_output(process):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600269 """Ensure pldmtool runs without error and if it does fail, detect that and
270 show the pldmtool exit status and it's stderr.
Brad Bishop260f75a2021-10-15 12:10:29 -0400271
Patrick Williamsc44715d2022-12-08 06:18:18 -0600272 A simpler implementation would just wait for the pldmtool exit status
273 prior to attempting to decode it's stdout. Instead, optimize for the
274 no error case and allow the json decoder to consume pldmtool stdout as
275 soon as it is available (in parallel). This results in the following
276 error scenarios:
277 - pldmtool fails and the decoder fails
278 Ignore the decoder fail and throw PLDMToolError.
279 - pldmtool fails and the decoder doesn't fail
280 Throw PLDMToolError.
281 - pldmtool doesn't fail and the decoder does fail
282 This is a pldmtool bug - re-throw the decoder error.
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400283
Patrick Williamsc44715d2022-12-08 06:18:18 -0600284 Parameters:
285 process: A Process object providing process control functions like
286 wait, and access functions such as reading stdout and
287 stderr.
Brad Bishop260f75a2021-10-15 12:10:29 -0400288
289 """
290
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400291 status = 0
292 try:
Brad Bishop241e0682021-10-19 15:03:39 -0400293 data = json.load(process.stdout)
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400294 # it's unlikely, but possible, that pldmtool failed but still wrote a
295 # valid json document - so check for that.
Brad Bishop241e0682021-10-19 15:03:39 -0400296 status = process.wait()
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400297 if status == 0:
298 return data
299 except json.decoder.JSONDecodeError:
300 # pldmtool wrote an invalid json document. Check to see if it had
301 # non-zero exit status.
Brad Bishop241e0682021-10-19 15:03:39 -0400302 status = process.wait()
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400303 if status == 0:
304 # pldmtool didn't have non zero exit status, so it wrote an invalid
305 # json document and the JSONDecodeError is the correct error.
306 raise
Brad Bishop260f75a2021-10-15 12:10:29 -0400307
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400308 # pldmtool had a non-zero exit status, so throw an error for that, possibly
309 # discarding a spurious JSONDecodeError exception.
Brad Bishop241e0682021-10-19 15:03:39 -0400310 raise PLDMToolError(status, "".join(process.stderr))
Brad Bishop260f75a2021-10-15 12:10:29 -0400311
312
Brad Bishop7dc416f2021-10-20 11:45:08 -0400313def get_pdrs_one_at_a_time(executor):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600314 """Using pldmtool, generate (record handle, PDR) tuples for each record in
315 the PDR repository.
Brad Bishop91523132021-10-14 19:25:37 -0400316
Patrick Williamsc44715d2022-12-08 06:18:18 -0600317 Parameters:
318 executor: executor object for running pldmtool
Brad Bishop91523132021-10-14 19:25:37 -0400319
320 """
321
Patrick Williamsc44715d2022-12-08 06:18:18 -0600322 command_fmt = "pldmtool platform getpdr -d {}"
Brad Bishop91523132021-10-14 19:25:37 -0400323 record_handle = 0
324 while True:
Brad Bishop241e0682021-10-19 15:03:39 -0400325 process = executor.exec_command(command_fmt.format(str(record_handle)))
326 pdr = process_pldmtool_output(process)
Brad Bishop91523132021-10-14 19:25:37 -0400327 yield record_handle, pdr
328 record_handle = pdr["nextRecordHandle"]
329 if record_handle == 0:
330 break
331
332
Brad Bishop7dc416f2021-10-20 11:45:08 -0400333def get_all_pdrs_at_once(executor):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600334 """Using pldmtool, generate (record handle, PDR) tuples for each record in
335 the PDR repository. Use pldmtool platform getpdr --all.
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400336
Patrick Williamsc44715d2022-12-08 06:18:18 -0600337 Parameters:
338 executor: executor object for running pldmtool
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400339
340 """
341
Patrick Williamsc44715d2022-12-08 06:18:18 -0600342 process = executor.exec_command("pldmtool platform getpdr -a")
Brad Bishop241e0682021-10-19 15:03:39 -0400343 all_pdrs = process_pldmtool_output(process)
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400344
345 # Explicitly request record 0 to find out what the real first record is.
Patrick Williamsc44715d2022-12-08 06:18:18 -0600346 process = executor.exec_command("pldmtool platform getpdr -d 0")
Brad Bishop241e0682021-10-19 15:03:39 -0400347 pdr_0 = process_pldmtool_output(process)
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400348 record_handle = pdr_0["recordHandle"]
349
350 while True:
351 for pdr in all_pdrs:
352 if pdr["recordHandle"] == record_handle:
353 yield record_handle, pdr
354 record_handle = pdr["nextRecordHandle"]
355 if record_handle == 0:
356 return
357 raise RuntimeError(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600358 "Dangling reference to record {}".format(record_handle)
359 )
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400360
361
Brad Bishop7dc416f2021-10-20 11:45:08 -0400362def get_pdrs(executor):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600363 """Using pldmtool, generate (record handle, PDR) tuples for each record in
364 the PDR repository. Use pldmtool platform getpdr --all or fallback on
365 getting them one at a time if pldmtool doesn't support the --all
366 option.
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400367
Patrick Williamsc44715d2022-12-08 06:18:18 -0600368 Parameters:
369 executor: executor object for running pldmtool
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400370
371 """
372 try:
Brad Bishop7dc416f2021-10-20 11:45:08 -0400373 for record_handle, pdr in get_all_pdrs_at_once(executor):
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400374 yield record_handle, pdr
375 return
376 except PLDMToolError as e:
377 # No support for the -a option
378 if e.get_status() != 106:
379 raise
380 except json.decoder.JSONDecodeError as e:
381 # Some versions of pldmtool don't print valid json documents with -a
382 if e.msg != "Extra data":
383 raise
384
Brad Bishop7dc416f2021-10-20 11:45:08 -0400385 for record_handle, pdr in get_pdrs_one_at_a_time(executor):
Brad Bishop98cb3ef2021-10-15 14:31:59 -0400386 yield record_handle, pdr
387
388
Brad Bishop7dc416f2021-10-20 11:45:08 -0400389def fetch_pdrs_from_bmc(executor):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600390 """This is the core function that would fire the getPDR pldmtool command
391 and it then agreegates the data received from all the calls into the
392 respective dictionaries based on the PDR Type.
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530393
Patrick Williamsc44715d2022-12-08 06:18:18 -0600394 Parameters:
395 executor: executor object for running pldmtool
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530396
397 """
398
399 entity_association_pdr = {}
400 state_sensor_pdr = {}
401 state_effecter_pdr = {}
402 state_effecter_pdr = {}
403 numeric_pdr = {}
404 fru_record_set_pdr = {}
405 tl_pdr = {}
Brad Bishop7dc416f2021-10-20 11:45:08 -0400406 for handle_number, my_dic in get_pdrs(executor):
Brad Bishop81c04512021-10-20 12:43:55 -0400407 if sys.stdout.isatty():
408 sys.stdout.write(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600409 "Fetching PDR's from BMC : %8d\r" % (handle_number)
410 )
Brad Bishop81c04512021-10-20 12:43:55 -0400411 sys.stdout.flush()
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530412 if my_dic["PDRType"] == "Entity Association PDR":
413 entity_association_pdr[handle_number] = my_dic
414 if my_dic["PDRType"] == "State Sensor PDR":
415 state_sensor_pdr[handle_number] = my_dic
416 if my_dic["PDRType"] == "State Effecter PDR":
417 state_effecter_pdr[handle_number] = my_dic
418 if my_dic["PDRType"] == "FRU Record Set PDR":
419 fru_record_set_pdr[handle_number] = my_dic
420 if my_dic["PDRType"] == "Terminus Locator PDR":
421 tl_pdr[handle_number] = my_dic
422 if my_dic["PDRType"] == "Numeric Effecter PDR":
423 numeric_pdr[handle_number] = my_dic
Brad Bishop7dc416f2021-10-20 11:45:08 -0400424 executor.close()
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530425
Patrick Williamsc44715d2022-12-08 06:18:18 -0600426 total_pdrs = (
427 len(entity_association_pdr.keys())
428 + len(tl_pdr.keys())
429 + len(state_effecter_pdr.keys())
430 + len(numeric_pdr.keys())
431 + len(state_sensor_pdr.keys())
432 + len(fru_record_set_pdr.keys())
433 )
434 print("\nSuccessfully fetched " + str(total_pdrs) + " PDR's")
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530435 print("Number of FRU Record PDR's : ", len(fru_record_set_pdr.keys()))
436 print("Number of TerminusLocator PDR's : ", len(tl_pdr.keys()))
437 print("Number of State Sensor PDR's : ", len(state_sensor_pdr.keys()))
438 print("Number of State Effecter PDR's : ", len(state_effecter_pdr.keys()))
439 print("Number of Numeric Effecter PDR's : ", len(numeric_pdr.keys()))
Patrick Williamsc44715d2022-12-08 06:18:18 -0600440 print(
441 "Number of Entity Association PDR's : ",
442 len(entity_association_pdr.keys()),
443 )
444 return (
445 entity_association_pdr,
446 state_sensor_pdr,
447 state_effecter_pdr,
448 len(fru_record_set_pdr.keys()),
449 )
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530450
451
452def main():
Patrick Williamsc44715d2022-12-08 06:18:18 -0600453 """Create a summary table capturing the information of all the PDR's
454 from the BMC & also create a diagram that captures the entity
455 association hierarchy."""
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530456
Patrick Williamsc44715d2022-12-08 06:18:18 -0600457 parser = argparse.ArgumentParser(prog="pldm_visualise_pdrs.py")
458 parser.add_argument("--bmc", type=str, help="BMC IPAddress/BMC Hostname")
459 parser.add_argument("--user", type=str, help="BMC username")
460 parser.add_argument("--password", type=str, help="BMC Password")
461 parser.add_argument("--port", type=int, help="BMC SSH port", default=22)
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530462 args = parser.parse_args()
Brad Bishop9a8192d2021-10-04 19:58:11 -0400463
Brad Bishop9e0265b2021-10-04 20:14:25 -0400464 extra_cfg = {}
Brad Bishopc8906372021-10-19 15:44:38 -0400465 if args.bmc:
466 try:
467 with open(os.path.expanduser("~/.ssh/config")) as f:
468 ssh_config = paramiko.SSHConfig()
469 ssh_config.parse(f)
470 host_config = ssh_config.lookup(args.bmc)
471 if host_config:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600472 if "hostname" in host_config:
473 args.bmc = host_config["hostname"]
474 if "user" in host_config and args.user is None:
475 args.user = host_config["user"]
476 if "proxycommand" in host_config:
477 extra_cfg["sock"] = paramiko.ProxyCommand(
478 host_config["proxycommand"]
479 )
Brad Bishopc8906372021-10-19 15:44:38 -0400480 except FileNotFoundError:
481 pass
Brad Bishop9a8192d2021-10-04 19:58:11 -0400482
Brad Bishopc8906372021-10-19 15:44:38 -0400483 executor = ParamikoExecutor(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600484 args.bmc, args.user, args.password, args.port, **extra_cfg
485 )
486 elif shutil.which("pldmtool"):
Brad Bishopc8906372021-10-19 15:44:38 -0400487 executor = SubprocessExecutor()
488 else:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600489 sys.exit(
490 "Can't find any PDRs: specify remote BMC with --bmc or "
491 "install pldmtool."
492 )
Brad Bishopc8906372021-10-19 15:44:38 -0400493
Patrick Williamsc44715d2022-12-08 06:18:18 -0600494 (
495 association_pdr,
496 state_sensor_pdr,
497 state_effecter_pdr,
498 counter,
499 ) = fetch_pdrs_from_bmc(executor)
Brad Bishop70ac60b2021-10-04 18:48:43 -0400500 draw_entity_associations(association_pdr, counter)
501 prepare_summary_report(state_sensor_pdr, state_effecter_pdr)
Manojkiran Edab8cc3252021-05-24 11:29:36 +0530502
503
504if __name__ == "__main__":
505 main()