1#!/usr/bin/env python
2
3r"""
4Provide useful ipmi functions.
5"""
6
7import re
8import gen_print as gp
9import gen_misc as gm
10import gen_robot_keyword as grk
11import gen_robot_utils as gru
12import bmc_ssh_utils as bsu
13import var_funcs as vf
14import tempfile
15gru.my_import_resource("ipmi_client.robot")
16from robot.libraries.BuiltIn import BuiltIn
17
18
19def get_sol_info():
20    r"""
21    Get all SOL info and return it as a dictionary.
22
23    Example use:
24
25    Robot code:
26    ${sol_info}=  get_sol_info
27    Rpvars  sol_info
28
29    Output:
30    sol_info:
31      sol_info[Info]:                                SOL parameter 'Payload Channel (7)'
32                                                     not supported - defaulting to 0x0e
33      sol_info[Character Send Threshold]:            1
34      sol_info[Force Authentication]:                true
35      sol_info[Privilege Level]:                     USER
36      sol_info[Set in progress]:                     set-complete
37      sol_info[Retry Interval (ms)]:                 100
38      sol_info[Non-Volatile Bit Rate (kbps)]:        IPMI-Over-Serial-Setting
39      sol_info[Character Accumulate Level (ms)]:     100
40      sol_info[Enabled]:                             true
41      sol_info[Volatile Bit Rate (kbps)]:            IPMI-Over-Serial-Setting
42      sol_info[Payload Channel]:                     14 (0x0e)
43      sol_info[Payload Port]:                        623
44      sol_info[Force Encryption]:                    true
45      sol_info[Retry Count]:                         7
46    """
47
48    status, ret_values = grk.run_key_u("Run IPMI Standard Command  sol info")
49
50    # Create temp file path.
51    temp = tempfile.NamedTemporaryFile()
52    temp_file_path = temp.name
53
54    # Write sol info to temp file path.
55    text_file = open(temp_file_path, "w")
56    text_file.write(ret_values)
57    text_file.close()
58
59    # Use my_parm_file to interpret data.
60    sol_info = gm.my_parm_file(temp_file_path)
61
62    return sol_info
63
64
65def set_sol_setting(setting_name, setting_value):
66    r"""
67    Set SOL setting with given value.
68
69    # Description of argument(s):
70    # setting_name                  SOL setting which needs to be set (e.g.
71    #                               "retry-count").
72    # setting_value                 Value which needs to be set (e.g. "7").
73    """
74
75    status, ret_values = grk.run_key_u("Run IPMI Standard Command  sol set "
76                                       + setting_name + " " + setting_value)
77
78    return status
79
80
81def get_lan_print_dict():
82    r"""
83    Get IPMI 'lan print' output and return it as a dictionary.
84
85    Here is an example of the IPMI lan print output:
86
87    Set in Progress         : Set Complete
88    Auth Type Support       : MD5
89    Auth Type Enable        : Callback : MD5
90                            : User     : MD5
91                            : Operator : MD5
92                            : Admin    : MD5
93                            : OEM      : MD5
94    IP Address Source       : Static Address
95    IP Address              : x.x.x.x
96    Subnet Mask             : x.x.x.x
97    MAC Address             : xx:xx:xx:xx:xx:xx
98    Default Gateway IP      : x.x.x.x
99    802.1q VLAN ID          : Disabled
100    Cipher Suite Priv Max   : Not Available
101    Bad Password Threshold  : Not Available
102
103    Given that data, this function will return the following dictionary.
104
105    lan_print_dict:
106      [Set in Progress]:                              Set Complete
107      [Auth Type Support]:                            MD5
108      [Auth Type Enable]:
109        [Callback]:                                   MD5
110        [User]:                                       MD5
111        [Operator]:                                   MD5
112        [Admin]:                                      MD5
113        [OEM]:                                        MD5
114      [IP Address Source]:                            Static Address
115      [IP Address]:                                   x.x.x.x
116      [Subnet Mask]:                                  x.x.x.x
117      [MAC Address]:                                  xx:xx:xx:xx:xx:xx
118      [Default Gateway IP]:                           x.x.x.x
119      [802.1q VLAN ID]:                               Disabled
120      [Cipher Suite Priv Max]:                        Not Available
121      [Bad Password Threshold]:                       Not Available
122
123    """
124
125    IPMI_INBAND_CMD = BuiltIn().get_variable_value("${IPMI_INBAND_CMD}")
126
127    # Notice in the example of data above that 'Auth Type Enable' needs some
128    # special processing.  We essentially want to isolate its data and remove
129    # the 'Auth Type Enable' string so that key_value_outbuf_to_dict can
130    # process it as a sub-dictionary.
131    cmd_buf = IPMI_INBAND_CMD + " lan print | grep -E '^(Auth Type Enable)" +\
132        "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'"
133    stdout1, stderr, rc = bsu.os_execute_command(cmd_buf)
134
135    # Now get the remainder of the data and exclude the lines with no field
136    # names (i.e. the 'Auth Type Enable' sub-fields).
137    cmd_buf = IPMI_INBAND_CMD + " lan print | grep -E -v '^[ ]+: '"
138    stdout2, stderr, rc = bsu.os_execute_command(cmd_buf)
139
140    # Make auth_type_enable_dict sub-dictionary...
141    auth_type_enable_dict = vf.key_value_outbuf_to_dict(stdout1, to_lower=0,
142                                                        underscores=0)
143
144    # Create the lan_print_dict...
145    lan_print_dict = vf.key_value_outbuf_to_dict(stdout2, to_lower=0,
146                                                 underscores=0)
147    # Re-assign 'Auth Type Enable' to contain the auth_type_enable_dict.
148    lan_print_dict['Auth Type Enable'] = auth_type_enable_dict
149
150    return lan_print_dict
151
152
153def get_ipmi_power_reading(strip_watts=1):
154    r"""
155    Get IPMI power reading data and return it as a dictionary.
156
157    The data is obtained by issuing the IPMI "power reading" command.  An
158    example is shown below:
159
160    Instantaneous power reading:                   234 Watts
161    Minimum during sampling period:                234 Watts
162    Maximum during sampling period:                234 Watts
163    Average power reading over sample period:      234 Watts
164    IPMI timestamp:                           Thu Jan  1 00:00:00 1970
165    Sampling period:                          00000000 Seconds.
166    Power reading state is:                   deactivated
167
168    For the data shown above, the following dictionary will be returned.
169
170    result:
171      [instantaneous_power_reading]:              238 Watts
172      [minimum_during_sampling_period]:           238 Watts
173      [maximum_during_sampling_period]:           238 Watts
174      [average_power_reading_over_sample_period]: 238 Watts
175      [ipmi_timestamp]:                           Thu Jan  1 00:00:00 1970
176      [sampling_period]:                          00000000 Seconds.
177      [power_reading_state_is]:                   deactivated
178
179    Description of argument(s):
180    strip_watts                     Strip all dictionary values of the
181                                    trailing " Watts" substring.
182    """
183
184    status, ret_values = \
185        grk.run_key_u("Run IPMI Standard Command  dcmi power reading")
186    result = vf.key_value_outbuf_to_dict(ret_values)
187
188    if strip_watts:
189        result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items())
190
191    return result
192
193
194def get_mc_info():
195    r"""
196    Get IPMI mc info data and return it as a dictionary.
197
198    The data is obtained by issuing the IPMI "mc info" command.  An
199    example is shown below:
200
201    Device ID                 : 0
202    Device Revision           : 0
203    Firmware Revision         : 2.01
204    IPMI Version              : 2.0
205    Manufacturer ID           : 42817
206    Manufacturer Name         : Unknown (0xA741)
207    Product ID                : 16975 (0x424f)
208    Product Name              : Unknown (0x424F)
209    Device Available          : yes
210    Provides Device SDRs      : yes
211    Additional Device Support :
212        Sensor Device
213        SEL Device
214        FRU Inventory Device
215        Chassis Device
216    Aux Firmware Rev Info     :
217        0x00
218        0x00
219        0x00
220        0x00
221
222    For the data shown above, the following dictionary will be returned.
223    mc_info:
224      [device_id]:                       0
225      [device_revision]:                 0
226      [firmware_revision]:               2.01
227      [ipmi_version]:                    2.0
228      [manufacturer_id]:                 42817
229      [manufacturer_name]:               Unknown (0xA741)
230      [product_id]:                      16975 (0x424f)
231      [product_name]:                    Unknown (0x424F)
232      [device_available]:                yes
233      [provides_device_sdrs]:            yes
234      [additional_device_support]:
235        [additional_device_support][0]:  Sensor Device
236        [additional_device_support][1]:  SEL Device
237        [additional_device_support][2]:  FRU Inventory Device
238        [additional_device_support][3]:  Chassis Device
239      [aux_firmware_rev_info]:
240        [aux_firmware_rev_info][0]:      0x00
241        [aux_firmware_rev_info][1]:      0x00
242        [aux_firmware_rev_info][2]:      0x00
243        [aux_firmware_rev_info][3]:      0x00
244    """
245
246    status, ret_values = \
247        grk.run_key_u("Run IPMI Standard Command  mc info")
248    result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
249
250    return result
251
252
253def get_sdr_info():
254    r"""
255    Get IPMI sdr info data and return it as a dictionary.
256
257    The data is obtained by issuing the IPMI "sdr info" command.  An
258    example is shown below:
259
260    SDR Version                         : 0x51
261    Record Count                        : 216
262    Free Space                          : unspecified
263    Most recent Addition                :
264    Most recent Erase                   :
265    SDR overflow                        : no
266    SDR Repository Update Support       : unspecified
267    Delete SDR supported                : no
268    Partial Add SDR supported           : no
269    Reserve SDR repository supported    : no
270    SDR Repository Alloc info supported : no
271
272    For the data shown above, the following dictionary will be returned.
273    mc_info:
274
275      [sdr_version]:                         0x51
276      [record_Count]:                        216
277      [free_space]:                          unspecified
278      [most_recent_addition]:
279      [most_recent_erase]:
280      [sdr_overflow]:                        no
281      [sdr_repository_update_support]:       unspecified
282      [delete_sdr_supported]:                no
283      [partial_add_sdr_supported]:           no
284      [reserve_sdr_repository_supported]:    no
285      [sdr_repository_alloc_info_supported]: no
286    """
287
288    status, ret_values = \
289        grk.run_key_u("Run IPMI Standard Command  sdr info")
290    result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
291
292    return result
293
294
295def get_aux_version(version_id):
296    r"""
297    Get IPMI Aux version info data and return it.
298
299    Description of argument(s):
300    version_id                      The data is obtained by from BMC
301                                    /etc/os-release
302                                    (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585").
303
304    In the prior example, the 3rd field is "438" is the commit version and
305    the 5th field is "r3" and value "3" is the release version.
306
307    Aux version return from this function 4380003.
308    """
309
310    # Commit version.
311    count = re.findall("-(\\d{1,4})-", version_id)
312
313    # Release version.
314    release = re.findall("-r(\\d{1,4})", version_id)
315    if release:
316        aux_version = count[0] + "{0:0>4}".format(release[0])
317    else:
318        aux_version = count[0] + "0000"
319
320    return aux_version
321
322
323def get_fru_info():
324    r"""
325    Get fru info and return it as a list of dictionaries.
326
327    The data is obtained by issuing the IPMI "fru print -N 50" command.  An
328    example is shown below:
329
330    FRU Device Description : Builtin FRU Device (ID 0)
331     Device not present (Unspecified error)
332
333    FRU Device Description : cpu0 (ID 1)
334     Board Mfg Date        : Sun Dec 31 18:00:00 1995
335     Board Mfg             : <Manufacturer Name>
336     Board Product         : PROCESSOR MODULE
337     Board Serial          : YA1934315964
338     Board Part Number     : 02CY209
339
340    FRU Device Description : cpu1 (ID 2)
341     Board Mfg Date        : Sun Dec 31 18:00:00 1995
342     Board Mfg             : <Manufacturer Name>
343     Board Product         : PROCESSOR MODULE
344     Board Serial          : YA1934315965
345     Board Part Number     : 02CY209
346
347    For the data shown above, the following list of dictionaries will be
348    returned.
349
350    fru_obj:
351      fru_obj[0]:
352        [fru_device_description]:  Builtin FRU Device (ID 0)
353        [state]:                   Device not present (Unspecified error)
354      fru_obj[1]:
355        [fru_device_description]:  cpu0 (ID 1)
356        [board_mfg_date]:          Sun Dec 31 18:00:00 1995
357        [board_mfg]:               <Manufacturer Name>
358        [board_product]:           PROCESSOR MODULE
359        [board_serial]:            YA1934315964
360        [board_part_number]:       02CY209
361      fru_obj[2]:
362        [fru_device_description]:  cpu1 (ID 2)
363        [board_mfg_date]:          Sun Dec 31 18:00:00 1995
364        [board_mfg]:               <Manufacturer Name>
365        [board_product]:           PROCESSOR MODULE
366        [board_serial]:            YA1934315965
367        [board_part_number]:       02CY209
368    """
369
370    status, ret_values = \
371        grk.run_key_u("Run IPMI Standard Command  fru print -N 50")
372
373    # Manipulate the "Device not present" line to create a "state" key.
374    ret_values = re.sub("Device not present", "state : Device not present",
375                        ret_values)
376
377    return [vf.key_value_outbuf_to_dict(x) for x in re.split("\n\n",
378                                                             ret_values)]
379
380
381def get_component_fru_info(component='cpu',
382                           fru_objs=None):
383    r"""
384    Get fru info for the given component and return it as a list of
385    dictionaries.
386
387    This function calls upon get_fru_info and then filters out the unwanted
388    entries.  See get_fru_info's prolog for a layout of the data.
389
390    Description of argument(s):
391    component                       The component (e.g. "cpu", "dimm", etc.).
392    fru_objs                        A fru_objs list such as the one returned
393                                    by get_fru_info.  If this is None, then
394                                    this function will call get_fru_info to
395                                    obtain such a list.
396                                    Supplying this argument may improve
397                                    performance if this function is to be
398                                    called multiple times.
399    """
400
401    if fru_objs is None:
402        fru_objs = get_fru_info()
403    return\
404        [x for x in fru_objs
405         if re.match(component + '([0-9]+)? ', x['fru_device_description'])]
406