xref: /openbmc/openbmc-test-automation/lib/utils.py (revision 841ac303bf2e6bcda9e81a88107343587e72d869)
1 #!/usr/bin/env python3
2 
3 r"""
4 Companion file to utils.robot.
5 """
6 
7 import os
8 import json
9 import collections
10 import gen_print as gp
11 import gen_robot_keyword as grk
12 import bmc_ssh_utils as bsu
13 import var_funcs as vf
14 from robot.libraries.BuiltIn import BuiltIn
15 from robot.libraries import DateTime
16 try:
17     from robot.utils import DotDict
18 except ImportError:
19     pass
20 
21 
22 # The code base directory will be one level up from the directory containing this module.
23 code_base_dir_path = os.path.dirname(os.path.dirname(__file__)) + os.sep
24 
25 
26 def get_code_base_dir_path():
27     r"""
28     Return the dir path of our code base.
29     """
30 
31     return code_base_dir_path
32 
33 
34 def set_power_policy_method():
35     r"""
36     Set the global bmc_power_policy_method to either 'Old' or 'New'.
37 
38     The power policy data has moved from an 'org' location to an 'xyz'
39     location.  This keyword will determine whether the new method of getting
40     the power policy is valid and will set the global bmc_power_policy_method
41     variable accordingly.  If power_policy_setup is already set (by a prior
42     call to this function), this keyword will simply return.
43 
44     If bmc_power_policy_method is "Old", this function will adjust the global
45     policy variables from data/variables.py: RESTORE_LAST_STATE,
46     ALWAYS_POWER_ON, ALWAYS_POWER_OFF.
47     """
48 
49     # Retrieve global variables.
50     power_policy_setup = \
51         int(BuiltIn().get_variable_value("${power_policy_setup}",
52                                          default=0))
53     bmc_power_policy_method = \
54         BuiltIn().get_variable_value("${bmc_power_policy_method}",
55                                      default=0)
56     gp.dpvar(power_policy_setup)
57 
58     # If this function has already been run once, we need not continue.
59     if power_policy_setup:
60         return
61 
62     gp.dpvar(bmc_power_policy_method, 1)
63 
64     # The user has not set bmc_power_policy_method via a -v parm so we will
65     # determine what it should be.
66     if bmc_power_policy_method == "":
67         status, ret_values = grk.run_key_u("New Get Power Policy", ignore=1)
68         if status == 'PASS':
69             bmc_power_policy_method = 'New'
70         else:
71             bmc_power_policy_method = 'Old'
72 
73     gp.qpvar(bmc_power_policy_method)
74     # For old style, we will rewrite these global variable settings to old
75     # values.
76     if bmc_power_policy_method == "Old":
77         BuiltIn().set_global_variable("${RESTORE_LAST_STATE}",
78                                       "RESTORE_LAST_STATE")
79         BuiltIn().set_global_variable("${ALWAYS_POWER_ON}",
80                                       "ALWAYS_POWER_ON")
81         BuiltIn().set_global_variable("${ALWAYS_POWER_OFF}",
82                                       "ALWAYS_POWER_OFF")
83 
84     # Set global variables to control subsequent calls to this function.
85     BuiltIn().set_global_variable("${bmc_power_policy_method}",
86                                   bmc_power_policy_method)
87     BuiltIn().set_global_variable("${power_policy_setup}", 1)
88 
89 
90 def translate_power_policy_value(policy):
91     r"""
92     Translate the policy value and return the result.
93 
94     Using old style functions, callers might call like this with a hard-
95     code value for policy:
96 
97     Set BMC Power Policy  ALWAYS_POWER_OFF
98 
99     This function will get the value of the corresponding global variable (if
100     it exists) and return it.
101 
102     This will allow the old style call to still work on systems using the new
103     method of storing the policy value.
104     """
105 
106     valid_power_policy_vars = \
107         BuiltIn().get_variable_value("${valid_power_policy_vars}")
108 
109     if policy not in valid_power_policy_vars:
110         return policy
111 
112     status, ret_values = grk.run_key_u("Get Variable Value  ${" + policy + "}",
113                                        quiet=1)
114     return ret_values
115 
116 
117 def get_bmc_date_time():
118     r"""
119     Get date/time info from BMC and return as a dictionary.
120 
121     Example of dictionary data returned by this keyword.
122     time_dict:
123       [local_time]:               Fri 2017-11-03 152756 UTC
124       [local_time_seconds]:       1509740876
125       [universal_time]:           Fri 2017-11-03 152756 UTC
126       [universal_time_seconds]:   1509740876
127       [rtc_time]:                 Fri 2016-05-20 163403
128       [rtc_time_seconds]:         1463780043
129       [time_zone]:                n/a (UTC, +0000)
130       [network_time_on]:          yes
131       [ntp_synchronized]:         no
132       [rtc_in_local_tz]:          no
133     """
134 
135     out_buf, stderr, rc = bsu.bmc_execute_command('timedatectl')
136     # Example of output returned by call to timedatectl:
137     #       Local time: Fri 2017-11-03 15:27:56 UTC
138     #   Universal time: Fri 2017-11-03 15:27:56 UTC
139     #         RTC time: Fri 2016-05-20 16:34:03
140     #        Time zone: n/a (UTC, +0000)
141     #  Network time on: yes
142     # NTP synchronized: no
143     #  RTC in local TZ: no
144 
145     # Convert the out_buf to a dictionary.
146     initial_time_dict = vf.key_value_outbuf_to_dict(out_buf)
147 
148     # For each "_time" entry in the dictionary, we will create a corresponding
149     # "_time_seconds" entry.  We create a new dictionary so that the entries
150     # are kept in a nice order for printing.
151     try:
152         result_time_dict = collections.OrderedDict()
153     except AttributeError:
154         result_time_dict = DotDict()
155 
156     for key, value in initial_time_dict.items():
157         result_time_dict[key] = value
158         if not key.endswith("_time"):
159             continue
160         result_time_dict[key + '_seconds'] = \
161             int(DateTime.convert_date(value, result_format='epoch'))
162 
163     return result_time_dict
164 
165 
166 def get_bmc_df(df_parm_string=""):
167     r"""
168     Get df report from BMC and return as a report "object".
169 
170     A df report object is a list where each entry is a dictionary whose keys
171     are the field names from the first entry in report_list.
172 
173     Example df report object:
174 
175     df_report:
176       df_report[0]:
177         [filesystem]:    dev
178         [1k-blocks]:     247120
179         [used]:          0
180         [available]:     247120
181         [use%]:          0%
182         [mounted]:       /dev
183       df_report[1]:
184         [filesystem]:    dev
185         [1k-blocks]:     247120
186         [used]:          0
187         [available]:     247120
188         [use%]:          0%
189         [mounted]:       /dev
190 
191 .   Description of argument(s):
192     df_parm_string  A string containing valid df command parms (e.g.
193                     "-h /var").
194     """
195 
196     out_buf, stderr, rc = bsu.bmc_execute_command("df " + df_parm_string)
197     return vf.outbuf_to_report(out_buf)
198 
199 
200 def get_sbe():
201     r"""
202     Return CFAM value which contains such things as SBE side bit.
203     """
204 
205     cmd_buf = "pdbg -d p9w -p0 getcfam 0x2808 | sed -re 's/.* = //g'"
206     out_buf, stderr, rc = bsu.bmc_execute_command(cmd_buf)
207 
208     return int(out_buf, 16)
209 
210 
211 def compare_mac_address(sys_mac_addr, user_mac_addr):
212     r"""
213     Return 1 if the MAC value matched, otherwise 0.
214 
215 .   Description of argument(s):
216     sys_mac_addr   A valid system MAC string (e.g. "70:e2:84:14:2a:08")
217     user_mac_addr  A user provided MAC string (e.g. "70:e2:84:14:2a:08")
218     """
219 
220     index = 0
221     # Example: ['70', 'e2', '84', '14', '2a', '08']
222     mac_list = user_mac_addr.split(":")
223     for item in sys_mac_addr.split(":"):
224         if int(item, 16) == int(mac_list[index], 16):
225             index = index + 1
226             continue
227         return 0
228 
229     return 1
230 
231 
232 def get_os_ethtool(interface_name):
233     r"""
234     Get OS 'ethtool' output for the given interface_name and return it as a
235     dictionary.
236 
237     Settings for enP52p1s0f0:
238           Supported ports: [ TP ]
239           Supported link modes:   10baseT/Half 10baseT/Full
240                                   100baseT/Half 100baseT/Full
241                                   1000baseT/Half 1000baseT/Full
242           Supported pause frame use: No
243           Supports auto-negotiation: Yes
244           Supported FEC modes: Not reported
245           Advertised link modes:  10baseT/Half 10baseT/Full
246                                   100baseT/Half 100baseT/Full
247                                   1000baseT/Half 1000baseT/Full
248           Advertised pause frame use: Symmetric
249           Advertised auto-negotiation: Yes
250           Advertised FEC modes: Not reported
251           Speed: Unknown!
252           Duplex: Unknown! (255)
253           Port: Twisted Pair
254           PHYAD: 1
255           Transceiver: internal
256           Auto-negotiation: on
257           MDI-X: Unknown
258           Supports Wake-on: g
259           Wake-on: g
260           Current message level: 0x000000ff (255)
261                                  drv probe link timer ifdown ifup rx_err tx_err
262           Link detected: no
263 
264     Given that data, this function will return the following dictionary.
265 
266     ethtool_dict:
267       [supported_ports]:             [ TP ]
268       [supported_link_modes]:
269         [supported_link_modes][0]:   10baseT/Half 10baseT/Full
270         [supported_link_modes][1]:   100baseT/Half 100baseT/Full
271         [supported_link_modes][2]:   1000baseT/Half 1000baseT/Full
272       [supported_pause_frame_use]:   No
273       [supports_auto-negotiation]:   Yes
274       [supported_fec_modes]:         Not reported
275       [advertised_link_modes]:
276         [advertised_link_modes][0]:  10baseT/Half 10baseT/Full
277         [advertised_link_modes][1]:  100baseT/Half 100baseT/Full
278         [advertised_link_modes][2]:  1000baseT/Half 1000baseT/Full
279       [advertised_pause_frame_use]:  Symmetric
280       [advertised_auto-negotiation]: Yes
281       [advertised_fec_modes]:        Not reported
282       [speed]:                       Unknown!
283       [duplex]:                      Unknown! (255)
284       [port]:                        Twisted Pair
285       [phyad]:                       1
286       [transceiver]:                 internal
287       [auto-negotiation]:            on
288       [mdi-x]:                       Unknown
289       [supports_wake-on]:            g
290       [wake-on]:                     g
291       [current_message_level]:       0x000000ff (255)
292       [drv_probe_link_timer_ifdown_ifup_rx_err_tx_err]:<blank>
293       [link_detected]:               no
294     """
295 
296     # Using sed and tail to massage the data a bit before running
297     # key_value_outbuf_to_dict.
298     cmd_buf = "ethtool " + interface_name +\
299         " | sed -re 's/(.* link modes:)(.*)/\\1\\n\\2/g' | tail -n +2"
300     stdout, stderr, rc = bsu.os_execute_command(cmd_buf)
301     result = vf.key_value_outbuf_to_dict(stdout, process_indent=1, strip=" \t")
302 
303     return result
304 
305 
306 def to_json_ordered(json_str):
307     r"""
308     Parse the JSON string data and return an ordered JSON dictionary object.
309 
310     Description of argument(s):
311     json_str                        The string containing the JSON data.
312     """
313 
314     try:
315         return json.loads(json_str, object_pairs_hook=DotDict)
316     except TypeError:
317         return json.loads(json_str.decode("utf-8"), object_pairs_hook=DotDict)
318 
319 
320 def get_bmc_release_info():
321     r"""
322     Get release info from the BMC and return as a dictionary.
323 
324     Example:
325 
326     ${release_info}=  Get BMC Release Info
327     Rprint Vars  release_info
328 
329     Output:
330 
331     release_info:
332       [id]:                           openbmc-phosphor
333       [name]:                         Phosphor OpenBMC (Phosphor OpenBMC Project Reference...
334       [version]:                      2.8.0-dev
335       [version_id]:                   2.8.0-dev-1083-g8954c3505
336       [pretty_name]:                  Phosphor OpenBMC (Phosphor OpenBMC Project Reference...
337       [build_id]:                     2.8.0-dev
338       [openbmc_target_machine]:       witherspoon
339     """
340 
341     out_buf, stderr, rc = bsu.bmc_execute_command('cat /etc/os-release')
342     return vf.key_value_outbuf_to_dict(out_buf, delim="=", strip='"')
343 
344 
345 def get_os_release_info():
346     r"""
347     Get release info from the OS and return as a dictionary.
348 
349     Example:
350 
351     ${release_info}=  Get OS Release Info
352     Rprint Vars  release_info
353 
354     Output:
355     release_info:
356       [name]:                                         Red Hat Enterprise Linux Server
357       [version]:                                      7.6 (Maipo)
358       [id]:                                           rhel
359       [id_like]:                                      fedora
360       [variant]:                                      Server
361       [variant_id]:                                   server
362       [version_id]:                                   7.6
363       [pretty_name]:                                  Red Hat Enterprise Linux Server 7.6 (Maipo)
364       [ansi_color]:                                   0;31
365       [cpe_name]:                                     cpe:/o:redhat:enterprise_linux:7.6:GA:server
366       [home_url]:                                     https://www.redhat.com/
367       [bug_report_url]:                               https://bugzilla.redhat.com/
368       [redhat_bugzilla_product]:                      Red Hat Enterprise Linux 7
369       [redhat_bugzilla_product_version]:              7.6
370       [redhat_support_product]:                       Red Hat Enterprise Linux
371       [redhat_support_product_version]:               7.6
372     """
373 
374     out_buf, stderr, rc = bsu.os_execute_command('cat /etc/os-release')
375     return vf.key_value_outbuf_to_dict(out_buf, delim="=", strip='"')
376 
377 
378 def pdbg(option_string, **bsu_options):
379     r"""
380     Run pdbg on the BMC with the caller's option string and return the output.
381 
382     Description of argument(s):
383     option_string    A string of options which are to be processed by the pdbg command.
384     bsu_options      Options to be passed directly to bmc_execute_command.  See its prolog for
385                      details.
386     """
387 
388     # Default print_out to 1.
389     if 'print_out' not in bsu_options:
390         bsu_options['print_out'] = 1
391 
392     stdout, stderr, rc = bsu.bmc_execute_command('pdbg ' + option_string, **bsu_options)
393     return stdout
394 
395 
396 def ecmd(option_string, **bsu_options):
397     r"""
398     Run ecmd command on the BMC with the caller's option string and return the output.
399 
400     Description of argument(s):
401     option_string    A string of options which are to be executed on BMC.
402                      (e.g. getscom pu 20010a40 -all,
403                      putscom pu 20010a40 4000000000000000 -p0).
404     bsu_options      Options to be passed directly to bmc_execute_command.  See its prolog for
405                      details.
406     """
407 
408     # Default print_out to 1.
409     if 'print_out' not in bsu_options:
410         bsu_options['print_out'] = 1
411 
412     stdout, stderr, rc = bsu.bmc_execute_command(option_string, **bsu_options)
413     return stdout
414 
415 
416 def split_string_with_index(stri, n):
417     r"""
418     To split every n characters and forms an element for every nth index
419 
420     Example : Given ${stri} = "abcdef", then the function call,
421     ${data}=  Split List With Index  ${stri}  2
422     then, result will be data = ['ab', 'cd', 'ef']
423     """
424 
425     n = int(n)
426     data = [stri[index: index + n] for index in range(0, len(stri), n)]
427     return data
428 
429 
430 def remove_whitespace(instring):
431     r"""
432     Removes the white spaces around the string
433 
434     Example: instring = "  xxx  ", then returns instring = "xxx"
435     """
436 
437     return instring.strip()
438 
439 
440 def zfill_data(data, num):
441     r"""
442     zfill() method adds zeros (0) at the beginning of the string, until it
443     reaches the specified length.
444 
445     Usage : ${anystr}=  Zfill Data  ${data}  num
446 
447     Example : Binary of one Byte has 8 bits - xxxx xxxx
448 
449     Consider ${binary} has only 3 bits after converting from Hexadecimal/decimal to Binary
450     Say ${binary} = 110 then,
451     ${binary}=  Zfill Data  ${binary}  8
452     Now ${binary} will be 0000 0110
453     """
454 
455     return data.zfill(int(num))
456 
457 
458 def get_subsequent_value_from_list(list, value):
459     r"""
460     returns first index of the element occurrence.
461     """
462 
463     index = [list.index(i) for i in list if value in i]
464     return index
465 
466 
467 def return_decoded_string(input):
468     r"""
469     returns decoded string of encoded byte.
470     """
471 
472     encoded_string = input.encode('ascii', 'ignore')
473     decoded_string = encoded_string.decode()
474     return decoded_string
475