1#!/usr/bin/env python
2
3r"""
4Companion file to utils.robot.
5"""
6
7import gen_print as gp
8import gen_robot_keyword as grk
9import bmc_ssh_utils as bsu
10import var_funcs as vf
11from robot.libraries.BuiltIn import BuiltIn
12from robot.libraries import DateTime
13import json
14try:
15    from robot.utils import DotDict
16except ImportError:
17    pass
18import collections
19
20
21def set_power_policy_method():
22    r"""
23    Set the global bmc_power_policy_method to either 'Old' or 'New'.
24
25    The power policy data has moved from an 'org' location to an 'xyz'
26    location.  This keyword will determine whether the new method of getting
27    the power policy is valid and will set the global bmc_power_policy_method
28    variable accordingly.  If power_policy_setup is already set (by a prior
29    call to this function), this keyword will simply return.
30
31    If bmc_power_policy_method is "Old", this function will adjust the global
32    policy variables from data/variables.py: RESTORE_LAST_STATE,
33    ALWAYS_POWER_ON, ALWAYS_POWER_OFF.
34    """
35
36    # Retrieve global variables.
37    power_policy_setup = \
38        int(BuiltIn().get_variable_value("${power_policy_setup}",
39                                         default=0))
40    bmc_power_policy_method = \
41        BuiltIn().get_variable_value("${bmc_power_policy_method}",
42                                     default=0)
43    gp.dpvar(power_policy_setup)
44
45    # If this function has already been run once, we need not continue.
46    if power_policy_setup:
47        return
48
49    gp.dpvar(bmc_power_policy_method, 1)
50
51    # The user has not set bmc_power_policy_method via a -v parm so we will
52    # determine what it should be.
53    if bmc_power_policy_method == "":
54        status, ret_values = grk.run_key_u("New Get Power Policy", ignore=1)
55        if status == 'PASS':
56            bmc_power_policy_method = 'New'
57        else:
58            bmc_power_policy_method = 'Old'
59
60    gp.qpvar(bmc_power_policy_method)
61    # For old style, we will rewrite these global variable settings to old
62    # values.
63    if bmc_power_policy_method == "Old":
64        BuiltIn().set_global_variable("${RESTORE_LAST_STATE}",
65                                      "RESTORE_LAST_STATE")
66        BuiltIn().set_global_variable("${ALWAYS_POWER_ON}",
67                                      "ALWAYS_POWER_ON")
68        BuiltIn().set_global_variable("${ALWAYS_POWER_OFF}",
69                                      "ALWAYS_POWER_OFF")
70
71    # Set global variables to control subsequent calls to this function.
72    BuiltIn().set_global_variable("${bmc_power_policy_method}",
73                                  bmc_power_policy_method)
74    BuiltIn().set_global_variable("${power_policy_setup}", 1)
75
76
77def translate_power_policy_value(policy):
78    r"""
79    Translate the policy value and return the result.
80
81    Using old style functions, callers might call like this with a hard-
82    code value for policy:
83
84    Set BMC Power Policy  ALWAYS_POWER_OFF
85
86    This function will get the value of the corresponding global variable (if
87    it exists) and return it.
88
89    This will allow the old style call to still work on systems using the new
90    method of storing the policy value.
91    """
92
93    valid_power_policy_vars = \
94        BuiltIn().get_variable_value("${valid_power_policy_vars}")
95
96    if policy not in valid_power_policy_vars:
97        return policy
98
99    status, ret_values = grk.run_key_u("Get Variable Value  ${" + policy + "}",
100                                       quiet=1)
101    return ret_values
102
103
104def get_bmc_date_time():
105    r"""
106    Get date/time info from BMC and return as a dictionary.
107
108    Example of dictionary data returned by this keyword.
109    time_dict:
110      [local_time]:               Fri 2017-11-03 152756 UTC
111      [local_time_seconds]:       1509740876
112      [universal_time]:           Fri 2017-11-03 152756 UTC
113      [universal_time_seconds]:   1509740876
114      [rtc_time]:                 Fri 2016-05-20 163403
115      [rtc_time_seconds]:         1463780043
116      [time_zone]:                n/a (UTC, +0000)
117      [network_time_on]:          yes
118      [ntp_synchronized]:         no
119      [rtc_in_local_tz]:          no
120    """
121
122    out_buf, stderr, rc = bsu.bmc_execute_command('timedatectl')
123    # Example of output returned by call to timedatectl:
124    #       Local time: Fri 2017-11-03 15:27:56 UTC
125    #   Universal time: Fri 2017-11-03 15:27:56 UTC
126    #         RTC time: Fri 2016-05-20 16:34:03
127    #        Time zone: n/a (UTC, +0000)
128    #  Network time on: yes
129    # NTP synchronized: no
130    #  RTC in local TZ: no
131
132    # Convert the out_buf to a dictionary.
133    initial_time_dict = vf.key_value_outbuf_to_dict(out_buf)
134
135    # For each "_time" entry in the dictionary, we will create a corresponding
136    # "_time_seconds" entry.  We create a new dictionary so that the entries
137    # are kept in a nice order for printing.
138    try:
139        result_time_dict = collections.OrderedDict()
140    except AttributeError:
141        result_time_dict = DotDict()
142
143    for key, value in initial_time_dict.items():
144        result_time_dict[key] = value
145        if not key.endswith("_time"):
146            continue
147        result_time_dict[key + '_seconds'] = \
148            int(DateTime.convert_date(value, result_format='epoch'))
149
150    return result_time_dict
151
152
153def get_bmc_df(df_parm_string=""):
154    r"""
155    Get df report from BMC and return as a report "object".
156
157    A df report object is a list where each entry is a dictionary whose keys
158    are the field names from the first entry in report_list.
159
160    Example df report object:
161
162    df_report:
163      df_report[0]:
164        [filesystem]:    dev
165        [1k-blocks]:     247120
166        [used]:          0
167        [available]:     247120
168        [use%]:          0%
169        [mounted]:       /dev
170      df_report[1]:
171        [filesystem]:    dev
172        [1k-blocks]:     247120
173        [used]:          0
174        [available]:     247120
175        [use%]:          0%
176        [mounted]:       /dev
177
178.   Description of argument(s):
179    df_parm_string  A string containing valid df command parms (e.g.
180                    "-h /var").
181    """
182
183    out_buf, stderr, rc = bsu.bmc_execute_command("df " + df_parm_string)
184    return vf.outbuf_to_report(out_buf)
185
186
187def get_sbe():
188    r"""
189    Return CFAM value which contains such things as SBE side bit.
190    """
191
192    cmd_buf = "pdbg -d p9w -p0 getcfam 0x2808 | sed -re 's/.* = //g'"
193    out_buf, stderr, rc = bsu.bmc_execute_command(cmd_buf)
194
195    return int(out_buf, 16)
196
197
198def compare_mac_address(sys_mac_addr, user_mac_addr):
199    r"""
200    Return 1 if the MAC value matched, otherwise 0.
201
202.   Description of argument(s):
203    sys_mac_addr   A valid system MAC string (e.g. "70:e2:84:14:2a:08")
204    user_mac_addr  A user provided MAC string (e.g. "70:e2:84:14:2a:08")
205    """
206
207    index = 0
208    # Example: ['70', 'e2', '84', '14', '2a', '08']
209    mac_list = user_mac_addr.split(":")
210    for item in sys_mac_addr.split(":"):
211        if int(item, 16) == int(mac_list[index], 16):
212            index = index + 1
213            continue
214        return 0
215
216    return 1
217
218
219def get_os_ethtool(interface_name):
220    r"""
221    Get OS 'ethtool' output for the given interface_name and return it as a
222    dictionary.
223
224    Settings for enP52p1s0f0:
225          Supported ports: [ TP ]
226          Supported link modes:   10baseT/Half 10baseT/Full
227                                  100baseT/Half 100baseT/Full
228                                  1000baseT/Half 1000baseT/Full
229          Supported pause frame use: No
230          Supports auto-negotiation: Yes
231          Supported FEC modes: Not reported
232          Advertised link modes:  10baseT/Half 10baseT/Full
233                                  100baseT/Half 100baseT/Full
234                                  1000baseT/Half 1000baseT/Full
235          Advertised pause frame use: Symmetric
236          Advertised auto-negotiation: Yes
237          Advertised FEC modes: Not reported
238          Speed: Unknown!
239          Duplex: Unknown! (255)
240          Port: Twisted Pair
241          PHYAD: 1
242          Transceiver: internal
243          Auto-negotiation: on
244          MDI-X: Unknown
245          Supports Wake-on: g
246          Wake-on: g
247          Current message level: 0x000000ff (255)
248                                 drv probe link timer ifdown ifup rx_err tx_err
249          Link detected: no
250
251    Given that data, this function will return the following dictionary.
252
253    ethtool_dict:
254      [supported_ports]:             [ TP ]
255      [supported_link_modes]:
256        [supported_link_modes][0]:   10baseT/Half 10baseT/Full
257        [supported_link_modes][1]:   100baseT/Half 100baseT/Full
258        [supported_link_modes][2]:   1000baseT/Half 1000baseT/Full
259      [supported_pause_frame_use]:   No
260      [supports_auto-negotiation]:   Yes
261      [supported_fec_modes]:         Not reported
262      [advertised_link_modes]:
263        [advertised_link_modes][0]:  10baseT/Half 10baseT/Full
264        [advertised_link_modes][1]:  100baseT/Half 100baseT/Full
265        [advertised_link_modes][2]:  1000baseT/Half 1000baseT/Full
266      [advertised_pause_frame_use]:  Symmetric
267      [advertised_auto-negotiation]: Yes
268      [advertised_fec_modes]:        Not reported
269      [speed]:                       Unknown!
270      [duplex]:                      Unknown! (255)
271      [port]:                        Twisted Pair
272      [phyad]:                       1
273      [transceiver]:                 internal
274      [auto-negotiation]:            on
275      [mdi-x]:                       Unknown
276      [supports_wake-on]:            g
277      [wake-on]:                     g
278      [current_message_level]:       0x000000ff (255)
279      [drv_probe_link_timer_ifdown_ifup_rx_err_tx_err]:<blank>
280      [link_detected]:               no
281    """
282
283    # Using sed and tail to massage the data a bit before running
284    # key_value_outbuf_to_dict.
285    cmd_buf = "ethtool " + interface_name +\
286        " | sed -re 's/(.* link modes:)(.*)/\\1\\n\\2/g' | tail -n +2"
287    stdout, stderr, rc = bsu.os_execute_command(cmd_buf)
288    result = vf.key_value_outbuf_to_dict(stdout, process_indent=1, strip=" \t")
289
290    return result
291
292
293def to_json_ordered(json_str):
294    r"""
295    Parse the JSON string data and return an ordered JSON dictionary object.
296
297    Description of argument(s):
298    json_str                        The string containing the JSON data.
299    """
300
301    try:
302        return json.loads(json_str, object_pairs_hook=DotDict)
303    except TypeError:
304        return json.loads(json_str.decode("utf-8"), object_pairs_hook=DotDict)
305