blob: 9f30e290457352ed3333a2e261abd1e1f95e6269 [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
10import gen_robot_keyword as grk
11import gen_robot_utils as gru
Michael Walsh4481b932018-02-08 11:45:15 -060012import bmc_ssh_utils as bsu
13import var_funcs as vf
Michael Walshed5b46e2017-05-24 11:49:14 -050014import tempfile
15gru.my_import_resource("ipmi_client.robot")
Michael Walsh4481b932018-02-08 11:45:15 -060016from robot.libraries.BuiltIn import BuiltIn
Michael Walshed5b46e2017-05-24 11:49:14 -050017
18
Michael Walshed5b46e2017-05-24 11:49:14 -050019def get_sol_info():
Michael Walshed5b46e2017-05-24 11:49:14 -050020 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:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050031 sol_info[Info]: SOL parameter 'Payload Channel (7)'
32 not supported - defaulting to 0x0e
Michael Walshed5b46e2017-05-24 11:49:14 -050033 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
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050064
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050065def set_sol_setting(setting_name, setting_value):
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050066 r"""
67 Set SOL setting with given value.
68
69 # Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050070 # 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").
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050073 """
74
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050075 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol set "
76 + setting_name + " " + setting_value)
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050077
78 return status
79
Rahul Maheshwarid629b5c2017-05-23 08:06:28 -050080
Michael Walsh4481b932018-02-08 11:45:15 -060081def get_lan_print_dict():
Michael Walsh4481b932018-02-08 11:45:15 -060082 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
Michael Walshd59ed7c2018-02-15 10:19:38 -0600151
152
Michael Walshf4098fb2018-02-28 10:54:46 -0600153def get_ipmi_power_reading(strip_watts=1):
Michael Walshd59ed7c2018-02-15 10:19:38 -0600154 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
Michael Walshf4098fb2018-02-28 10:54:46 -0600178
179 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500180 strip_watts Strip all dictionary values of the
181 trailing " Watts" substring.
Michael Walshd59ed7c2018-02-15 10:19:38 -0600182 """
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
Michael Walshf4098fb2018-02-28 10:54:46 -0600188 if strip_watts:
189 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items())
190
Michael Walshd59ed7c2018-02-15 10:19:38 -0600191 return result
Michael Walshaf5607e2018-02-19 17:37:20 -0600192
193
194def get_mc_info():
Michael Walshaf5607e2018-02-19 17:37:20 -0600195 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
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500251
252
253def get_sdr_info():
Rahul Maheshwaridc6a32c2018-03-15 05:21:55 -0500254 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
George Keishing3511a3f2018-04-19 10:38:30 -0500293
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):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500300 version_id The data is obtained by from BMC
301 /etc/os-release
302 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585").
George Keishing3511a3f2018-04-19 10:38:30 -0500303
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.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500311 count = re.findall("-(\\d{1,4})-", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500312
313 # Release version.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500314 release = re.findall("-r(\\d{1,4})", version_id)
George Keishing3511a3f2018-04-19 10:38:30 -0500315 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
Michael Walsh27b14a62018-05-24 11:05:07 -0500321
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
George Keishinge0a81282018-06-08 10:02:30 -0500335 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500336 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
George Keishinge0a81282018-06-08 10:02:30 -0500342 Board Mfg : <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500343 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
George Keishinge0a81282018-06-08 10:02:30 -0500357 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500358 [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
George Keishinge0a81282018-06-08 10:02:30 -0500364 [board_mfg]: <Manufacturer Name>
Michael Walsh27b14a62018-05-24 11:05:07 -0500365 [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",
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500378 ret_values)]
Michael Walsh61224e62018-05-30 17:58:42 -0500379
380
Michael Walsha95e4ef2018-06-06 17:53:04 -0500381def get_component_fru_info(component='cpu',
382 fru_objs=None):
Michael Walsh61224e62018-05-30 17:58:42 -0500383 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):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500391 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.
Michael Walsh61224e62018-05-30 17:58:42 -0500399 """
400
Michael Walsha95e4ef2018-06-06 17:53:04 -0500401 if fru_objs is None:
402 fru_objs = get_fru_info()
Michael Walsh61224e62018-05-30 17:58:42 -0500403 return\
404 [x for x in fru_objs
405 if re.match(component + '([0-9]+)? ', x['fru_device_description'])]