blob: 820064305ef7c63327f1558c83c33147b8466ffb [file] [log] [blame]
Michael Walshed5b46e2017-05-24 11:49:14 -05001#!/usr/bin/env python
2
3r"""
4Provide useful ipmi functions.
5"""
6
Michael Walshf4098fb2018-02-28 10:54:46 -06007import re
Michael Walsh4481b932018-02-08 11:45:15 -06008import gen_print as gp
Michael Walshed5b46e2017-05-24 11:49:14 -05009import gen_misc as gm
Michael Walsh94811f62018-09-05 14:55:12 -050010import gen_cmd as gc
Michael Walshed5b46e2017-05-24 11:49:14 -050011import gen_robot_keyword as grk
12import gen_robot_utils as gru
Michael Walsh4481b932018-02-08 11:45:15 -060013import bmc_ssh_utils as bsu
14import var_funcs as vf
Michael Walshed5b46e2017-05-24 11:49:14 -050015import tempfile
16gru.my_import_resource("ipmi_client.robot")
Michael Walsh4481b932018-02-08 11:45:15 -060017from robot.libraries.BuiltIn import BuiltIn
Michael Walshed5b46e2017-05-24 11:49:14 -050018
19
Michael Walshed5b46e2017-05-24 11:49:14 -050020def get_sol_info():
Michael Walshed5b46e2017-05-24 11:49:14 -050021 r"""
22 Get all SOL info and return it as a dictionary.
23
24 Example use:
25
26 Robot code:
27 ${sol_info}= get_sol_info
28 Rpvars sol_info
29
30 Output:
31 sol_info:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050032 sol_info[Info]: SOL parameter 'Payload Channel (7)'
33 not supported - defaulting to 0x0e
Michael Walshed5b46e2017-05-24 11:49:14 -050034 sol_info[Character Send Threshold]: 1
35 sol_info[Force Authentication]: true
36 sol_info[Privilege Level]: USER
37 sol_info[Set in progress]: set-complete
38 sol_info[Retry Interval (ms)]: 100
39 sol_info[Non-Volatile Bit Rate (kbps)]: IPMI-Over-Serial-Setting
40 sol_info[Character Accumulate Level (ms)]: 100
41 sol_info[Enabled]: true
42 sol_info[Volatile Bit Rate (kbps)]: IPMI-Over-Serial-Setting
43 sol_info[Payload Channel]: 14 (0x0e)
44 sol_info[Payload Port]: 623
45 sol_info[Force Encryption]: true
46 sol_info[Retry Count]: 7
47 """
48
49 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol info")
50
51 # Create temp file path.
52 temp = tempfile.NamedTemporaryFile()
53 temp_file_path = temp.name
54
55 # Write sol info to temp file path.
56 text_file = open(temp_file_path, "w")
57 text_file.write(ret_values)
58 text_file.close()
59
60 # Use my_parm_file to interpret data.
61 sol_info = gm.my_parm_file(temp_file_path)
62
63 return sol_info
64
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050065
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050066def set_sol_setting(setting_name, setting_value):
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050067 r"""
68 Set SOL setting with given value.
69
70 # Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050071 # setting_name SOL setting which needs to be set (e.g.
72 # "retry-count").
73 # setting_value Value which needs to be set (e.g. "7").
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050074 """
75
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050076 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol set "
77 + setting_name + " " + setting_value)
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050078
79 return status
80
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050081
Michael Walsh94811f62018-09-05 14:55:12 -050082def execute_ipmi_cmd(cmd_string,
83 ipmi_cmd_type='inband',
84 print_output=1,
85 ignore_err=0):
86 r"""
87 Run the given command string as an IPMI command and return the stdout,
88 stderr and the return code.
89
90 Description of argument(s):
91 cmd_string The command string to be run as an IPMI
92 command.
93 ipmi_cmd_type 'inband' or 'external'.
94 print_output If this is set, this function will print
95 the stdout/stderr generated by
96 the IPMI command.
97 ignore_err Ignore error means that a failure
98 encountered by running the command
99 string will not be raised as a python
100 exception.
101 """
102
103 if ipmi_cmd_type == 'inband':
104 IPMI_INBAND_CMD = BuiltIn().get_variable_value("${IPMI_INBAND_CMD}")
105 cmd_buf = IPMI_INBAND_CMD + " " + cmd_string
106 return bsu.os_execute_command(cmd_buf,
107 print_out=print_output,
108 ignore_err=ignore_err)
109
110 if ipmi_cmd_type == 'external':
111 cmd_buf = BuiltIn().get_variable_value("${IPMI_EXT_CMD}")
112 IPMI_USER_OPTIONS =\
113 BuiltIn().get_variable_value("${IPMI_USER_OPTIONS}")
114 if IPMI_USER_OPTIONS != "":
115 cmd_buf += " " + IPMI_USER_OPTIONS
Michael Walsh94811f62018-09-05 14:55:12 -0500116 cmd_buf += " " + BuiltIn().get_variable_value("${HOST}")
117 cmd_buf += " " + BuiltIn().get_variable_value("${OPENBMC_HOST}")
118 cmd_buf += " " + cmd_string
119 rc, stdout, stderr = gc.shell_cmd(cmd_buf,
120 print_output=print_output,
121 ignore_err=ignore_err,
122 return_stderr=1)
123 return stdout, stderr, rc
124
125
126def get_lan_print_dict(ipmi_cmd_type='external'):
Michael Walsh4481b932018-02-08 11:45:15 -0600127 r"""
128 Get IPMI 'lan print' output and return it as a dictionary.
129
130 Here is an example of the IPMI lan print output:
131
132 Set in Progress : Set Complete
133 Auth Type Support : MD5
134 Auth Type Enable : Callback : MD5
135 : User : MD5
136 : Operator : MD5
137 : Admin : MD5
138 : OEM : MD5
139 IP Address Source : Static Address
140 IP Address : x.x.x.x
141 Subnet Mask : x.x.x.x
142 MAC Address : xx:xx:xx:xx:xx:xx
143 Default Gateway IP : x.x.x.x
144 802.1q VLAN ID : Disabled
145 Cipher Suite Priv Max : Not Available
146 Bad Password Threshold : Not Available
147
148 Given that data, this function will return the following dictionary.
149
150 lan_print_dict:
151 [Set in Progress]: Set Complete
152 [Auth Type Support]: MD5
153 [Auth Type Enable]:
154 [Callback]: MD5
155 [User]: MD5
156 [Operator]: MD5
157 [Admin]: MD5
158 [OEM]: MD5
159 [IP Address Source]: Static Address
160 [IP Address]: x.x.x.x
161 [Subnet Mask]: x.x.x.x
162 [MAC Address]: xx:xx:xx:xx:xx:xx
163 [Default Gateway IP]: x.x.x.x
164 [802.1q VLAN ID]: Disabled
165 [Cipher Suite Priv Max]: Not Available
166 [Bad Password Threshold]: Not Available
167
Michael Walsh94811f62018-09-05 14:55:12 -0500168 Description of argument(s):
169 ipmi_cmd_type The type of ipmi command to use (e.g.
170 'inband', 'external').
Michael Walsh4481b932018-02-08 11:45:15 -0600171 """
172
Michael Walsh4481b932018-02-08 11:45:15 -0600173 # Notice in the example of data above that 'Auth Type Enable' needs some
174 # special processing. We essentially want to isolate its data and remove
175 # the 'Auth Type Enable' string so that key_value_outbuf_to_dict can
176 # process it as a sub-dictionary.
Michael Walsh94811f62018-09-05 14:55:12 -0500177 cmd_buf = "lan print | grep -E '^(Auth Type Enable)" +\
Michael Walsh4481b932018-02-08 11:45:15 -0600178 "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'"
Michael Walsh94811f62018-09-05 14:55:12 -0500179 stdout1, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type,
180 print_output=0)
Michael Walsh4481b932018-02-08 11:45:15 -0600181
182 # Now get the remainder of the data and exclude the lines with no field
183 # names (i.e. the 'Auth Type Enable' sub-fields).
Michael Walsh94811f62018-09-05 14:55:12 -0500184 cmd_buf = "lan print | grep -E -v '^[ ]+: '"
185 stdout2, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type,
186 print_output=0)
Michael Walsh4481b932018-02-08 11:45:15 -0600187
188 # Make auth_type_enable_dict sub-dictionary...
189 auth_type_enable_dict = vf.key_value_outbuf_to_dict(stdout1, to_lower=0,
190 underscores=0)
191
192 # Create the lan_print_dict...
193 lan_print_dict = vf.key_value_outbuf_to_dict(stdout2, to_lower=0,
194 underscores=0)
195 # Re-assign 'Auth Type Enable' to contain the auth_type_enable_dict.
196 lan_print_dict['Auth Type Enable'] = auth_type_enable_dict
197
198 return lan_print_dict
Michael Walshd59ed7c2018-02-15 10:19:38 -0600199
200
Michael Walshf4098fb2018-02-28 10:54:46 -0600201def get_ipmi_power_reading(strip_watts=1):
Michael Walshd59ed7c2018-02-15 10:19:38 -0600202 r"""
203 Get IPMI power reading data and return it as a dictionary.
204
205 The data is obtained by issuing the IPMI "power reading" command. An
206 example is shown below:
207
208 Instantaneous power reading: 234 Watts
209 Minimum during sampling period: 234 Watts
210 Maximum during sampling period: 234 Watts
211 Average power reading over sample period: 234 Watts
212 IPMI timestamp: Thu Jan 1 00:00:00 1970
213 Sampling period: 00000000 Seconds.
214 Power reading state is: deactivated
215
216 For the data shown above, the following dictionary will be returned.
217
218 result:
219 [instantaneous_power_reading]: 238 Watts
220 [minimum_during_sampling_period]: 238 Watts
221 [maximum_during_sampling_period]: 238 Watts
222 [average_power_reading_over_sample_period]: 238 Watts
223 [ipmi_timestamp]: Thu Jan 1 00:00:00 1970
224 [sampling_period]: 00000000 Seconds.
225 [power_reading_state_is]: deactivated
Michael Walshf4098fb2018-02-28 10:54:46 -0600226
227 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500228 strip_watts Strip all dictionary values of the
229 trailing " Watts" substring.
Michael Walshd59ed7c2018-02-15 10:19:38 -0600230 """
231
232 status, ret_values = \
233 grk.run_key_u("Run IPMI Standard Command dcmi power reading")
234 result = vf.key_value_outbuf_to_dict(ret_values)
235
Michael Walshf4098fb2018-02-28 10:54:46 -0600236 if strip_watts:
237 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items())
238
Michael Walshd59ed7c2018-02-15 10:19:38 -0600239 return result
Michael Walshaf5607e2018-02-19 17:37:20 -0600240
241
242def get_mc_info():
Michael Walshaf5607e2018-02-19 17:37:20 -0600243 r"""
244 Get IPMI mc info data and return it as a dictionary.
245
246 The data is obtained by issuing the IPMI "mc info" command. An
247 example is shown below:
248
249 Device ID : 0
250 Device Revision : 0
251 Firmware Revision : 2.01
252 IPMI Version : 2.0
253 Manufacturer ID : 42817
254 Manufacturer Name : Unknown (0xA741)
255 Product ID : 16975 (0x424f)
256 Product Name : Unknown (0x424F)
257 Device Available : yes
258 Provides Device SDRs : yes
259 Additional Device Support :
260 Sensor Device
261 SEL Device
262 FRU Inventory Device
263 Chassis Device
264 Aux Firmware Rev Info :
265 0x00
266 0x00
267 0x00
268 0x00
269
270 For the data shown above, the following dictionary will be returned.
271 mc_info:
272 [device_id]: 0
273 [device_revision]: 0
274 [firmware_revision]: 2.01
275 [ipmi_version]: 2.0
276 [manufacturer_id]: 42817
277 [manufacturer_name]: Unknown (0xA741)
278 [product_id]: 16975 (0x424f)
279 [product_name]: Unknown (0x424F)
280 [device_available]: yes
281 [provides_device_sdrs]: yes
282 [additional_device_support]:
283 [additional_device_support][0]: Sensor Device
284 [additional_device_support][1]: SEL Device
285 [additional_device_support][2]: FRU Inventory Device
286 [additional_device_support][3]: Chassis Device
287 [aux_firmware_rev_info]:
288 [aux_firmware_rev_info][0]: 0x00
289 [aux_firmware_rev_info][1]: 0x00
290 [aux_firmware_rev_info][2]: 0x00
291 [aux_firmware_rev_info][3]: 0x00
292 """
293
294 status, ret_values = \
295 grk.run_key_u("Run IPMI Standard Command mc info")
296 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
297
298 return result
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500299
300
301def get_sdr_info():
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500302 r"""
303 Get IPMI sdr info data and return it as a dictionary.
304
305 The data is obtained by issuing the IPMI "sdr info" command. An
306 example is shown below:
307
308 SDR Version : 0x51
309 Record Count : 216
310 Free Space : unspecified
311 Most recent Addition :
312 Most recent Erase :
313 SDR overflow : no
314 SDR Repository Update Support : unspecified
315 Delete SDR supported : no
316 Partial Add SDR supported : no
317 Reserve SDR repository supported : no
318 SDR Repository Alloc info supported : no
319
320 For the data shown above, the following dictionary will be returned.
321 mc_info:
322
323 [sdr_version]: 0x51
324 [record_Count]: 216
325 [free_space]: unspecified
326 [most_recent_addition]:
327 [most_recent_erase]:
328 [sdr_overflow]: no
329 [sdr_repository_update_support]: unspecified
330 [delete_sdr_supported]: no
331 [partial_add_sdr_supported]: no
332 [reserve_sdr_repository_supported]: no
333 [sdr_repository_alloc_info_supported]: no
334 """
335
336 status, ret_values = \
337 grk.run_key_u("Run IPMI Standard Command sdr info")
338 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
339
340 return result
George Keishing3511a3f2018-04-19 10:38:30 -0500341
342
343def get_aux_version(version_id):
344 r"""
345 Get IPMI Aux version info data and return it.
346
347 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500348 version_id The data is obtained by from BMC
349 /etc/os-release
350 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585").
George Keishing3511a3f2018-04-19 10:38:30 -0500351
352 In the prior example, the 3rd field is "438" is the commit version and
353 the 5th field is "r3" and value "3" is the release version.
354
355 Aux version return from this function 4380003.
356 """
357
358 # Commit version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500359 count = re.findall("-(\\d{1,4})-", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500360
361 # Release version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500362 release = re.findall("-r(\\d{1,4})", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500363 if release:
364 aux_version = count[0] + "{0:0>4}".format(release[0])
365 else:
366 aux_version = count[0] + "0000"
367
368 return aux_version
Michael Walsh27b14a62018-05-24 11:05:07 -0500369
370
371def get_fru_info():
372 r"""
373 Get fru info and return it as a list of dictionaries.
374
375 The data is obtained by issuing the IPMI "fru print -N 50" command. An
376 example is shown below:
377
378 FRU Device Description : Builtin FRU Device (ID 0)
379 Device not present (Unspecified error)
380
381 FRU Device Description : cpu0 (ID 1)
382 Board Mfg Date : Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500383 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500384 Board Product : PROCESSOR MODULE
385 Board Serial : YA1934315964
386 Board Part Number : 02CY209
387
388 FRU Device Description : cpu1 (ID 2)
389 Board Mfg Date : Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500390 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500391 Board Product : PROCESSOR MODULE
392 Board Serial : YA1934315965
393 Board Part Number : 02CY209
394
395 For the data shown above, the following list of dictionaries will be
396 returned.
397
398 fru_obj:
399 fru_obj[0]:
400 [fru_device_description]: Builtin FRU Device (ID 0)
401 [state]: Device not present (Unspecified error)
402 fru_obj[1]:
403 [fru_device_description]: cpu0 (ID 1)
404 [board_mfg_date]: Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500405 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500406 [board_product]: PROCESSOR MODULE
407 [board_serial]: YA1934315964
408 [board_part_number]: 02CY209
409 fru_obj[2]:
410 [fru_device_description]: cpu1 (ID 2)
411 [board_mfg_date]: Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500412 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500413 [board_product]: PROCESSOR MODULE
414 [board_serial]: YA1934315965
415 [board_part_number]: 02CY209
416 """
417
418 status, ret_values = \
419 grk.run_key_u("Run IPMI Standard Command fru print -N 50")
420
421 # Manipulate the "Device not present" line to create a "state" key.
422 ret_values = re.sub("Device not present", "state : Device not present",
423 ret_values)
424
425 return [vf.key_value_outbuf_to_dict(x) for x in re.split("\n\n",
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500426 ret_values)]
Michael Walsh61224e62018-05-30 17:58:42 -0500427
428
Michael Walsha95e4ef2018-06-06 17:53:04 -0500429def get_component_fru_info(component='cpu',
430 fru_objs=None):
Michael Walsh61224e62018-05-30 17:58:42 -0500431 r"""
432 Get fru info for the given component and return it as a list of
433 dictionaries.
434
435 This function calls upon get_fru_info and then filters out the unwanted
436 entries. See get_fru_info's prolog for a layout of the data.
437
438 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500439 component The component (e.g. "cpu", "dimm", etc.).
440 fru_objs A fru_objs list such as the one returned
441 by get_fru_info. If this is None, then
442 this function will call get_fru_info to
443 obtain such a list.
444 Supplying this argument may improve
445 performance if this function is to be
446 called multiple times.
Michael Walsh61224e62018-05-30 17:58:42 -0500447 """
448
Michael Walsha95e4ef2018-06-06 17:53:04 -0500449 if fru_objs is None:
450 fru_objs = get_fru_info()
Michael Walsh61224e62018-05-30 17:58:42 -0500451 return\
452 [x for x in fru_objs
453 if re.match(component + '([0-9]+)? ', x['fru_device_description'])]