blob: e182e3ec66e557fcd92231689fb29daa245f2bb6 [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
124def get_lan_print_dict(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
Michael Walsh4481b932018-02-08 11:45:15 -0600171 # Notice in the example of data above that 'Auth Type Enable' needs some
172 # special processing. We essentially want to isolate its data and remove
173 # the 'Auth Type Enable' string so that key_value_outbuf_to_dict can
174 # process it as a sub-dictionary.
Michael Walsh94811f62018-09-05 14:55:12 -0500175 cmd_buf = "lan print | grep -E '^(Auth Type Enable)" +\
Michael Walsh4481b932018-02-08 11:45:15 -0600176 "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'"
Michael Walsh94811f62018-09-05 14:55:12 -0500177 stdout1, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type,
178 print_output=0)
Michael Walsh4481b932018-02-08 11:45:15 -0600179
180 # Now get the remainder of the data and exclude the lines with no field
181 # names (i.e. the 'Auth Type Enable' sub-fields).
Michael Walsh94811f62018-09-05 14:55:12 -0500182 cmd_buf = "lan print | grep -E -v '^[ ]+: '"
183 stdout2, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type,
184 print_output=0)
Michael Walsh4481b932018-02-08 11:45:15 -0600185
186 # Make auth_type_enable_dict sub-dictionary...
187 auth_type_enable_dict = vf.key_value_outbuf_to_dict(stdout1, to_lower=0,
188 underscores=0)
189
190 # Create the lan_print_dict...
191 lan_print_dict = vf.key_value_outbuf_to_dict(stdout2, to_lower=0,
192 underscores=0)
193 # Re-assign 'Auth Type Enable' to contain the auth_type_enable_dict.
194 lan_print_dict['Auth Type Enable'] = auth_type_enable_dict
195
196 return lan_print_dict
Michael Walshd59ed7c2018-02-15 10:19:38 -0600197
198
Michael Walshf4098fb2018-02-28 10:54:46 -0600199def get_ipmi_power_reading(strip_watts=1):
Michael Walshd59ed7c2018-02-15 10:19:38 -0600200 r"""
201 Get IPMI power reading data and return it as a dictionary.
202
203 The data is obtained by issuing the IPMI "power reading" command. An
204 example is shown below:
205
206 Instantaneous power reading: 234 Watts
207 Minimum during sampling period: 234 Watts
208 Maximum during sampling period: 234 Watts
209 Average power reading over sample period: 234 Watts
210 IPMI timestamp: Thu Jan 1 00:00:00 1970
211 Sampling period: 00000000 Seconds.
212 Power reading state is: deactivated
213
214 For the data shown above, the following dictionary will be returned.
215
216 result:
217 [instantaneous_power_reading]: 238 Watts
218 [minimum_during_sampling_period]: 238 Watts
219 [maximum_during_sampling_period]: 238 Watts
220 [average_power_reading_over_sample_period]: 238 Watts
221 [ipmi_timestamp]: Thu Jan 1 00:00:00 1970
222 [sampling_period]: 00000000 Seconds.
223 [power_reading_state_is]: deactivated
Michael Walshf4098fb2018-02-28 10:54:46 -0600224
225 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500226 strip_watts Strip all dictionary values of the
227 trailing " Watts" substring.
Michael Walshd59ed7c2018-02-15 10:19:38 -0600228 """
229
230 status, ret_values = \
231 grk.run_key_u("Run IPMI Standard Command dcmi power reading")
232 result = vf.key_value_outbuf_to_dict(ret_values)
233
Michael Walshf4098fb2018-02-28 10:54:46 -0600234 if strip_watts:
235 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items())
236
Michael Walshd59ed7c2018-02-15 10:19:38 -0600237 return result
Michael Walshaf5607e2018-02-19 17:37:20 -0600238
239
240def get_mc_info():
Michael Walshaf5607e2018-02-19 17:37:20 -0600241 r"""
242 Get IPMI mc info data and return it as a dictionary.
243
244 The data is obtained by issuing the IPMI "mc info" command. An
245 example is shown below:
246
247 Device ID : 0
248 Device Revision : 0
249 Firmware Revision : 2.01
250 IPMI Version : 2.0
251 Manufacturer ID : 42817
252 Manufacturer Name : Unknown (0xA741)
253 Product ID : 16975 (0x424f)
254 Product Name : Unknown (0x424F)
255 Device Available : yes
256 Provides Device SDRs : yes
257 Additional Device Support :
258 Sensor Device
259 SEL Device
260 FRU Inventory Device
261 Chassis Device
262 Aux Firmware Rev Info :
263 0x00
264 0x00
265 0x00
266 0x00
267
268 For the data shown above, the following dictionary will be returned.
269 mc_info:
270 [device_id]: 0
271 [device_revision]: 0
272 [firmware_revision]: 2.01
273 [ipmi_version]: 2.0
274 [manufacturer_id]: 42817
275 [manufacturer_name]: Unknown (0xA741)
276 [product_id]: 16975 (0x424f)
277 [product_name]: Unknown (0x424F)
278 [device_available]: yes
279 [provides_device_sdrs]: yes
280 [additional_device_support]:
281 [additional_device_support][0]: Sensor Device
282 [additional_device_support][1]: SEL Device
283 [additional_device_support][2]: FRU Inventory Device
284 [additional_device_support][3]: Chassis Device
285 [aux_firmware_rev_info]:
286 [aux_firmware_rev_info][0]: 0x00
287 [aux_firmware_rev_info][1]: 0x00
288 [aux_firmware_rev_info][2]: 0x00
289 [aux_firmware_rev_info][3]: 0x00
290 """
291
292 status, ret_values = \
293 grk.run_key_u("Run IPMI Standard Command mc info")
294 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
295
296 return result
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500297
298
299def get_sdr_info():
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500300 r"""
301 Get IPMI sdr info data and return it as a dictionary.
302
303 The data is obtained by issuing the IPMI "sdr info" command. An
304 example is shown below:
305
306 SDR Version : 0x51
307 Record Count : 216
308 Free Space : unspecified
309 Most recent Addition :
310 Most recent Erase :
311 SDR overflow : no
312 SDR Repository Update Support : unspecified
313 Delete SDR supported : no
314 Partial Add SDR supported : no
315 Reserve SDR repository supported : no
316 SDR Repository Alloc info supported : no
317
318 For the data shown above, the following dictionary will be returned.
319 mc_info:
320
321 [sdr_version]: 0x51
322 [record_Count]: 216
323 [free_space]: unspecified
324 [most_recent_addition]:
325 [most_recent_erase]:
326 [sdr_overflow]: no
327 [sdr_repository_update_support]: unspecified
328 [delete_sdr_supported]: no
329 [partial_add_sdr_supported]: no
330 [reserve_sdr_repository_supported]: no
331 [sdr_repository_alloc_info_supported]: no
332 """
333
334 status, ret_values = \
335 grk.run_key_u("Run IPMI Standard Command sdr info")
336 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
337
338 return result
George Keishing3511a3f2018-04-19 10:38:30 -0500339
340
341def get_aux_version(version_id):
342 r"""
343 Get IPMI Aux version info data and return it.
344
345 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500346 version_id The data is obtained by from BMC
347 /etc/os-release
348 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585").
George Keishing3511a3f2018-04-19 10:38:30 -0500349
350 In the prior example, the 3rd field is "438" is the commit version and
351 the 5th field is "r3" and value "3" is the release version.
352
353 Aux version return from this function 4380003.
354 """
355
356 # Commit version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500357 count = re.findall("-(\\d{1,4})-", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500358
359 # Release version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500360 release = re.findall("-r(\\d{1,4})", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500361 if release:
362 aux_version = count[0] + "{0:0>4}".format(release[0])
363 else:
364 aux_version = count[0] + "0000"
365
366 return aux_version
Michael Walsh27b14a62018-05-24 11:05:07 -0500367
368
369def get_fru_info():
370 r"""
371 Get fru info and return it as a list of dictionaries.
372
373 The data is obtained by issuing the IPMI "fru print -N 50" command. An
374 example is shown below:
375
376 FRU Device Description : Builtin FRU Device (ID 0)
377 Device not present (Unspecified error)
378
379 FRU Device Description : cpu0 (ID 1)
380 Board Mfg Date : Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500381 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500382 Board Product : PROCESSOR MODULE
383 Board Serial : YA1934315964
384 Board Part Number : 02CY209
385
386 FRU Device Description : cpu1 (ID 2)
387 Board Mfg Date : Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500388 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500389 Board Product : PROCESSOR MODULE
390 Board Serial : YA1934315965
391 Board Part Number : 02CY209
392
393 For the data shown above, the following list of dictionaries will be
394 returned.
395
396 fru_obj:
397 fru_obj[0]:
398 [fru_device_description]: Builtin FRU Device (ID 0)
399 [state]: Device not present (Unspecified error)
400 fru_obj[1]:
401 [fru_device_description]: cpu0 (ID 1)
402 [board_mfg_date]: Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500403 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500404 [board_product]: PROCESSOR MODULE
405 [board_serial]: YA1934315964
406 [board_part_number]: 02CY209
407 fru_obj[2]:
408 [fru_device_description]: cpu1 (ID 2)
409 [board_mfg_date]: Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500410 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500411 [board_product]: PROCESSOR MODULE
412 [board_serial]: YA1934315965
413 [board_part_number]: 02CY209
414 """
415
416 status, ret_values = \
417 grk.run_key_u("Run IPMI Standard Command fru print -N 50")
418
419 # Manipulate the "Device not present" line to create a "state" key.
420 ret_values = re.sub("Device not present", "state : Device not present",
421 ret_values)
422
423 return [vf.key_value_outbuf_to_dict(x) for x in re.split("\n\n",
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500424 ret_values)]
Michael Walsh61224e62018-05-30 17:58:42 -0500425
426
Michael Walsha95e4ef2018-06-06 17:53:04 -0500427def get_component_fru_info(component='cpu',
428 fru_objs=None):
Michael Walsh61224e62018-05-30 17:58:42 -0500429 r"""
430 Get fru info for the given component and return it as a list of
431 dictionaries.
432
433 This function calls upon get_fru_info and then filters out the unwanted
434 entries. See get_fru_info's prolog for a layout of the data.
435
436 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500437 component The component (e.g. "cpu", "dimm", etc.).
438 fru_objs A fru_objs list such as the one returned
439 by get_fru_info. If this is None, then
440 this function will call get_fru_info to
441 obtain such a list.
442 Supplying this argument may improve
443 performance if this function is to be
444 called multiple times.
Michael Walsh61224e62018-05-30 17:58:42 -0500445 """
446
Michael Walsha95e4ef2018-06-06 17:53:04 -0500447 if fru_objs is None:
448 fru_objs = get_fru_info()
Michael Walsh61224e62018-05-30 17:58:42 -0500449 return\
450 [x for x in fru_objs
451 if re.match(component + '([0-9]+)? ', x['fru_device_description'])]
Rahul Maheshwari24e0e792019-02-12 22:40:23 -0600452
453
454def get_user_info(userid, channel_number=1):
455 r"""
456 Get user info using channel command and return it as a dictionary.
457
458 Description of argument(s):
459 userid The userid (e.g. "1", "2", etc.).
460 channel_number The user's channel number (e.g. "1").
461
462 The data is obtained by issuing the IPMI "channel getaccess" command. An
463 example is shown below for user id 1 and channel number 1.
464
465 Maximum User IDs : 15
466 Enabled User IDs : 1
467 User ID : 1
468 User Name : root
469 Fixed Name : No
470 Access Available : callback
471 Link Authentication : enabled
472 IPMI Messaging : enabled
473 Privilege Level : ADMINISTRATOR
474 Enable Status : enabled
475
476 For the data shown above, the following dictionary will be returned.
477
478 user_info:
479 [maximum_userids]: 15
480 [enabled_userids: 1
481 [userid] 1
482 [user_name] root
483 [fixed_name] No
484 [access_available] callback
485 [link_authentication] enabled
486 [ipmi_messaging] enabled
487 [privilege_level] ADMINISTRATOR
488 [enable_status] enabled
489
490 """
491
492 status, ret_values = grk.run_key_u("Run IPMI Standard Command channel getaccess "
493 + str(channel_number) + " " + str(userid))
494
495 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
496
497 return result