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 += " -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'): 128 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 169 Description of argument(s): 170 ipmi_cmd_type The type of ipmi command to use (e.g. 171 'inband', 'external'). 172 """ 173 174 # 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. 178 cmd_buf = "lan print | grep -E '^(Auth Type Enable)" +\ 179 "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'" 180 stdout1, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type, 181 print_output=0) 182 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). 185 cmd_buf = "lan print | grep -E -v '^[ ]+: '" 186 stdout2, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type, 187 print_output=0) 188 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 200 201 202def get_ipmi_power_reading(strip_watts=1): 203 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 227 228 Description of argument(s): 229 strip_watts Strip all dictionary values of the 230 trailing " Watts" substring. 231 """ 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 237 if strip_watts: 238 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items()) 239 240 return result 241 242 243def get_mc_info(): 244 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 300 301 302def get_sdr_info(): 303 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 342 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): 349 version_id The data is obtained by from BMC 350 /etc/os-release 351 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585"). 352 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. 360 count = re.findall("-(\\d{1,4})-", version_id) 361 362 # Release version. 363 release = re.findall("-r(\\d{1,4})", version_id) 364 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 370 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 384 Board Mfg : <Manufacturer Name> 385 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 391 Board Mfg : <Manufacturer Name> 392 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 406 [board_mfg]: <Manufacturer Name> 407 [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 413 [board_mfg]: <Manufacturer Name> 414 [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", 427 ret_values)] 428 429 430def get_component_fru_info(component='cpu', 431 fru_objs=None): 432 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): 440 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. 448 """ 449 450 if fru_objs is None: 451 fru_objs = get_fru_info() 452 return\ 453 [x for x in fru_objs 454 if re.match(component + '([0-9]+)? ', x['fru_device_description'])] 455