1#!/usr/bin/env python3 -u
2
3r"""
4Generic utility functions.
5"""
6import importlib.util
7import random
8import string
9import subprocess
10
11from robot.libraries.BuiltIn import BuiltIn
12from robot.utils import DotDict
13
14
15def load_source(name, module_path):
16    r"""
17    import util to replace deprecated imp.load_source
18    """
19    spec = importlib.util.spec_from_file_location(name, module_path)
20    module = importlib.util.module_from_spec(spec)
21    spec.loader.exec_module(module)
22    return module
23
24
25def random_mac():
26    r"""
27    Return random mac address in the following format.
28    Example: 00:01:6C:80:02:78
29    """
30    return ":".join(
31        map(
32            lambda x: "%02x" % x,
33            (random.randint(0x00, 0xFF) for _ in range(6)),
34        )
35    )
36
37
38def random_ip():
39    r"""
40    Return random ip address in the following format.
41    Example: 9.3.128.100
42    """
43    return ".".join(map(str, (random.randint(0, 255) for _ in range(4))))
44
45
46def get_sensor(module_name, value):
47    r"""
48    Return sensor matched ID name.
49    """
50    m = load_source("module.name", module_name)
51
52    for i in m.ID_LOOKUP["SENSOR"]:
53        if m.ID_LOOKUP["SENSOR"][i] == value:
54            return i
55
56    return 0xFF
57
58
59def get_inventory_sensor(module_name, value):
60    r"""
61    Return sensor matched ID name from inventory.
62    """
63    m = load_source("module.name", module_name)
64
65    value = string.replace(value, m.INVENTORY_ROOT, "<inventory_root>")
66
67    for i in m.ID_LOOKUP["SENSOR"]:
68        if m.ID_LOOKUP["SENSOR"][i] == value:
69            return i
70
71    return 0xFF
72
73
74################################################################
75#  This will return the URI's of the FRU type
76#
77#  i.e.  get_inventory_list('../data/Palmetto.py')
78#
79#  [/org/openbmc/inventory/system/chassis/motherboard/cpu0/core0,
80#   /org/openbmc/inventory/system/chassis/motherboard/dimm0]
81################################################################
82def get_inventory_list(module_name):
83    r"""
84    Return all FRU URI(s) list available from inventory.
85    """
86
87    inventory_list = []
88    m = load_source("module.name", module_name)
89
90    for i in m.ID_LOOKUP["FRU"]:
91        s = m.ID_LOOKUP["FRU"][i]
92        s = s.replace("<inventory_root>", m.INVENTORY_ROOT)
93        inventory_list.append(s)
94
95    return inventory_list
96
97
98################################################################
99#  This will return the URI's of the FRU type
100#
101#  i.e.  get_inventory_fru_type_list('../data/Witherspoon.py', 'CPU')
102#
103#  [/org/openbmc/inventory/system/chassis/motherboard/cpu0,
104#   /org/openbmc/inventory/system/chassis/motherboard/cpu1]
105################################################################
106def get_inventory_fru_type_list(module_name, fru):
107    r"""
108    Return FRU URI(s) list of a given type from inventory.
109    """
110    inventory_list = []
111    m = load_source("module.name", module_name)
112
113    for i in m.FRU_INSTANCES.keys():
114        if m.FRU_INSTANCES[i]["fru_type"] == fru:
115            s = i.replace("<inventory_root>", m.INVENTORY_ROOT)
116            inventory_list.append(s)
117
118    return inventory_list
119
120
121################################################################
122#  This will return the URI's of the FRU type that contain VPD
123#
124#  i.e.  get_vpd_inventory_list('../data/Palmetto.py', 'DIMM')
125#
126#  [/org/openbmc/inventory/system/chassis/motherboard/dimm0,
127#   /org/openbmc/inventory/system/chassis/motherboard/dimm1]
128################################################################
129def get_vpd_inventory_list(module_name, fru):
130    r"""
131    Return VPD URI(s) list of a FRU type from inventory.
132    """
133    inventory_list = []
134    m = load_source("module.name", module_name)
135
136    for i in m.ID_LOOKUP["FRU_STR"]:
137        x = m.ID_LOOKUP["FRU_STR"][i]
138
139        if m.FRU_INSTANCES[x]["fru_type"] == fru:
140            s = x.replace("<inventory_root>", m.INVENTORY_ROOT)
141            inventory_list.append(s)
142
143    return inventory_list
144
145
146def call_keyword(keyword):
147    r"""
148    Return result of the execute robot keyword.
149    """
150    return BuiltIn().run_keyword(keyword)
151
152
153def main():
154    r"""
155    Python main func call.
156    """
157    print(get_vpd_inventory_list("../data/Palmetto.py", "DIMM"))
158
159
160if __name__ == "__main__":
161    main()
162
163
164def get_mtr_report(host=""):
165    r"""
166    Get an mtr report and return it as a dictionary of dictionaries.
167
168    The key for the top level dictionary will be the host DNS name.  The key
169    for the next level dictionary will be the field of a given row of the
170    report.
171
172    Example result:
173
174    report:
175      report[host_dummy-dnsname.com]:
176        report[host_dummy-dnsname.com][row_num]:  1
177        report[host_dummy-dnsname.com][host]:     host_dummy-dnsname.com
178        report[host_dummy-dnsname.com][loss]:     0.0
179        report[host_dummy-dnsname.com][snt]:      10
180        report[host_dummy-dnsname.com][last]:     0.2
181        report[host_dummy-dnsname.com][avg]:      3.5
182        report[host_dummy-dnsname.com][best]:     0.2
183        report[host_dummy-dnsname.com][wrst]:     32.5
184        report[host_dummy-dnsname.com][stdev]:    10.2
185      report[bmc-dummy-dnsname.com]:
186        report[bmc-dummy-dnsname.com][row_num]:     2
187        report[bmc-dummy-dnsname.com][host]:        bmc-dummy-dnsname.com
188        report[bmc-dummy-dnsname.com][loss]:        0.0
189        report[bmc-dummy-dnsname.com][snt]:         10
190        report[bmc-dummy-dnsname.com][last]:        0.5
191        report[bmc-dummy-dnsname.com][avg]:         0.5
192        report[bmc-dummy-dnsname.com][best]:        0.5
193        report[bmc-dummy-dnsname.com][wrst]:        0.5
194        report[bmc-dummy-dnsname.com][stdev]:       0.0
195
196    Description of arguments:
197    host   The DNS name or IP address to be passed to the mtr command.
198    """
199
200    # Run the mtr command.  Exclude the header line.  Trim leading space from
201    # each line.  Change all multiple spaces delims to single space delims.
202    cmd_buf = (
203        "mtr --report "
204        + host
205        + " | tail -n +2 | sed -r -e 's/^[ ]+//g' -e 's/[ ]+/ /g'"
206    )
207    sub_proc = subprocess.Popen(
208        cmd_buf, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
209    )
210    out_buf, err_buf = sub_proc.communicate()
211    shell_rc = sub_proc.returncode
212    out_buf = out_buf.decode("utf-8")
213
214    # Split the output by line.
215    rows = out_buf.rstrip("\n").split("\n")
216
217    # Initialize report dictionary.
218    report = DotDict()
219    for row in rows:
220        # Process each row of mtr output.
221        # Create a list of fields by splitting on space delimiter.
222        row_list = row.split(" ")
223        # Create dictionary for the row.
224        row = DotDict()
225        row["row_num"] = row_list[0].rstrip(".")
226        row["host"] = row_list[1]
227        row["loss"] = row_list[2].rstrip("%")
228        row["snt"] = row_list[3]
229        row["last"] = row_list[4]
230        row["avg"] = row_list[5]
231        row["best"] = row_list[6]
232        row["wrst"] = row_list[7]
233        row["stdev"] = row_list[8]
234        report[row["host"]] = row
235
236    # Return the full report as dictionary of dictionaries.
237    return report
238
239
240def get_mtr_row(host=""):
241    r"""
242    Run an mtr report and get a specified row and return it as a dictionary.
243
244    Example result:
245
246    row:
247      row[row_num]:              2
248      row[host]:                 bmc-dummy-dnsname.com
249      row[loss]:                 0.0
250      row[snt]:                  10
251      row[last]:                 0.5
252      row[avg]:                  0.5
253      row[best]:                 0.4
254      row[wrst]:                 0.7
255      row[stdev]:                0.1
256
257    Description of arguments:
258    host   The DNS name or IP address to be passed to the mtr command as
259           well as the indicating which row of the report to return.
260    """
261
262    report = get_mtr_report(host)
263
264    # The max length of host in output is 28 chars.
265    row = [value for key, value in report.items() if host[0:28] in key][0]
266
267    return row
268
269
270def list_to_set(fru_list=""):
271    r"""
272    Pack the list into a set tuple and return.
273
274    It may seem that this function is rather trivial. However, it simplifies
275    the code and improves robot program readability and achieve the result
276    required.
277
278    Example result:
279
280    set(['Version', 'PartNumber', 'SerialNumber', 'FieldReplaceable',
281    'BuildDate', 'Present', 'Manufacturer', 'PrettyName', 'Cached', 'Model'])
282
283    # Description of arguments.
284    fru_list   List of FRU's elements.
285    """
286    return set(fru_list)
287
288
289def min_list_value(value_list):
290    r"""
291    Returns the element from the list with minimum value.
292    """
293    return min(value_list)
294
295
296def convert_lsb_to_msb(string):
297    r"""
298    Reverse given string (From LSB first to MSB first) and converts to HEX.
299
300    Input string     0a 00
301    Return string    0a
302    """
303    datalist = string.split(" ")
304    new_list = datalist[::-1]
305    new_string = "".join([str(element) for element in new_list])
306    return int(new_string, 16)
307
308
309def add_prefix_to_string(string, prefix):
310    r"""
311    Add given prefix to the string and return string.
312
313    Input string      0a 01
314    Return string     0x0a 0x01
315    """
316    prefix_string = ""
317    data_list = string.strip().split(" ")
318    for item in data_list:
319        prefix_string += prefix + item + " "
320    return prefix_string.strip()
321
322
323def get_value_from_nested_dict(key, nested_dict):
324    r"""
325    Returns the key value from the nested dictionary.
326
327    key               Key value of the dictionary to look up.
328    nested_dict       Dictionary data.
329    """
330    result = []
331    for k, v in nested_dict.items():
332        if k == key:
333            result.append(v)
334        elif isinstance(v, dict) and k != key:
335            result += get_value_from_nested_dict(key, v)
336
337    return result
338