blob: 0dd7f5b50b9ffc37e180af904ab95d7a8848eb17 [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
116 cmd_buf += " -P " + BuiltIn().get_variable_value("${IPMI_PASSWORD}")
117 cmd_buf += " " + BuiltIn().get_variable_value("${HOST}")
118 cmd_buf += " " + BuiltIn().get_variable_value("${OPENBMC_HOST}")
119 cmd_buf += " " + cmd_string
120 rc, stdout, stderr = gc.shell_cmd(cmd_buf,
121 print_output=print_output,
122 ignore_err=ignore_err,
123 return_stderr=1)
124 return stdout, stderr, rc
125
126
127def get_lan_print_dict(ipmi_cmd_type='external'):
Michael Walsh4481b932018-02-08 11:45:15 -0600128 r"""
129 Get IPMI 'lan print' output and return it as a dictionary.
130
131 Here is an example of the IPMI lan print output:
132
133 Set in Progress : Set Complete
134 Auth Type Support : MD5
135 Auth Type Enable : Callback : MD5
136 : User : MD5
137 : Operator : MD5
138 : Admin : MD5
139 : OEM : MD5
140 IP Address Source : Static Address
141 IP Address : x.x.x.x
142 Subnet Mask : x.x.x.x
143 MAC Address : xx:xx:xx:xx:xx:xx
144 Default Gateway IP : x.x.x.x
145 802.1q VLAN ID : Disabled
146 Cipher Suite Priv Max : Not Available
147 Bad Password Threshold : Not Available
148
149 Given that data, this function will return the following dictionary.
150
151 lan_print_dict:
152 [Set in Progress]: Set Complete
153 [Auth Type Support]: MD5
154 [Auth Type Enable]:
155 [Callback]: MD5
156 [User]: MD5
157 [Operator]: MD5
158 [Admin]: MD5
159 [OEM]: MD5
160 [IP Address Source]: Static Address
161 [IP Address]: x.x.x.x
162 [Subnet Mask]: x.x.x.x
163 [MAC Address]: xx:xx:xx:xx:xx:xx
164 [Default Gateway IP]: x.x.x.x
165 [802.1q VLAN ID]: Disabled
166 [Cipher Suite Priv Max]: Not Available
167 [Bad Password Threshold]: Not Available
168
Michael Walsh94811f62018-09-05 14:55:12 -0500169 Description of argument(s):
170 ipmi_cmd_type The type of ipmi command to use (e.g.
171 'inband', 'external').
Michael Walsh4481b932018-02-08 11:45:15 -0600172 """
173
Michael Walsh4481b932018-02-08 11:45:15 -0600174 # Notice in the example of data above that 'Auth Type Enable' needs some
175 # special processing. We essentially want to isolate its data and remove
176 # the 'Auth Type Enable' string so that key_value_outbuf_to_dict can
177 # process it as a sub-dictionary.
Michael Walsh94811f62018-09-05 14:55:12 -0500178 cmd_buf = "lan print | grep -E '^(Auth Type Enable)" +\
Michael Walsh4481b932018-02-08 11:45:15 -0600179 "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'"
Michael Walsh94811f62018-09-05 14:55:12 -0500180 stdout1, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type,
181 print_output=0)
Michael Walsh4481b932018-02-08 11:45:15 -0600182
183 # Now get the remainder of the data and exclude the lines with no field
184 # names (i.e. the 'Auth Type Enable' sub-fields).
Michael Walsh94811f62018-09-05 14:55:12 -0500185 cmd_buf = "lan print | grep -E -v '^[ ]+: '"
186 stdout2, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type,
187 print_output=0)
Michael Walsh4481b932018-02-08 11:45:15 -0600188
189 # Make auth_type_enable_dict sub-dictionary...
190 auth_type_enable_dict = vf.key_value_outbuf_to_dict(stdout1, to_lower=0,
191 underscores=0)
192
193 # Create the lan_print_dict...
194 lan_print_dict = vf.key_value_outbuf_to_dict(stdout2, to_lower=0,
195 underscores=0)
196 # Re-assign 'Auth Type Enable' to contain the auth_type_enable_dict.
197 lan_print_dict['Auth Type Enable'] = auth_type_enable_dict
198
199 return lan_print_dict
Michael Walshd59ed7c2018-02-15 10:19:38 -0600200
201
Michael Walshf4098fb2018-02-28 10:54:46 -0600202def get_ipmi_power_reading(strip_watts=1):
Michael Walshd59ed7c2018-02-15 10:19:38 -0600203 r"""
204 Get IPMI power reading data and return it as a dictionary.
205
206 The data is obtained by issuing the IPMI "power reading" command. An
207 example is shown below:
208
209 Instantaneous power reading: 234 Watts
210 Minimum during sampling period: 234 Watts
211 Maximum during sampling period: 234 Watts
212 Average power reading over sample period: 234 Watts
213 IPMI timestamp: Thu Jan 1 00:00:00 1970
214 Sampling period: 00000000 Seconds.
215 Power reading state is: deactivated
216
217 For the data shown above, the following dictionary will be returned.
218
219 result:
220 [instantaneous_power_reading]: 238 Watts
221 [minimum_during_sampling_period]: 238 Watts
222 [maximum_during_sampling_period]: 238 Watts
223 [average_power_reading_over_sample_period]: 238 Watts
224 [ipmi_timestamp]: Thu Jan 1 00:00:00 1970
225 [sampling_period]: 00000000 Seconds.
226 [power_reading_state_is]: deactivated
Michael Walshf4098fb2018-02-28 10:54:46 -0600227
228 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500229 strip_watts Strip all dictionary values of the
230 trailing " Watts" substring.
Michael Walshd59ed7c2018-02-15 10:19:38 -0600231 """
232
233 status, ret_values = \
234 grk.run_key_u("Run IPMI Standard Command dcmi power reading")
235 result = vf.key_value_outbuf_to_dict(ret_values)
236
Michael Walshf4098fb2018-02-28 10:54:46 -0600237 if strip_watts:
238 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items())
239
Michael Walshd59ed7c2018-02-15 10:19:38 -0600240 return result
Michael Walshaf5607e2018-02-19 17:37:20 -0600241
242
243def get_mc_info():
Michael Walshaf5607e2018-02-19 17:37:20 -0600244 r"""
245 Get IPMI mc info data and return it as a dictionary.
246
247 The data is obtained by issuing the IPMI "mc info" command. An
248 example is shown below:
249
250 Device ID : 0
251 Device Revision : 0
252 Firmware Revision : 2.01
253 IPMI Version : 2.0
254 Manufacturer ID : 42817
255 Manufacturer Name : Unknown (0xA741)
256 Product ID : 16975 (0x424f)
257 Product Name : Unknown (0x424F)
258 Device Available : yes
259 Provides Device SDRs : yes
260 Additional Device Support :
261 Sensor Device
262 SEL Device
263 FRU Inventory Device
264 Chassis Device
265 Aux Firmware Rev Info :
266 0x00
267 0x00
268 0x00
269 0x00
270
271 For the data shown above, the following dictionary will be returned.
272 mc_info:
273 [device_id]: 0
274 [device_revision]: 0
275 [firmware_revision]: 2.01
276 [ipmi_version]: 2.0
277 [manufacturer_id]: 42817
278 [manufacturer_name]: Unknown (0xA741)
279 [product_id]: 16975 (0x424f)
280 [product_name]: Unknown (0x424F)
281 [device_available]: yes
282 [provides_device_sdrs]: yes
283 [additional_device_support]:
284 [additional_device_support][0]: Sensor Device
285 [additional_device_support][1]: SEL Device
286 [additional_device_support][2]: FRU Inventory Device
287 [additional_device_support][3]: Chassis Device
288 [aux_firmware_rev_info]:
289 [aux_firmware_rev_info][0]: 0x00
290 [aux_firmware_rev_info][1]: 0x00
291 [aux_firmware_rev_info][2]: 0x00
292 [aux_firmware_rev_info][3]: 0x00
293 """
294
295 status, ret_values = \
296 grk.run_key_u("Run IPMI Standard Command mc info")
297 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
298
299 return result
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500300
301
302def get_sdr_info():
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500303 r"""
304 Get IPMI sdr info data and return it as a dictionary.
305
306 The data is obtained by issuing the IPMI "sdr info" command. An
307 example is shown below:
308
309 SDR Version : 0x51
310 Record Count : 216
311 Free Space : unspecified
312 Most recent Addition :
313 Most recent Erase :
314 SDR overflow : no
315 SDR Repository Update Support : unspecified
316 Delete SDR supported : no
317 Partial Add SDR supported : no
318 Reserve SDR repository supported : no
319 SDR Repository Alloc info supported : no
320
321 For the data shown above, the following dictionary will be returned.
322 mc_info:
323
324 [sdr_version]: 0x51
325 [record_Count]: 216
326 [free_space]: unspecified
327 [most_recent_addition]:
328 [most_recent_erase]:
329 [sdr_overflow]: no
330 [sdr_repository_update_support]: unspecified
331 [delete_sdr_supported]: no
332 [partial_add_sdr_supported]: no
333 [reserve_sdr_repository_supported]: no
334 [sdr_repository_alloc_info_supported]: no
335 """
336
337 status, ret_values = \
338 grk.run_key_u("Run IPMI Standard Command sdr info")
339 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1)
340
341 return result
George Keishing3511a3f2018-04-19 10:38:30 -0500342
343
344def get_aux_version(version_id):
345 r"""
346 Get IPMI Aux version info data and return it.
347
348 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500349 version_id The data is obtained by from BMC
350 /etc/os-release
351 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585").
George Keishing3511a3f2018-04-19 10:38:30 -0500352
353 In the prior example, the 3rd field is "438" is the commit version and
354 the 5th field is "r3" and value "3" is the release version.
355
356 Aux version return from this function 4380003.
357 """
358
359 # Commit version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500360 count = re.findall("-(\\d{1,4})-", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500361
362 # Release version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500363 release = re.findall("-r(\\d{1,4})", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500364 if release:
365 aux_version = count[0] + "{0:0>4}".format(release[0])
366 else:
367 aux_version = count[0] + "0000"
368
369 return aux_version
Michael Walsh27b14a62018-05-24 11:05:07 -0500370
371
372def get_fru_info():
373 r"""
374 Get fru info and return it as a list of dictionaries.
375
376 The data is obtained by issuing the IPMI "fru print -N 50" command. An
377 example is shown below:
378
379 FRU Device Description : Builtin FRU Device (ID 0)
380 Device not present (Unspecified error)
381
382 FRU Device Description : cpu0 (ID 1)
383 Board Mfg Date : Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500384 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500385 Board Product : PROCESSOR MODULE
386 Board Serial : YA1934315964
387 Board Part Number : 02CY209
388
389 FRU Device Description : cpu1 (ID 2)
390 Board Mfg Date : Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500391 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500392 Board Product : PROCESSOR MODULE
393 Board Serial : YA1934315965
394 Board Part Number : 02CY209
395
396 For the data shown above, the following list of dictionaries will be
397 returned.
398
399 fru_obj:
400 fru_obj[0]:
401 [fru_device_description]: Builtin FRU Device (ID 0)
402 [state]: Device not present (Unspecified error)
403 fru_obj[1]:
404 [fru_device_description]: cpu0 (ID 1)
405 [board_mfg_date]: Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500406 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500407 [board_product]: PROCESSOR MODULE
408 [board_serial]: YA1934315964
409 [board_part_number]: 02CY209
410 fru_obj[2]:
411 [fru_device_description]: cpu1 (ID 2)
412 [board_mfg_date]: Sun Dec 31 18:00:00 1995
George Keishinge0a81282018-06-08 10:02:30 -0500413 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500414 [board_product]: PROCESSOR MODULE
415 [board_serial]: YA1934315965
416 [board_part_number]: 02CY209
417 """
418
419 status, ret_values = \
420 grk.run_key_u("Run IPMI Standard Command fru print -N 50")
421
422 # Manipulate the "Device not present" line to create a "state" key.
423 ret_values = re.sub("Device not present", "state : Device not present",
424 ret_values)
425
426 return [vf.key_value_outbuf_to_dict(x) for x in re.split("\n\n",
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500427 ret_values)]
Michael Walsh61224e62018-05-30 17:58:42 -0500428
429
Michael Walsha95e4ef2018-06-06 17:53:04 -0500430def get_component_fru_info(component='cpu',
431 fru_objs=None):
Michael Walsh61224e62018-05-30 17:58:42 -0500432 r"""
433 Get fru info for the given component and return it as a list of
434 dictionaries.
435
436 This function calls upon get_fru_info and then filters out the unwanted
437 entries. See get_fru_info's prolog for a layout of the data.
438
439 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500440 component The component (e.g. "cpu", "dimm", etc.).
441 fru_objs A fru_objs list such as the one returned
442 by get_fru_info. If this is None, then
443 this function will call get_fru_info to
444 obtain such a list.
445 Supplying this argument may improve
446 performance if this function is to be
447 called multiple times.
Michael Walsh61224e62018-05-30 17:58:42 -0500448 """
449
Michael Walsha95e4ef2018-06-06 17:53:04 -0500450 if fru_objs is None:
451 fru_objs = get_fru_info()
Michael Walsh61224e62018-05-30 17:58:42 -0500452 return\
453 [x for x in fru_objs
454 if re.match(component + '([0-9]+)? ', x['fru_device_description'])]