1#!/usr/bin/env python3
2
3r"""
4Companion file to utils.robot.
5"""
6
7import os
8import gen_print as gp
9import gen_robot_keyword as grk
10import bmc_ssh_utils as bsu
11import var_funcs as vf
12from robot.libraries.BuiltIn import BuiltIn
13from robot.libraries import DateTime
14import json
15try:
16    from robot.utils import DotDict
17except ImportError:
18    pass
19import collections
20
21
22# The code base directory will be one level up from the directory containing this module.
23code_base_dir_path = os.path.dirname(os.path.dirname(__file__)) + os.sep
24
25
26def 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
34def 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
90def 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
117def 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
166def 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
200def 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
211def 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
232def 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
306def 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
320def 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
345def 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
378def 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
396def 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
416def 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
430def 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
440def zfill_data(data, num):
441    r"""
442    zfill() method adds zeros (0) at the beginning of the string, until it reaches the specified length.
443
444    Usage : ${anystr}=  Zfill Data  ${data}  num
445
446    Example : Binary of one Byte has 8 bits - xxxx xxxx
447
448    Consider ${binary} has only 3 bits after converting from Hexadecimal/decimal to Binary
449    Say ${binary} = 110 then,
450    ${binary}=  Zfill Data  ${binary}  8
451    Now ${binary} will be 0000 0110
452    """
453
454    return data.zfill(int(num))
455