blob: 7c474a90c53cfa888a986af7adbcd4bcc1b89c2f [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 Walsh19e70c82019-01-23 11:07:15 -060015import ipmi_client as ic
Michael Walshed5b46e2017-05-24 11:49:14 -050016import tempfile
17gru.my_import_resource("ipmi_client.robot")
Michael Walsh4481b932018-02-08 11:45:15 -060018from robot.libraries.BuiltIn import BuiltIn
Michael Walshed5b46e2017-05-24 11:49:14 -050019
20
Michael Walshed5b46e2017-05-24 11:49:14 -050021def get_sol_info():
Michael Walshed5b46e2017-05-24 11:49:14 -050022 r"""
23 Get all SOL info and return it as a dictionary.
24
25 Example use:
26
27 Robot code:
28 ${sol_info}= get_sol_info
29 Rpvars sol_info
30
31 Output:
32 sol_info:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050033 sol_info[Info]: SOL parameter 'Payload Channel (7)'
34 not supported - defaulting to 0x0e
Michael Walshed5b46e2017-05-24 11:49:14 -050035 sol_info[Character Send Threshold]: 1
36 sol_info[Force Authentication]: true
37 sol_info[Privilege Level]: USER
38 sol_info[Set in progress]: set-complete
39 sol_info[Retry Interval (ms)]: 100
40 sol_info[Non-Volatile Bit Rate (kbps)]: IPMI-Over-Serial-Setting
41 sol_info[Character Accumulate Level (ms)]: 100
42 sol_info[Enabled]: true
43 sol_info[Volatile Bit Rate (kbps)]: IPMI-Over-Serial-Setting
44 sol_info[Payload Channel]: 14 (0x0e)
45 sol_info[Payload Port]: 623
46 sol_info[Force Encryption]: true
47 sol_info[Retry Count]: 7
48 """
49
50 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol info")
51
52 # Create temp file path.
53 temp = tempfile.NamedTemporaryFile()
54 temp_file_path = temp.name
55
56 # Write sol info to temp file path.
57 text_file = open(temp_file_path, "w")
58 text_file.write(ret_values)
59 text_file.close()
60
61 # Use my_parm_file to interpret data.
62 sol_info = gm.my_parm_file(temp_file_path)
63
64 return sol_info
65
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050066
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050067def set_sol_setting(setting_name, setting_value):
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050068 r"""
69 Set SOL setting with given value.
70
71 # Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050072 # setting_name SOL setting which needs to be set (e.g.
73 # "retry-count").
74 # setting_value Value which needs to be set (e.g. "7").
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050075 """
76
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050077 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol set "
78 + setting_name + " " + setting_value)
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050079
80 return status
81
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050082
Michael Walsh94811f62018-09-05 14:55:12 -050083def execute_ipmi_cmd(cmd_string,
84 ipmi_cmd_type='inband',
85 print_output=1,
Michael Walsh19e70c82019-01-23 11:07:15 -060086 ignore_err=0,
87 **options):
Michael Walsh94811f62018-09-05 14:55:12 -050088 r"""
89 Run the given command string as an IPMI command and return the stdout,
90 stderr and the return code.
91
92 Description of argument(s):
93 cmd_string The command string to be run as an IPMI
94 command.
95 ipmi_cmd_type 'inband' or 'external'.
96 print_output If this is set, this function will print
97 the stdout/stderr generated by
98 the IPMI command.
99 ignore_err Ignore error means that a failure
100 encountered by running the command
101 string will not be raised as a python
102 exception.
Michael Walsh19e70c82019-01-23 11:07:15 -0600103 options These are passed directly to the
104 create_ipmi_ext_command_string function.
105 See that function's prolog for details.
Michael Walsh94811f62018-09-05 14:55:12 -0500106 """
107
108 if ipmi_cmd_type == 'inband':
109 IPMI_INBAND_CMD = BuiltIn().get_variable_value("${IPMI_INBAND_CMD}")
110 cmd_buf = IPMI_INBAND_CMD + " " + cmd_string
111 return bsu.os_execute_command(cmd_buf,
112 print_out=print_output,
113 ignore_err=ignore_err)
114
115 if ipmi_cmd_type == 'external':
Michael Walsh19e70c82019-01-23 11:07:15 -0600116 cmd_buf = ic.create_ipmi_ext_command_string(cmd_string, **options)
Michael Walsh94811f62018-09-05 14:55:12 -0500117 rc, stdout, stderr = gc.shell_cmd(cmd_buf,
118 print_output=print_output,
119 ignore_err=ignore_err,
120 return_stderr=1)
121 return stdout, stderr, rc
122
123
Tony Leeea741302019-11-08 11:01:58 +0800124def get_lan_print_dict(channel_number='', ipmi_cmd_type='external'):
Michael Walsh4481b932018-02-08 11:45:15 -0600125 r"""
126 Get IPMI 'lan print' output and return it as a dictionary.
127
128 Here is an example of the IPMI lan print output:
129
130 Set in Progress : Set Complete
131 Auth Type Support : MD5
132 Auth Type Enable : Callback : MD5
133 : User : MD5
134 : Operator : MD5
135 : Admin : MD5
136 : OEM : MD5
137 IP Address Source : Static Address
138 IP Address : x.x.x.x
139 Subnet Mask : x.x.x.x
140 MAC Address : xx:xx:xx:xx:xx:xx
141 Default Gateway IP : x.x.x.x
142 802.1q VLAN ID : Disabled
143 Cipher Suite Priv Max : Not Available
144 Bad Password Threshold : Not Available
145
146 Given that data, this function will return the following dictionary.
147
148 lan_print_dict:
149 [Set in Progress]: Set Complete
150 [Auth Type Support]: MD5
151 [Auth Type Enable]:
152 [Callback]: MD5
153 [User]: MD5
154 [Operator]: MD5
155 [Admin]: MD5
156 [OEM]: MD5
157 [IP Address Source]: Static Address
158 [IP Address]: x.x.x.x
159 [Subnet Mask]: x.x.x.x
160 [MAC Address]: xx:xx:xx:xx:xx:xx
161 [Default Gateway IP]: x.x.x.x
162 [802.1q VLAN ID]: Disabled
163 [Cipher Suite Priv Max]: Not Available
164 [Bad Password Threshold]: Not Available
165
Michael Walsh94811f62018-09-05 14:55:12 -0500166 Description of argument(s):
167 ipmi_cmd_type The type of ipmi command to use (e.g.
168 'inband', 'external').
Michael Walsh4481b932018-02-08 11:45:15 -0600169 """
170
Tony Leeea741302019-11-08 11:01:58 +0800171 channel_number = str(channel_number)
Michael Walsh4481b932018-02-08 11:45:15 -0600172 # Notice in the example of data above that 'Auth Type Enable' needs some
173 # special processing. We essentially want to isolate its data and remove
174 # the 'Auth Type Enable' string so that key_value_outbuf_to_dict can
175 # process it as a sub-dictionary.
Tony Leeea741302019-11-08 11:01:58 +0800176 cmd_buf = "lan print " + channel_number + " | grep -E '^(Auth Type Enable)" +\
Michael Walsh4481b932018-02-08 11:45:15 -0600177 "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'"
Michael Walsh94811f62018-09-05 14:55:12 -0500178 stdout1, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type,
179 print_output=0)
Michael Walsh4481b932018-02-08 11:45:15 -0600180
181 # Now get the remainder of the data and exclude the lines with no field
182 # names (i.e. the 'Auth Type Enable' sub-fields).
Tony Leeea741302019-11-08 11:01:58 +0800183 cmd_buf = "lan print " + channel_number + " | grep -E -v '^[ ]+: '"
Michael Walsh94811f62018-09-05 14:55:12 -0500184 stdout2, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type,
185 print_output=0)
Michael Walsh4481b932018-02-08 11:45:15 -0600186
187 # Make auth_type_enable_dict sub-dictionary...
188 auth_type_enable_dict = vf.key_value_outbuf_to_dict(stdout1, to_lower=0,
189 underscores=0)
190
191 # Create the lan_print_dict...
192 lan_print_dict = vf.key_value_outbuf_to_dict(stdout2, to_lower=0,
193 underscores=0)
194 # Re-assign 'Auth Type Enable' to contain the auth_type_enable_dict.
195 lan_print_dict['Auth Type Enable'] = auth_type_enable_dict
196
197 return lan_print_dict
Michael Walshd59ed7c2018-02-15 10:19:38 -0600198
199
Michael Walshf4098fb2018-02-28 10:54:46 -0600200def get_ipmi_power_reading(strip_watts=1):
Michael Walshd59ed7c2018-02-15 10:19:38 -0600201 r"""
202 Get IPMI power reading data and return it as a dictionary.
203
204 The data is obtained by issuing the IPMI "power reading" command. An
205 example is shown below:
206
207 Instantaneous power reading: 234 Watts
208 Minimum during sampling period: 234 Watts
209 Maximum during sampling period: 234 Watts
210 Average power reading over sample period: 234 Watts
211 IPMI timestamp: Thu Jan 1 00:00:00 1970
212 Sampling period: 00000000 Seconds.
213 Power reading state is: deactivated
214
215 For the data shown above, the following dictionary will be returned.
216
217 result:
218 [instantaneous_power_reading]: 238 Watts
219 [minimum_during_sampling_period]: 238 Watts
220 [maximum_during_sampling_period]: 238 Watts
221 [average_power_reading_over_sample_period]: 238 Watts
222 [ipmi_timestamp]: Thu Jan 1 00:00:00 1970
223 [sampling_period]: 00000000 Seconds.
224 [power_reading_state_is]: deactivated
Michael Walshf4098fb2018-02-28 10:54:46 -0600225
226 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500227 strip_watts Strip all dictionary values of the
228 trailing " Watts" substring.
Michael Walshd59ed7c2018-02-15 10:19:38 -0600229 """
230
231 status, ret_values = \
232 grk.run_key_u("Run IPMI Standard Command dcmi power reading")
233 result = vf.key_value_outbuf_to_dict(ret_values)
234
Michael Walshf4098fb2018-02-28 10:54:46 -0600235 if strip_watts:
236 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items())
237
Michael Walshd59ed7c2018-02-15 10:19:38 -0600238 return result
Michael Walshaf5607e2018-02-19 17:37:20 -0600239
240
241def get_mc_info():
Michael Walshaf5607e2018-02-19 17:37:20 -0600242 r"""
243 Get IPMI mc info data and return it as a dictionary.
244
245 The data is obtained by issuing the IPMI "mc info" command. An
246 example is shown below:
247
248 Device ID : 0
249 Device Revision : 0
250 Firmware Revision : 2.01
251 IPMI Version : 2.0
252 Manufacturer ID : 42817
253 Manufacturer Name : Unknown (0xA741)
254 Product ID : 16975 (0x424f)
255 Product Name : Unknown (0x424F)
256 Device Available : yes
257 Provides Device SDRs : yes
258 Additional Device Support :
259 Sensor Device
260 SEL Device
261 FRU Inventory Device
262 Chassis Device
263 Aux Firmware Rev Info :
264 0x00
265 0x00
266 0x00
267 0x00
268
269 For the data shown above, the following dictionary will be returned.
270 mc_info:
271 [device_id]: 0
272 [device_revision]: 0
273 [firmware_revision]: 2.01
274 [ipmi_version]: 2.0
275 [manufacturer_id]: 42817
276 [manufacturer_name]: Unknown (0xA741)
277 [product_id]: 16975 (0x424f)
278 [product_name]: Unknown (0x424F)
279 [device_available]: yes
280 [provides_device_sdrs]: yes
281 [additional_device_support]:
282 [additional_device_support][0]: Sensor Device
283 [additional_device_support][1]: SEL Device
284 [additional_device_support][2]: FRU Inventory Device
285 [additional_device_support][3]: Chassis Device
286 [aux_firmware_rev_info]:
287 [aux_firmware_rev_info][0]: 0x00
288 [aux_firmware_rev_info][1]: 0x00
289 [aux_firmware_rev_info][2]: 0x00
290 [aux_firmware_rev_info][3]: 0x00
291 """
292
293 status, ret_values = \
294 grk.run_key_u("Run IPMI Standard Command mc info")
295 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
296
297 return result
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500298
299
300def get_sdr_info():
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500301 r"""
302 Get IPMI sdr info data and return it as a dictionary.
303
304 The data is obtained by issuing the IPMI "sdr info" command. An
305 example is shown below:
306
307 SDR Version : 0x51
308 Record Count : 216
309 Free Space : unspecified
310 Most recent Addition :
311 Most recent Erase :
312 SDR overflow : no
313 SDR Repository Update Support : unspecified
314 Delete SDR supported : no
315 Partial Add SDR supported : no
316 Reserve SDR repository supported : no
317 SDR Repository Alloc info supported : no
318
319 For the data shown above, the following dictionary will be returned.
320 mc_info:
321
322 [sdr_version]: 0x51
323 [record_Count]: 216
324 [free_space]: unspecified
325 [most_recent_addition]:
326 [most_recent_erase]:
327 [sdr_overflow]: no
328 [sdr_repository_update_support]: unspecified
329 [delete_sdr_supported]: no
330 [partial_add_sdr_supported]: no
331 [reserve_sdr_repository_supported]: no
332 [sdr_repository_alloc_info_supported]: no
333 """
334
335 status, ret_values = \
336 grk.run_key_u("Run IPMI Standard Command sdr info")
337 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
338
339 return result
George Keishing3511a3f2018-04-19 10:38:30 -0500340
341
342def get_aux_version(version_id):
343 r"""
344 Get IPMI Aux version info data and return it.
345
346 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500347 version_id The data is obtained by from BMC
348 /etc/os-release
349 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585").
George Keishing3511a3f2018-04-19 10:38:30 -0500350
351 In the prior example, the 3rd field is "438" is the commit version and
352 the 5th field is "r3" and value "3" is the release version.
353
354 Aux version return from this function 4380003.
355 """
356
357 # Commit version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500358 count = re.findall("-(\\d{1,4})-", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500359
360 # Release version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500361 release = re.findall("-r(\\d{1,4})", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500362 if release:
363 aux_version = count[0] + "{0:0>4}".format(release[0])
364 else:
365 aux_version = count[0] + "0000"
366
367 return aux_version
Michael Walsh27b14a62018-05-24 11:05:07 -0500368
369
370def get_fru_info():
371 r"""
372 Get fru info and return it as a list of dictionaries.
373
374 The data is obtained by issuing the IPMI "fru print -N 50" command. An
375 example is shown below:
376
377 FRU Device Description : Builtin FRU Device (ID 0)
378 Device not present (Unspecified error)
379
380 FRU Device Description : cpu0 (ID 1)
381 Board Mfg Date : Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500382 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500383 Board Product : PROCESSOR MODULE
384 Board Serial : YA1934315964
385 Board Part Number : 02CY209
386
387 FRU Device Description : cpu1 (ID 2)
388 Board Mfg Date : Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500389 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500390 Board Product : PROCESSOR MODULE
391 Board Serial : YA1934315965
392 Board Part Number : 02CY209
393
394 For the data shown above, the following list of dictionaries will be
395 returned.
396
397 fru_obj:
398 fru_obj[0]:
399 [fru_device_description]: Builtin FRU Device (ID 0)
400 [state]: Device not present (Unspecified error)
401 fru_obj[1]:
402 [fru_device_description]: cpu0 (ID 1)
403 [board_mfg_date]: Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500404 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500405 [board_product]: PROCESSOR MODULE
406 [board_serial]: YA1934315964
407 [board_part_number]: 02CY209
408 fru_obj[2]:
409 [fru_device_description]: cpu1 (ID 2)
410 [board_mfg_date]: Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500411 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500412 [board_product]: PROCESSOR MODULE
413 [board_serial]: YA1934315965
414 [board_part_number]: 02CY209
415 """
416
417 status, ret_values = \
418 grk.run_key_u("Run IPMI Standard Command fru print -N 50")
419
420 # Manipulate the "Device not present" line to create a "state" key.
421 ret_values = re.sub("Device not present", "state : Device not present",
422 ret_values)
423
424 return [vf.key_value_outbuf_to_dict(x) for x in re.split("\n\n",
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500425 ret_values)]
Michael Walsh61224e62018-05-30 17:58:42 -0500426
427
Michael Walsha95e4ef2018-06-06 17:53:04 -0500428def get_component_fru_info(component='cpu',
429 fru_objs=None):
Michael Walsh61224e62018-05-30 17:58:42 -0500430 r"""
431 Get fru info for the given component and return it as a list of
432 dictionaries.
433
434 This function calls upon get_fru_info and then filters out the unwanted
435 entries. See get_fru_info's prolog for a layout of the data.
436
437 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500438 component The component (e.g. "cpu", "dimm", etc.).
439 fru_objs A fru_objs list such as the one returned
440 by get_fru_info. If this is None, then
441 this function will call get_fru_info to
442 obtain such a list.
443 Supplying this argument may improve
444 performance if this function is to be
445 called multiple times.
Michael Walsh61224e62018-05-30 17:58:42 -0500446 """
447
Michael Walsha95e4ef2018-06-06 17:53:04 -0500448 if fru_objs is None:
449 fru_objs = get_fru_info()
Michael Walsh61224e62018-05-30 17:58:42 -0500450 return\
451 [x for x in fru_objs
452 if re.match(component + '([0-9]+)? ', x['fru_device_description'])]
Rahul Maheshwari24e0e792019-02-12 22:40:23 -0600453
454
455def get_user_info(userid, channel_number=1):
456 r"""
457 Get user info using channel command and return it as a dictionary.
458
459 Description of argument(s):
460 userid The userid (e.g. "1", "2", etc.).
461 channel_number The user's channel number (e.g. "1").
462
463 The data is obtained by issuing the IPMI "channel getaccess" command. An
464 example is shown below for user id 1 and channel number 1.
465
466 Maximum User IDs : 15
467 Enabled User IDs : 1
468 User ID : 1
469 User Name : root
470 Fixed Name : No
471 Access Available : callback
472 Link Authentication : enabled
473 IPMI Messaging : enabled
474 Privilege Level : ADMINISTRATOR
475 Enable Status : enabled
476
477 For the data shown above, the following dictionary will be returned.
478
479 user_info:
480 [maximum_userids]: 15
481 [enabled_userids: 1
482 [userid] 1
483 [user_name] root
484 [fixed_name] No
485 [access_available] callback
486 [link_authentication] enabled
487 [ipmi_messaging] enabled
488 [privilege_level] ADMINISTRATOR
489 [enable_status] enabled
490
491 """
492
493 status, ret_values = grk.run_key_u("Run IPMI Standard Command channel getaccess "
494 + str(channel_number) + " " + str(userid))
495
496 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
497
498 return result
George Keishing45511e82019-10-15 01:26:55 -0500499
500
501def channel_getciphers_ipmi():
502
503 r"""
504 Run 'channel getciphers ipmi' command and return the result as a list of dictionaries.
505
506 Example robot code:
507 ${ipmi_channel_ciphers}= Channel Getciphers IPMI
508 Rprint Vars ipmi_channel_ciphers
509
510 Example output:
511 ipmi_channel_ciphers:
512 [0]:
513 [id]: 3
514 [iana]: N/A
515 [auth_alg]: hmac_sha1
516 [integrity_alg]: hmac_sha1_96
517 [confidentiality_alg]: aes_cbc_128
518 [1]:
519 [id]: 17
520 [iana]: N/A
521 [auth_alg]: hmac_sha256
522 [integrity_alg]: sha256_128
523 [confidentiality_alg]: aes_cbc_128
524 """
525
526 cmd_buf = "channel getciphers ipmi | sed -re 's/ Alg/_Alg/g'"
527 stdout, stderr, rc = execute_ipmi_cmd(cmd_buf, "external", print_output=0)
528 return vf.outbuf_to_report(stdout)