| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 1 | #!/usr/bin/env python3 | 
 | 2 |  | 
 | 3 | """Tool to visualize PLDM PDR's""" | 
 | 4 |  | 
 | 5 | import argparse | 
 | 6 | import json | 
 | 7 | import hashlib | 
 | 8 | import sys | 
 | 9 | from datetime import datetime | 
 | 10 | import paramiko | 
 | 11 | from graphviz import Digraph | 
 | 12 | from tabulate import tabulate | 
| Brad Bishop | 9a8192d | 2021-10-04 19:58:11 -0400 | [diff] [blame] | 13 | import os | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 14 |  | 
 | 15 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 16 | class Executor: | 
 | 17 |     """ Interface definition for interacting with executors.  An executor is an | 
 | 18 |         object that can run a program.""" | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 19 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 20 |     def exec_command(self, cmd): | 
 | 21 |         raise NotImplementedError | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 22 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 23 |     def close(self): | 
 | 24 |         pass | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 25 |  | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 26 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 27 | class ParamikoExecutor(Executor): | 
 | 28 |     """ Concrete implementation of the Executor interface that uses | 
 | 29 |         Paramiko to connect to a remote BMC to run the program.""" | 
 | 30 |  | 
 | 31 |     def __init__(self, hostname, uname, passwd, port, **kw): | 
 | 32 |         """ This function is responsible for connecting to the BMC via | 
 | 33 |             ssh and returning an executor object. | 
 | 34 |  | 
 | 35 |             Parameters: | 
 | 36 |                 hostname: hostname/IP address of BMC | 
 | 37 |                 uname: ssh username of BMC | 
 | 38 |                 passwd: ssh password of BMC | 
 | 39 |                 port: ssh port of BMC | 
 | 40 |         """ | 
 | 41 |  | 
 | 42 |         super(ParamikoExecutor, self).__init__() | 
 | 43 |         self.client = paramiko.SSHClient() | 
 | 44 |         self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) | 
 | 45 |         self.client.connect( | 
 | 46 |             hostname, username=uname, password=passwd, port=port, **kw) | 
 | 47 |  | 
 | 48 |     def exec_command(self, cmd): | 
 | 49 |         return self.client.exec_command(cmd) | 
 | 50 |  | 
 | 51 |     def close(self): | 
 | 52 |         self.client.close() | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 53 |  | 
 | 54 |  | 
 | 55 | def prepare_summary_report(state_sensor_pdr, state_effecter_pdr): | 
 | 56 |  | 
 | 57 |     """ This function is responsible to parse the state sensor pdr | 
 | 58 |         and the state effecter pdr dictionaries and creating the | 
 | 59 |         summary table. | 
 | 60 |  | 
 | 61 |         Parameters: | 
 | 62 |             state_sensor_pdr: list of state sensor pdrs | 
 | 63 |             state_effecter_pdr: list of state effecter pdrs | 
 | 64 |  | 
 | 65 |     """ | 
 | 66 |  | 
 | 67 |     summary_table = [] | 
 | 68 |     headers = ["sensor_id", "entity_type", "state_set", "states"] | 
 | 69 |     summary_table.append(headers) | 
 | 70 |     for value in state_sensor_pdr.values(): | 
 | 71 |         summary_record = [] | 
 | 72 |         sensor_possible_states = '' | 
 | 73 |         for sensor_state in value["possibleStates[0]"]: | 
 | 74 |             sensor_possible_states += sensor_state+"\n" | 
 | 75 |         summary_record.extend([value["sensorID"], value["entityType"], | 
 | 76 |                                value["stateSetID[0]"], | 
 | 77 |                                sensor_possible_states]) | 
 | 78 |         summary_table.append(summary_record) | 
 | 79 |     print("Created at : ", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) | 
 | 80 |     print(tabulate(summary_table, tablefmt="fancy_grid", headers="firstrow")) | 
 | 81 |  | 
 | 82 |     summary_table = [] | 
 | 83 |     headers = ["effecter_id", "entity_type", "state_set", "states"] | 
 | 84 |     summary_table.append(headers) | 
 | 85 |     for value in state_effecter_pdr.values(): | 
 | 86 |         summary_record = [] | 
 | 87 |         effecter_possible_states = '' | 
 | 88 |         for state in value["possibleStates[0]"]: | 
 | 89 |             effecter_possible_states += state+"\n" | 
 | 90 |         summary_record.extend([value["effecterID"], value["entityType"], | 
 | 91 |                                value["stateSetID[0]"], | 
 | 92 |                                effecter_possible_states]) | 
 | 93 |         summary_table.append(summary_record) | 
 | 94 |     print(tabulate(summary_table, tablefmt="fancy_grid", headers="firstrow")) | 
 | 95 |  | 
 | 96 |  | 
 | 97 | def draw_entity_associations(pdr, counter): | 
 | 98 |  | 
 | 99 |     """ This function is responsible to create a picture that captures | 
 | 100 |         the entity association hierarchy based on the entity association | 
 | 101 |         PDR's received from the BMC. | 
 | 102 |  | 
 | 103 |         Parameters: | 
 | 104 |             pdr: list of entity association PDR's | 
 | 105 |             counter: variable to capture the count of PDR's to unflatten | 
 | 106 |                      the tree | 
 | 107 |  | 
 | 108 |     """ | 
 | 109 |  | 
 | 110 |     dot = Digraph('entity_hierarchy', node_attr={'color': 'lightblue1', | 
 | 111 |                                                  'style': 'filled'}) | 
 | 112 |     dot.attr(label=r'\n\nEntity Relation Diagram < ' + | 
 | 113 |              str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))+'>\n') | 
 | 114 |     dot.attr(fontsize='20') | 
 | 115 |     edge_list = [] | 
 | 116 |     for value in pdr.values(): | 
 | 117 |         parentnode = str(value["containerEntityType"]) + \ | 
 | 118 |                      str(value["containerEntityInstanceNumber"]) | 
 | 119 |         dot.node(hashlib.md5((parentnode + | 
 | 120 |                               str(value["containerEntityContainerID"])) | 
 | 121 |                              .encode()).hexdigest(), parentnode) | 
 | 122 |  | 
 | 123 |         for i in range(1, value["containedEntityCount"]+1): | 
 | 124 |             childnode = str(value[f"containedEntityType[{i}]"]) + \ | 
 | 125 |                         str(value[f"containedEntityInstanceNumber[{i}]"]) | 
 | 126 |             cid = str(value[f"containedEntityContainerID[{i}]"]) | 
 | 127 |             dot.node(hashlib.md5((childnode + cid) | 
 | 128 |                                  .encode()).hexdigest(), childnode) | 
 | 129 |  | 
 | 130 |             if[hashlib.md5((parentnode + | 
 | 131 |                             str(value["containerEntityContainerID"])) | 
 | 132 |                            .encode()).hexdigest(), | 
 | 133 |                hashlib.md5((childnode + cid) | 
 | 134 |                            .encode()).hexdigest()] not in edge_list: | 
 | 135 |                 edge_list.append([hashlib.md5((parentnode + | 
 | 136 |                                   str(value["containerEntityContainerID"])) | 
 | 137 |                                               .encode()).hexdigest(), | 
 | 138 |                                   hashlib.md5((childnode + cid) | 
 | 139 |                                               .encode()).hexdigest()]) | 
 | 140 |                 dot.edge(hashlib.md5((parentnode + | 
 | 141 |                                       str(value["containerEntityContainerID"])) | 
 | 142 |                                      .encode()).hexdigest(), | 
 | 143 |                          hashlib.md5((childnode + cid).encode()).hexdigest()) | 
 | 144 |     unflattentree = dot.unflatten(stagger=(round(counter/3))) | 
 | 145 |     unflattentree.render(filename='entity_association_' + | 
 | 146 |                          str(datetime.now().strftime("%Y-%m-%d_%H-%M-%S")), | 
 | 147 |                          view=False, cleanup=True, format='pdf') | 
 | 148 |  | 
 | 149 |  | 
| Brad Bishop | 260f75a | 2021-10-15 12:10:29 -0400 | [diff] [blame] | 150 | class PLDMToolError(Exception): | 
 | 151 |     """ Exception class intended to be used to hold pldmtool invocation failure | 
 | 152 |         information such as exit status and stderr. | 
 | 153 |  | 
 | 154 |     """ | 
 | 155 |  | 
 | 156 |     def __init__(self, status, stderr): | 
 | 157 |         msg = "pldmtool failed with exit status {}.\n".format(status) | 
 | 158 |         msg += "stderr: \n\n{}".format(stderr) | 
 | 159 |         super(PLDMToolError, self).__init__(msg) | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 160 |         self.status = status | 
 | 161 |  | 
 | 162 |     def get_status(self): | 
 | 163 |         return self.status | 
| Brad Bishop | 260f75a | 2021-10-15 12:10:29 -0400 | [diff] [blame] | 164 |  | 
 | 165 |  | 
 | 166 | def process_pldmtool_output(stdout_channel, stderr_channel): | 
 | 167 |     """ Ensure pldmtool runs without error and if it does fail, detect that and | 
 | 168 |         show the pldmtool exit status and it's stderr. | 
 | 169 |  | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 170 |         A simpler implementation would just wait for the pldmtool exit status | 
 | 171 |         prior to attempting to decode it's stdout.  Instead, optimize for the | 
 | 172 |         no error case and allow the json decoder to consume pldmtool stdout as | 
 | 173 |         soon as it is available (in parallel).  This results in the following | 
 | 174 |         error scenarios: | 
 | 175 |             - pldmtool fails and the decoder fails | 
 | 176 |               Ignore the decoder fail and throw PLDMToolError. | 
 | 177 |             - pldmtool fails and the decoder doesn't fail | 
 | 178 |               Throw PLDMToolError. | 
 | 179 |             - pldmtool doesn't fail and the decoder does fail | 
 | 180 |               This is a pldmtool bug - re-throw the decoder error. | 
 | 181 |  | 
| Brad Bishop | 260f75a | 2021-10-15 12:10:29 -0400 | [diff] [blame] | 182 |         Parameters: | 
 | 183 |             stdout_channel: file-like stdout channel | 
 | 184 |             stderr_channel: file-like stderr channel | 
 | 185 |  | 
 | 186 |     """ | 
 | 187 |  | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 188 |     status = 0 | 
 | 189 |     try: | 
 | 190 |         data = json.load(stdout_channel) | 
 | 191 |         # it's unlikely, but possible, that pldmtool failed but still wrote a | 
 | 192 |         # valid json document - so check for that. | 
 | 193 |         status = stderr_channel.channel.recv_exit_status() | 
 | 194 |         if status == 0: | 
 | 195 |             return data | 
 | 196 |     except json.decoder.JSONDecodeError: | 
 | 197 |         # pldmtool wrote an invalid json document.  Check to see if it had | 
 | 198 |         # non-zero exit status. | 
 | 199 |         status = stderr_channel.channel.recv_exit_status() | 
 | 200 |         if status == 0: | 
 | 201 |             # pldmtool didn't have non zero exit status, so it wrote an invalid | 
 | 202 |             # json document and the JSONDecodeError is the correct error. | 
 | 203 |             raise | 
| Brad Bishop | 260f75a | 2021-10-15 12:10:29 -0400 | [diff] [blame] | 204 |  | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 205 |     # pldmtool had a non-zero exit status, so throw an error for that, possibly | 
 | 206 |     # discarding a spurious JSONDecodeError exception. | 
| Brad Bishop | 260f75a | 2021-10-15 12:10:29 -0400 | [diff] [blame] | 207 |     raise PLDMToolError(status, "".join(stderr_channel)) | 
 | 208 |  | 
 | 209 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 210 | def get_pdrs_one_at_a_time(executor): | 
 | 211 |     """ Using pldmtool, generate (record handle, PDR) tuples for each record in | 
 | 212 |         the PDR repository. | 
| Brad Bishop | 9152313 | 2021-10-14 19:25:37 -0400 | [diff] [blame] | 213 |  | 
 | 214 |         Parameters: | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 215 |             executor: executor object for running pldmtool | 
| Brad Bishop | 9152313 | 2021-10-14 19:25:37 -0400 | [diff] [blame] | 216 |  | 
 | 217 |     """ | 
 | 218 |  | 
 | 219 |     command_fmt = 'pldmtool platform getpdr -d {}' | 
 | 220 |     record_handle = 0 | 
 | 221 |     while True: | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 222 |         output = executor.exec_command(command_fmt.format(str(record_handle))) | 
| Brad Bishop | 9152313 | 2021-10-14 19:25:37 -0400 | [diff] [blame] | 223 |         _, stdout, stderr = output | 
| Brad Bishop | 260f75a | 2021-10-15 12:10:29 -0400 | [diff] [blame] | 224 |         pdr = process_pldmtool_output(stdout, stderr) | 
| Brad Bishop | 9152313 | 2021-10-14 19:25:37 -0400 | [diff] [blame] | 225 |         yield record_handle, pdr | 
 | 226 |         record_handle = pdr["nextRecordHandle"] | 
 | 227 |         if record_handle == 0: | 
 | 228 |             break | 
 | 229 |  | 
 | 230 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 231 | def get_all_pdrs_at_once(executor): | 
 | 232 |     """ Using pldmtool, generate (record handle, PDR) tuples for each record in | 
 | 233 |         the PDR repository.  Use pldmtool platform getpdr --all. | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 234 |  | 
 | 235 |         Parameters: | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 236 |             executor: executor object for running pldmtool | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 237 |  | 
 | 238 |     """ | 
 | 239 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 240 |     _, stdout, stderr = executor.exec_command('pldmtool platform getpdr -a') | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 241 |     all_pdrs = process_pldmtool_output(stdout, stderr) | 
 | 242 |  | 
 | 243 |     # Explicitly request record 0 to find out what the real first record is. | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 244 |     _, stdout, stderr = executor.exec_command('pldmtool platform getpdr -d 0') | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 245 |     pdr_0 = process_pldmtool_output(stdout, stderr) | 
 | 246 |     record_handle = pdr_0["recordHandle"] | 
 | 247 |  | 
 | 248 |     while True: | 
 | 249 |         for pdr in all_pdrs: | 
 | 250 |             if pdr["recordHandle"] == record_handle: | 
 | 251 |                 yield record_handle, pdr | 
 | 252 |                 record_handle = pdr["nextRecordHandle"] | 
 | 253 |                 if record_handle == 0: | 
 | 254 |                     return | 
 | 255 |         raise RuntimeError( | 
 | 256 |             "Dangling reference to record {}".format(record_handle)) | 
 | 257 |  | 
 | 258 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 259 | def get_pdrs(executor): | 
 | 260 |     """ Using pldmtool, generate (record handle, PDR) tuples for each record in | 
 | 261 |         the PDR repository.  Use pldmtool platform getpdr --all or fallback on | 
 | 262 |         getting them one at a time if pldmtool doesn't support the --all | 
 | 263 |         option. | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 264 |  | 
 | 265 |         Parameters: | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 266 |             executor: executor object for running pldmtool | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 267 |  | 
 | 268 |     """ | 
 | 269 |     try: | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 270 |         for record_handle, pdr in get_all_pdrs_at_once(executor): | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 271 |             yield record_handle, pdr | 
 | 272 |         return | 
 | 273 |     except PLDMToolError as e: | 
 | 274 |         # No support for the -a option | 
 | 275 |         if e.get_status() != 106: | 
 | 276 |             raise | 
 | 277 |     except json.decoder.JSONDecodeError as e: | 
 | 278 |         # Some versions of pldmtool don't print valid json documents with -a | 
 | 279 |         if e.msg != "Extra data": | 
 | 280 |             raise | 
 | 281 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 282 |     for record_handle, pdr in get_pdrs_one_at_a_time(executor): | 
| Brad Bishop | 98cb3ef | 2021-10-15 14:31:59 -0400 | [diff] [blame] | 283 |         yield record_handle, pdr | 
 | 284 |  | 
 | 285 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 286 | def fetch_pdrs_from_bmc(executor): | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 287 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 288 |     """ This is the core function that would fire the getPDR pldmtool command | 
 | 289 |         and it then agreegates the data received from all the calls into the | 
 | 290 |         respective dictionaries based on the PDR Type. | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 291 |  | 
 | 292 |         Parameters: | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 293 |             executor: executor object for running pldmtool | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 294 |  | 
 | 295 |     """ | 
 | 296 |  | 
 | 297 |     entity_association_pdr = {} | 
 | 298 |     state_sensor_pdr = {} | 
 | 299 |     state_effecter_pdr = {} | 
 | 300 |     state_effecter_pdr = {} | 
 | 301 |     numeric_pdr = {} | 
 | 302 |     fru_record_set_pdr = {} | 
 | 303 |     tl_pdr = {} | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 304 |     for handle_number, my_dic in get_pdrs(executor): | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 305 |         sys.stdout.write("Fetching PDR's from BMC : %8d\r" % (handle_number)) | 
 | 306 |         sys.stdout.flush() | 
 | 307 |         if my_dic["PDRType"] == "Entity Association PDR": | 
 | 308 |             entity_association_pdr[handle_number] = my_dic | 
 | 309 |         if my_dic["PDRType"] == "State Sensor PDR": | 
 | 310 |             state_sensor_pdr[handle_number] = my_dic | 
 | 311 |         if my_dic["PDRType"] == "State Effecter PDR": | 
 | 312 |             state_effecter_pdr[handle_number] = my_dic | 
 | 313 |         if my_dic["PDRType"] == "FRU Record Set PDR": | 
 | 314 |             fru_record_set_pdr[handle_number] = my_dic | 
 | 315 |         if my_dic["PDRType"] == "Terminus Locator PDR": | 
 | 316 |             tl_pdr[handle_number] = my_dic | 
 | 317 |         if my_dic["PDRType"] == "Numeric Effecter PDR": | 
 | 318 |             numeric_pdr[handle_number] = my_dic | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 319 |     executor.close() | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 320 |  | 
 | 321 |     total_pdrs = len(entity_association_pdr.keys()) + len(tl_pdr.keys()) + \ | 
 | 322 |         len(state_effecter_pdr.keys()) + len(numeric_pdr.keys()) + \ | 
 | 323 |         len(state_sensor_pdr.keys()) + len(fru_record_set_pdr.keys()) | 
 | 324 |     print("\nSuccessfully fetched " + str(total_pdrs) + " PDR\'s") | 
 | 325 |     print("Number of FRU Record PDR's : ", len(fru_record_set_pdr.keys())) | 
 | 326 |     print("Number of TerminusLocator PDR's : ", len(tl_pdr.keys())) | 
 | 327 |     print("Number of State Sensor PDR's : ", len(state_sensor_pdr.keys())) | 
 | 328 |     print("Number of State Effecter PDR's : ", len(state_effecter_pdr.keys())) | 
 | 329 |     print("Number of Numeric Effecter PDR's : ", len(numeric_pdr.keys())) | 
 | 330 |     print("Number of Entity Association PDR's : ", | 
 | 331 |           len(entity_association_pdr.keys())) | 
 | 332 |     return (entity_association_pdr, state_sensor_pdr, | 
 | 333 |             state_effecter_pdr, len(fru_record_set_pdr.keys())) | 
 | 334 |  | 
 | 335 |  | 
 | 336 | def main(): | 
 | 337 |  | 
 | 338 |     """ Create a summary table capturing the information of all the PDR's | 
 | 339 |         from the BMC & also create a diagram that captures the entity | 
 | 340 |         association hierarchy.""" | 
 | 341 |  | 
 | 342 |     parser = argparse.ArgumentParser(prog='pldm_visualise_pdrs.py') | 
 | 343 |     parser.add_argument('--bmc', type=str, required=True, | 
 | 344 |                         help="BMC IPAddress/BMC Hostname") | 
| Brad Bishop | e0c55c8 | 2021-10-12 17:14:07 -0400 | [diff] [blame] | 345 |     parser.add_argument('--user', type=str, help="BMC username") | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 346 |     parser.add_argument('--password', type=str, required=True, | 
 | 347 |                         help="BMC Password") | 
 | 348 |     parser.add_argument('--port', type=int, help="BMC SSH port", | 
 | 349 |                         default=22) | 
 | 350 |     args = parser.parse_args() | 
| Brad Bishop | 9a8192d | 2021-10-04 19:58:11 -0400 | [diff] [blame] | 351 |  | 
| Brad Bishop | 9e0265b | 2021-10-04 20:14:25 -0400 | [diff] [blame] | 352 |     extra_cfg = {} | 
| Brad Bishop | 9a8192d | 2021-10-04 19:58:11 -0400 | [diff] [blame] | 353 |     try: | 
 | 354 |         with open(os.path.expanduser("~/.ssh/config")) as f: | 
 | 355 |             ssh_config = paramiko.SSHConfig() | 
 | 356 |             ssh_config.parse(f) | 
 | 357 |             host_config = ssh_config.lookup(args.bmc) | 
 | 358 |             if host_config: | 
 | 359 |                 if 'hostname' in host_config: | 
 | 360 |                     args.bmc = host_config['hostname'] | 
 | 361 |                 if 'user' in host_config and args.user is None: | 
 | 362 |                     args.user = host_config['user'] | 
| Brad Bishop | 9e0265b | 2021-10-04 20:14:25 -0400 | [diff] [blame] | 363 |                 if 'proxycommand' in host_config: | 
 | 364 |                     extra_cfg['sock'] = paramiko.ProxyCommand( | 
 | 365 |                         host_config['proxycommand']) | 
| Brad Bishop | 9a8192d | 2021-10-04 19:58:11 -0400 | [diff] [blame] | 366 |     except FileNotFoundError: | 
 | 367 |         pass | 
 | 368 |  | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 369 |     executor = ParamikoExecutor( | 
| Brad Bishop | 9e0265b | 2021-10-04 20:14:25 -0400 | [diff] [blame] | 370 |         args.bmc, args.user, args.password, args.port, **extra_cfg) | 
| Brad Bishop | 70ac60b | 2021-10-04 18:48:43 -0400 | [diff] [blame] | 371 |     association_pdr, state_sensor_pdr, state_effecter_pdr, counter = \ | 
| Brad Bishop | 7dc416f | 2021-10-20 11:45:08 -0400 | [diff] [blame^] | 372 |         fetch_pdrs_from_bmc(executor) | 
| Brad Bishop | 70ac60b | 2021-10-04 18:48:43 -0400 | [diff] [blame] | 373 |     draw_entity_associations(association_pdr, counter) | 
 | 374 |     prepare_summary_report(state_sensor_pdr, state_effecter_pdr) | 
| Manojkiran Eda | b8cc325 | 2021-05-24 11:29:36 +0530 | [diff] [blame] | 375 |  | 
 | 376 |  | 
 | 377 | if __name__ == "__main__": | 
 | 378 |     main() |