xref: /openbmc/pldm/tools/visualize-pdr/pldm_visualise_pdrs.py (revision 9e0265b5273438f68ca30281f60e99831739fd73)
1b8cc3257SManojkiran Eda#!/usr/bin/env python3
2b8cc3257SManojkiran Eda
3b8cc3257SManojkiran Eda"""Tool to visualize PLDM PDR's"""
4b8cc3257SManojkiran Eda
5b8cc3257SManojkiran Edaimport argparse
6b8cc3257SManojkiran Edaimport json
7b8cc3257SManojkiran Edaimport hashlib
8b8cc3257SManojkiran Edaimport sys
9b8cc3257SManojkiran Edafrom datetime import datetime
10b8cc3257SManojkiran Edaimport paramiko
11b8cc3257SManojkiran Edafrom graphviz import Digraph
12b8cc3257SManojkiran Edafrom tabulate import tabulate
139a8192d9SBrad Bishopimport os
14b8cc3257SManojkiran Eda
15b8cc3257SManojkiran Eda
16*9e0265b5SBrad Bishopdef connect_to_bmc(hostname, uname, passwd, port, **kw):
17b8cc3257SManojkiran Eda
18b8cc3257SManojkiran Eda    """ This function is responsible to connect to the BMC via
19b8cc3257SManojkiran Eda        ssh and returns a client object.
20b8cc3257SManojkiran Eda
21b8cc3257SManojkiran Eda        Parameters:
22b8cc3257SManojkiran Eda            hostname: hostname/IP address of BMC
23b8cc3257SManojkiran Eda            uname: ssh username of BMC
24b8cc3257SManojkiran Eda            passwd: ssh password of BMC
25b8cc3257SManojkiran Eda            port: ssh port of BMC
26b8cc3257SManojkiran Eda
27b8cc3257SManojkiran Eda    """
28b8cc3257SManojkiran Eda
29b8cc3257SManojkiran Eda    client = paramiko.SSHClient()
30b8cc3257SManojkiran Eda    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
31*9e0265b5SBrad Bishop    client.connect(hostname, username=uname, password=passwd, port=port, **kw)
32b8cc3257SManojkiran Eda    return client
33b8cc3257SManojkiran Eda
34b8cc3257SManojkiran Eda
35b8cc3257SManojkiran Edadef prepare_summary_report(state_sensor_pdr, state_effecter_pdr):
36b8cc3257SManojkiran Eda
37b8cc3257SManojkiran Eda    """ This function is responsible to parse the state sensor pdr
38b8cc3257SManojkiran Eda        and the state effecter pdr dictionaries and creating the
39b8cc3257SManojkiran Eda        summary table.
40b8cc3257SManojkiran Eda
41b8cc3257SManojkiran Eda        Parameters:
42b8cc3257SManojkiran Eda            state_sensor_pdr: list of state sensor pdrs
43b8cc3257SManojkiran Eda            state_effecter_pdr: list of state effecter pdrs
44b8cc3257SManojkiran Eda
45b8cc3257SManojkiran Eda    """
46b8cc3257SManojkiran Eda
47b8cc3257SManojkiran Eda    summary_table = []
48b8cc3257SManojkiran Eda    headers = ["sensor_id", "entity_type", "state_set", "states"]
49b8cc3257SManojkiran Eda    summary_table.append(headers)
50b8cc3257SManojkiran Eda    for value in state_sensor_pdr.values():
51b8cc3257SManojkiran Eda        summary_record = []
52b8cc3257SManojkiran Eda        sensor_possible_states = ''
53b8cc3257SManojkiran Eda        for sensor_state in value["possibleStates[0]"]:
54b8cc3257SManojkiran Eda            sensor_possible_states += sensor_state+"\n"
55b8cc3257SManojkiran Eda        summary_record.extend([value["sensorID"], value["entityType"],
56b8cc3257SManojkiran Eda                               value["stateSetID[0]"],
57b8cc3257SManojkiran Eda                               sensor_possible_states])
58b8cc3257SManojkiran Eda        summary_table.append(summary_record)
59b8cc3257SManojkiran Eda    print("Created at : ", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
60b8cc3257SManojkiran Eda    print(tabulate(summary_table, tablefmt="fancy_grid", headers="firstrow"))
61b8cc3257SManojkiran Eda
62b8cc3257SManojkiran Eda    summary_table = []
63b8cc3257SManojkiran Eda    headers = ["effecter_id", "entity_type", "state_set", "states"]
64b8cc3257SManojkiran Eda    summary_table.append(headers)
65b8cc3257SManojkiran Eda    for value in state_effecter_pdr.values():
66b8cc3257SManojkiran Eda        summary_record = []
67b8cc3257SManojkiran Eda        effecter_possible_states = ''
68b8cc3257SManojkiran Eda        for state in value["possibleStates[0]"]:
69b8cc3257SManojkiran Eda            effecter_possible_states += state+"\n"
70b8cc3257SManojkiran Eda        summary_record.extend([value["effecterID"], value["entityType"],
71b8cc3257SManojkiran Eda                               value["stateSetID[0]"],
72b8cc3257SManojkiran Eda                               effecter_possible_states])
73b8cc3257SManojkiran Eda        summary_table.append(summary_record)
74b8cc3257SManojkiran Eda    print(tabulate(summary_table, tablefmt="fancy_grid", headers="firstrow"))
75b8cc3257SManojkiran Eda
76b8cc3257SManojkiran Eda
77b8cc3257SManojkiran Edadef draw_entity_associations(pdr, counter):
78b8cc3257SManojkiran Eda
79b8cc3257SManojkiran Eda    """ This function is responsible to create a picture that captures
80b8cc3257SManojkiran Eda        the entity association hierarchy based on the entity association
81b8cc3257SManojkiran Eda        PDR's received from the BMC.
82b8cc3257SManojkiran Eda
83b8cc3257SManojkiran Eda        Parameters:
84b8cc3257SManojkiran Eda            pdr: list of entity association PDR's
85b8cc3257SManojkiran Eda            counter: variable to capture the count of PDR's to unflatten
86b8cc3257SManojkiran Eda                     the tree
87b8cc3257SManojkiran Eda
88b8cc3257SManojkiran Eda    """
89b8cc3257SManojkiran Eda
90b8cc3257SManojkiran Eda    dot = Digraph('entity_hierarchy', node_attr={'color': 'lightblue1',
91b8cc3257SManojkiran Eda                                                 'style': 'filled'})
92b8cc3257SManojkiran Eda    dot.attr(label=r'\n\nEntity Relation Diagram < ' +
93b8cc3257SManojkiran Eda             str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))+'>\n')
94b8cc3257SManojkiran Eda    dot.attr(fontsize='20')
95b8cc3257SManojkiran Eda    edge_list = []
96b8cc3257SManojkiran Eda    for value in pdr.values():
97b8cc3257SManojkiran Eda        parentnode = str(value["containerEntityType"]) + \
98b8cc3257SManojkiran Eda                     str(value["containerEntityInstanceNumber"])
99b8cc3257SManojkiran Eda        dot.node(hashlib.md5((parentnode +
100b8cc3257SManojkiran Eda                              str(value["containerEntityContainerID"]))
101b8cc3257SManojkiran Eda                             .encode()).hexdigest(), parentnode)
102b8cc3257SManojkiran Eda
103b8cc3257SManojkiran Eda        for i in range(1, value["containedEntityCount"]+1):
104b8cc3257SManojkiran Eda            childnode = str(value[f"containedEntityType[{i}]"]) + \
105b8cc3257SManojkiran Eda                        str(value[f"containedEntityInstanceNumber[{i}]"])
106b8cc3257SManojkiran Eda            cid = str(value[f"containedEntityContainerID[{i}]"])
107b8cc3257SManojkiran Eda            dot.node(hashlib.md5((childnode + cid)
108b8cc3257SManojkiran Eda                                 .encode()).hexdigest(), childnode)
109b8cc3257SManojkiran Eda
110b8cc3257SManojkiran Eda            if[hashlib.md5((parentnode +
111b8cc3257SManojkiran Eda                            str(value["containerEntityContainerID"]))
112b8cc3257SManojkiran Eda                           .encode()).hexdigest(),
113b8cc3257SManojkiran Eda               hashlib.md5((childnode + cid)
114b8cc3257SManojkiran Eda                           .encode()).hexdigest()] not in edge_list:
115b8cc3257SManojkiran Eda                edge_list.append([hashlib.md5((parentnode +
116b8cc3257SManojkiran Eda                                  str(value["containerEntityContainerID"]))
117b8cc3257SManojkiran Eda                                              .encode()).hexdigest(),
118b8cc3257SManojkiran Eda                                  hashlib.md5((childnode + cid)
119b8cc3257SManojkiran Eda                                              .encode()).hexdigest()])
120b8cc3257SManojkiran Eda                dot.edge(hashlib.md5((parentnode +
121b8cc3257SManojkiran Eda                                      str(value["containerEntityContainerID"]))
122b8cc3257SManojkiran Eda                                     .encode()).hexdigest(),
123b8cc3257SManojkiran Eda                         hashlib.md5((childnode + cid).encode()).hexdigest())
124b8cc3257SManojkiran Eda    unflattentree = dot.unflatten(stagger=(round(counter/3)))
125b8cc3257SManojkiran Eda    unflattentree.render(filename='entity_association_' +
126b8cc3257SManojkiran Eda                         str(datetime.now().strftime("%Y-%m-%d_%H-%M-%S")),
127b8cc3257SManojkiran Eda                         view=False, cleanup=True, format='pdf')
128b8cc3257SManojkiran Eda
129b8cc3257SManojkiran Eda
130260f75a6SBrad Bishopclass PLDMToolError(Exception):
131260f75a6SBrad Bishop    """ Exception class intended to be used to hold pldmtool invocation failure
132260f75a6SBrad Bishop        information such as exit status and stderr.
133260f75a6SBrad Bishop
134260f75a6SBrad Bishop    """
135260f75a6SBrad Bishop
136260f75a6SBrad Bishop    def __init__(self, status, stderr):
137260f75a6SBrad Bishop        msg = "pldmtool failed with exit status {}.\n".format(status)
138260f75a6SBrad Bishop        msg += "stderr: \n\n{}".format(stderr)
139260f75a6SBrad Bishop        super(PLDMToolError, self).__init__(msg)
14098cb3efbSBrad Bishop        self.status = status
14198cb3efbSBrad Bishop
14298cb3efbSBrad Bishop    def get_status(self):
14398cb3efbSBrad Bishop        return self.status
144260f75a6SBrad Bishop
145260f75a6SBrad Bishop
146260f75a6SBrad Bishopdef process_pldmtool_output(stdout_channel, stderr_channel):
147260f75a6SBrad Bishop    """ Ensure pldmtool runs without error and if it does fail, detect that and
148260f75a6SBrad Bishop        show the pldmtool exit status and it's stderr.
149260f75a6SBrad Bishop
15098cb3efbSBrad Bishop        A simpler implementation would just wait for the pldmtool exit status
15198cb3efbSBrad Bishop        prior to attempting to decode it's stdout.  Instead, optimize for the
15298cb3efbSBrad Bishop        no error case and allow the json decoder to consume pldmtool stdout as
15398cb3efbSBrad Bishop        soon as it is available (in parallel).  This results in the following
15498cb3efbSBrad Bishop        error scenarios:
15598cb3efbSBrad Bishop            - pldmtool fails and the decoder fails
15698cb3efbSBrad Bishop              Ignore the decoder fail and throw PLDMToolError.
15798cb3efbSBrad Bishop            - pldmtool fails and the decoder doesn't fail
15898cb3efbSBrad Bishop              Throw PLDMToolError.
15998cb3efbSBrad Bishop            - pldmtool doesn't fail and the decoder does fail
16098cb3efbSBrad Bishop              This is a pldmtool bug - re-throw the decoder error.
16198cb3efbSBrad Bishop
162260f75a6SBrad Bishop        Parameters:
163260f75a6SBrad Bishop            stdout_channel: file-like stdout channel
164260f75a6SBrad Bishop            stderr_channel: file-like stderr channel
165260f75a6SBrad Bishop
166260f75a6SBrad Bishop    """
167260f75a6SBrad Bishop
16898cb3efbSBrad Bishop    status = 0
16998cb3efbSBrad Bishop    try:
17098cb3efbSBrad Bishop        data = json.load(stdout_channel)
17198cb3efbSBrad Bishop        # it's unlikely, but possible, that pldmtool failed but still wrote a
17298cb3efbSBrad Bishop        # valid json document - so check for that.
173260f75a6SBrad Bishop        status = stderr_channel.channel.recv_exit_status()
174260f75a6SBrad Bishop        if status == 0:
17598cb3efbSBrad Bishop            return data
17698cb3efbSBrad Bishop    except json.decoder.JSONDecodeError:
17798cb3efbSBrad Bishop        # pldmtool wrote an invalid json document.  Check to see if it had
17898cb3efbSBrad Bishop        # non-zero exit status.
17998cb3efbSBrad Bishop        status = stderr_channel.channel.recv_exit_status()
18098cb3efbSBrad Bishop        if status == 0:
18198cb3efbSBrad Bishop            # pldmtool didn't have non zero exit status, so it wrote an invalid
18298cb3efbSBrad Bishop            # json document and the JSONDecodeError is the correct error.
18398cb3efbSBrad Bishop            raise
184260f75a6SBrad Bishop
18598cb3efbSBrad Bishop    # pldmtool had a non-zero exit status, so throw an error for that, possibly
18698cb3efbSBrad Bishop    # discarding a spurious JSONDecodeError exception.
187260f75a6SBrad Bishop    raise PLDMToolError(status, "".join(stderr_channel))
188260f75a6SBrad Bishop
189260f75a6SBrad Bishop
19098cb3efbSBrad Bishopdef get_pdrs_one_at_a_time(client):
19191523137SBrad Bishop    """ Using pldmtool over SSH, generate (record handle, PDR) tuples for each
19291523137SBrad Bishop        record in the PDR repository.
19391523137SBrad Bishop
19491523137SBrad Bishop        Parameters:
19591523137SBrad Bishop            client: paramiko ssh client object
19691523137SBrad Bishop
19791523137SBrad Bishop    """
19891523137SBrad Bishop
19991523137SBrad Bishop    command_fmt = 'pldmtool platform getpdr -d {}'
20091523137SBrad Bishop    record_handle = 0
20191523137SBrad Bishop    while True:
20291523137SBrad Bishop        output = client.exec_command(command_fmt.format(str(record_handle)))
20391523137SBrad Bishop        _, stdout, stderr = output
204260f75a6SBrad Bishop        pdr = process_pldmtool_output(stdout, stderr)
20591523137SBrad Bishop        yield record_handle, pdr
20691523137SBrad Bishop        record_handle = pdr["nextRecordHandle"]
20791523137SBrad Bishop        if record_handle == 0:
20891523137SBrad Bishop            break
20991523137SBrad Bishop
21091523137SBrad Bishop
21198cb3efbSBrad Bishopdef get_all_pdrs_at_once(client):
21298cb3efbSBrad Bishop    """ Using pldmtool over SSH, generate (record handle, PDR) tuples for each
21398cb3efbSBrad Bishop        record in the PDR repository.  Use pldmtool platform getpdr --all.
21498cb3efbSBrad Bishop
21598cb3efbSBrad Bishop        Parameters:
21698cb3efbSBrad Bishop            client: paramiko ssh client object
21798cb3efbSBrad Bishop
21898cb3efbSBrad Bishop    """
21998cb3efbSBrad Bishop
22098cb3efbSBrad Bishop    _, stdout, stderr = client.exec_command('pldmtool platform getpdr -a')
22198cb3efbSBrad Bishop    all_pdrs = process_pldmtool_output(stdout, stderr)
22298cb3efbSBrad Bishop
22398cb3efbSBrad Bishop    # Explicitly request record 0 to find out what the real first record is.
22498cb3efbSBrad Bishop    _, stdout, stderr = client.exec_command('pldmtool platform getpdr -d 0')
22598cb3efbSBrad Bishop    pdr_0 = process_pldmtool_output(stdout, stderr)
22698cb3efbSBrad Bishop    record_handle = pdr_0["recordHandle"]
22798cb3efbSBrad Bishop
22898cb3efbSBrad Bishop    while True:
22998cb3efbSBrad Bishop        for pdr in all_pdrs:
23098cb3efbSBrad Bishop            if pdr["recordHandle"] == record_handle:
23198cb3efbSBrad Bishop                yield record_handle, pdr
23298cb3efbSBrad Bishop                record_handle = pdr["nextRecordHandle"]
23398cb3efbSBrad Bishop                if record_handle == 0:
23498cb3efbSBrad Bishop                    return
23598cb3efbSBrad Bishop        raise RuntimeError(
23698cb3efbSBrad Bishop            "Dangling reference to record {}".format(record_handle))
23798cb3efbSBrad Bishop
23898cb3efbSBrad Bishop
23998cb3efbSBrad Bishopdef get_pdrs(client):
24098cb3efbSBrad Bishop    """ Using pldmtool over SSH, generate (record handle, PDR) tuples for each
24198cb3efbSBrad Bishop        record in the PDR repository.  Use pldmtool platform getpdr --all or
24298cb3efbSBrad Bishop        fallback on getting them one at a time if pldmtool doesn't support the
24398cb3efbSBrad Bishop        --all option.
24498cb3efbSBrad Bishop
24598cb3efbSBrad Bishop        Parameters:
24698cb3efbSBrad Bishop            client: paramiko ssh client object
24798cb3efbSBrad Bishop
24898cb3efbSBrad Bishop    """
24998cb3efbSBrad Bishop    try:
25098cb3efbSBrad Bishop        for record_handle, pdr in get_all_pdrs_at_once(client):
25198cb3efbSBrad Bishop            yield record_handle, pdr
25298cb3efbSBrad Bishop        return
25398cb3efbSBrad Bishop    except PLDMToolError as e:
25498cb3efbSBrad Bishop        # No support for the -a option
25598cb3efbSBrad Bishop        if e.get_status() != 106:
25698cb3efbSBrad Bishop            raise
25798cb3efbSBrad Bishop    except json.decoder.JSONDecodeError as e:
25898cb3efbSBrad Bishop        # Some versions of pldmtool don't print valid json documents with -a
25998cb3efbSBrad Bishop        if e.msg != "Extra data":
26098cb3efbSBrad Bishop            raise
26198cb3efbSBrad Bishop
26298cb3efbSBrad Bishop    for record_handle, pdr in get_pdrs_one_at_a_time(client):
26398cb3efbSBrad Bishop        yield record_handle, pdr
26498cb3efbSBrad Bishop
26598cb3efbSBrad Bishop
266b8cc3257SManojkiran Edadef fetch_pdrs_from_bmc(client):
267b8cc3257SManojkiran Eda
268b8cc3257SManojkiran Eda    """ This is the core function that would use the existing ssh connection
269b8cc3257SManojkiran Eda        object to connect to BMC and fire the getPDR pldmtool command
270b8cc3257SManojkiran Eda        and it then agreegates the data received from all the calls into
271b8cc3257SManojkiran Eda        the respective dictionaries based on the PDR Type.
272b8cc3257SManojkiran Eda
273b8cc3257SManojkiran Eda        Parameters:
274b8cc3257SManojkiran Eda            client: paramiko ssh client object
275b8cc3257SManojkiran Eda
276b8cc3257SManojkiran Eda    """
277b8cc3257SManojkiran Eda
278b8cc3257SManojkiran Eda    entity_association_pdr = {}
279b8cc3257SManojkiran Eda    state_sensor_pdr = {}
280b8cc3257SManojkiran Eda    state_effecter_pdr = {}
281b8cc3257SManojkiran Eda    state_effecter_pdr = {}
282b8cc3257SManojkiran Eda    numeric_pdr = {}
283b8cc3257SManojkiran Eda    fru_record_set_pdr = {}
284b8cc3257SManojkiran Eda    tl_pdr = {}
28591523137SBrad Bishop    for handle_number, my_dic in get_pdrs(client):
286b8cc3257SManojkiran Eda        sys.stdout.write("Fetching PDR's from BMC : %8d\r" % (handle_number))
287b8cc3257SManojkiran Eda        sys.stdout.flush()
288b8cc3257SManojkiran Eda        if my_dic["PDRType"] == "Entity Association PDR":
289b8cc3257SManojkiran Eda            entity_association_pdr[handle_number] = my_dic
290b8cc3257SManojkiran Eda        if my_dic["PDRType"] == "State Sensor PDR":
291b8cc3257SManojkiran Eda            state_sensor_pdr[handle_number] = my_dic
292b8cc3257SManojkiran Eda        if my_dic["PDRType"] == "State Effecter PDR":
293b8cc3257SManojkiran Eda            state_effecter_pdr[handle_number] = my_dic
294b8cc3257SManojkiran Eda        if my_dic["PDRType"] == "FRU Record Set PDR":
295b8cc3257SManojkiran Eda            fru_record_set_pdr[handle_number] = my_dic
296b8cc3257SManojkiran Eda        if my_dic["PDRType"] == "Terminus Locator PDR":
297b8cc3257SManojkiran Eda            tl_pdr[handle_number] = my_dic
298b8cc3257SManojkiran Eda        if my_dic["PDRType"] == "Numeric Effecter PDR":
299b8cc3257SManojkiran Eda            numeric_pdr[handle_number] = my_dic
300b8cc3257SManojkiran Eda    client.close()
301b8cc3257SManojkiran Eda
302b8cc3257SManojkiran Eda    total_pdrs = len(entity_association_pdr.keys()) + len(tl_pdr.keys()) + \
303b8cc3257SManojkiran Eda        len(state_effecter_pdr.keys()) + len(numeric_pdr.keys()) + \
304b8cc3257SManojkiran Eda        len(state_sensor_pdr.keys()) + len(fru_record_set_pdr.keys())
305b8cc3257SManojkiran Eda    print("\nSuccessfully fetched " + str(total_pdrs) + " PDR\'s")
306b8cc3257SManojkiran Eda    print("Number of FRU Record PDR's : ", len(fru_record_set_pdr.keys()))
307b8cc3257SManojkiran Eda    print("Number of TerminusLocator PDR's : ", len(tl_pdr.keys()))
308b8cc3257SManojkiran Eda    print("Number of State Sensor PDR's : ", len(state_sensor_pdr.keys()))
309b8cc3257SManojkiran Eda    print("Number of State Effecter PDR's : ", len(state_effecter_pdr.keys()))
310b8cc3257SManojkiran Eda    print("Number of Numeric Effecter PDR's : ", len(numeric_pdr.keys()))
311b8cc3257SManojkiran Eda    print("Number of Entity Association PDR's : ",
312b8cc3257SManojkiran Eda          len(entity_association_pdr.keys()))
313b8cc3257SManojkiran Eda    return (entity_association_pdr, state_sensor_pdr,
314b8cc3257SManojkiran Eda            state_effecter_pdr, len(fru_record_set_pdr.keys()))
315b8cc3257SManojkiran Eda
316b8cc3257SManojkiran Eda
317b8cc3257SManojkiran Edadef main():
318b8cc3257SManojkiran Eda
319b8cc3257SManojkiran Eda    """ Create a summary table capturing the information of all the PDR's
320b8cc3257SManojkiran Eda        from the BMC & also create a diagram that captures the entity
321b8cc3257SManojkiran Eda        association hierarchy."""
322b8cc3257SManojkiran Eda
323b8cc3257SManojkiran Eda    parser = argparse.ArgumentParser(prog='pldm_visualise_pdrs.py')
324b8cc3257SManojkiran Eda    parser.add_argument('--bmc', type=str, required=True,
325b8cc3257SManojkiran Eda                        help="BMC IPAddress/BMC Hostname")
326e0c55c8aSBrad Bishop    parser.add_argument('--user', type=str, help="BMC username")
327b8cc3257SManojkiran Eda    parser.add_argument('--password', type=str, required=True,
328b8cc3257SManojkiran Eda                        help="BMC Password")
329b8cc3257SManojkiran Eda    parser.add_argument('--port', type=int, help="BMC SSH port",
330b8cc3257SManojkiran Eda                        default=22)
331b8cc3257SManojkiran Eda    args = parser.parse_args()
3329a8192d9SBrad Bishop
333*9e0265b5SBrad Bishop    extra_cfg = {}
3349a8192d9SBrad Bishop    try:
3359a8192d9SBrad Bishop        with open(os.path.expanduser("~/.ssh/config")) as f:
3369a8192d9SBrad Bishop            ssh_config = paramiko.SSHConfig()
3379a8192d9SBrad Bishop            ssh_config.parse(f)
3389a8192d9SBrad Bishop            host_config = ssh_config.lookup(args.bmc)
3399a8192d9SBrad Bishop            if host_config:
3409a8192d9SBrad Bishop                if 'hostname' in host_config:
3419a8192d9SBrad Bishop                    args.bmc = host_config['hostname']
3429a8192d9SBrad Bishop                if 'user' in host_config and args.user is None:
3439a8192d9SBrad Bishop                    args.user = host_config['user']
344*9e0265b5SBrad Bishop                if 'proxycommand' in host_config:
345*9e0265b5SBrad Bishop                    extra_cfg['sock'] = paramiko.ProxyCommand(
346*9e0265b5SBrad Bishop                        host_config['proxycommand'])
3479a8192d9SBrad Bishop    except FileNotFoundError:
3489a8192d9SBrad Bishop        pass
3499a8192d9SBrad Bishop
350*9e0265b5SBrad Bishop    client = connect_to_bmc(
351*9e0265b5SBrad Bishop        args.bmc, args.user, args.password, args.port, **extra_cfg)
352b8cc3257SManojkiran Eda    association_pdr, state_sensor_pdr, state_effecter_pdr, counter = \
353b8cc3257SManojkiran Eda        fetch_pdrs_from_bmc(client)
354b8cc3257SManojkiran Eda    draw_entity_associations(association_pdr, counter)
355b8cc3257SManojkiran Eda    prepare_summary_report(state_sensor_pdr, state_effecter_pdr)
356b8cc3257SManojkiran Eda
357b8cc3257SManojkiran Eda
358b8cc3257SManojkiran Edaif __name__ == "__main__":
359b8cc3257SManojkiran Eda    main()
360