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