1#!/usr/bin/env python 2 3r""" 4Provide useful ipmi functions. 5""" 6 7import re 8import gen_print as gp 9import gen_misc as gm 10import gen_cmd as gc 11import gen_robot_keyword as grk 12import gen_robot_utils as gru 13import bmc_ssh_utils as bsu 14import var_funcs as vf 15import tempfile 16gru.my_import_resource("ipmi_client.robot") 17from robot.libraries.BuiltIn import BuiltIn 18 19 20def get_sol_info(): 21 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: 32 sol_info[Info]: SOL parameter 'Payload Channel (7)' 33 not supported - defaulting to 0x0e 34 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 65 66def set_sol_setting(setting_name, setting_value): 67 r""" 68 Set SOL setting with given value. 69 70 # Description of argument(s): 71 # 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"). 74 """ 75 76 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol set " 77 + setting_name + " " + setting_value) 78 79 return status 80 81 82def 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 += " " + 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'): 127 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 168 Description of argument(s): 169 ipmi_cmd_type The type of ipmi command to use (e.g. 170 'inband', 'external'). 171 """ 172 173 # 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. 177 cmd_buf = "lan print | grep -E '^(Auth Type Enable)" +\ 178 "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'" 179 stdout1, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type, 180 print_output=0) 181 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). 184 cmd_buf = "lan print | grep -E -v '^[ ]+: '" 185 stdout2, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type, 186 print_output=0) 187 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 199 200 201def get_ipmi_power_reading(strip_watts=1): 202 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 226 227 Description of argument(s): 228 strip_watts Strip all dictionary values of the 229 trailing " Watts" substring. 230 """ 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 236 if strip_watts: 237 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items()) 238 239 return result 240 241 242def get_mc_info(): 243 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 299 300 301def get_sdr_info(): 302 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 341 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): 348 version_id The data is obtained by from BMC 349 /etc/os-release 350 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585"). 351 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. 359 count = re.findall("-(\\d{1,4})-", version_id) 360 361 # Release version. 362 release = re.findall("-r(\\d{1,4})", version_id) 363 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 369 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 383 Board Mfg : <Manufacturer Name> 384 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 390 Board Mfg : <Manufacturer Name> 391 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 405 [board_mfg]: <Manufacturer Name> 406 [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 412 [board_mfg]: <Manufacturer Name> 413 [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", 426 ret_values)] 427 428 429def get_component_fru_info(component='cpu', 430 fru_objs=None): 431 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): 439 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. 447 """ 448 449 if fru_objs is None: 450 fru_objs = get_fru_info() 451 return\ 452 [x for x in fru_objs 453 if re.match(component + '([0-9]+)? ', x['fru_device_description'])] 454