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 ipmi_client as ic 16import tempfile 17gru.my_import_resource("ipmi_client.robot") 18from robot.libraries.BuiltIn import BuiltIn 19 20 21def get_sol_info(): 22 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: 33 sol_info[Info]: SOL parameter 'Payload Channel (7)' 34 not supported - defaulting to 0x0e 35 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 66 67def set_sol_setting(setting_name, setting_value): 68 r""" 69 Set SOL setting with given value. 70 71 # Description of argument(s): 72 # 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"). 75 """ 76 77 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol set " 78 + setting_name + " " + setting_value) 79 80 return status 81 82 83def execute_ipmi_cmd(cmd_string, 84 ipmi_cmd_type='inband', 85 print_output=1, 86 ignore_err=0, 87 **options): 88 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. 103 options These are passed directly to the 104 create_ipmi_ext_command_string function. 105 See that function's prolog for details. 106 """ 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': 116 cmd_buf = ic.create_ipmi_ext_command_string(cmd_string, **options) 117 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'): 125 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 166 Description of argument(s): 167 ipmi_cmd_type The type of ipmi command to use (e.g. 168 'inband', 'external'). 169 """ 170 171 # 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. 175 cmd_buf = "lan print | grep -E '^(Auth Type Enable)" +\ 176 "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'" 177 stdout1, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type, 178 print_output=0) 179 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). 182 cmd_buf = "lan print | grep -E -v '^[ ]+: '" 183 stdout2, stderr, rc = execute_ipmi_cmd(cmd_buf, ipmi_cmd_type, 184 print_output=0) 185 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 197 198 199def get_ipmi_power_reading(strip_watts=1): 200 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 224 225 Description of argument(s): 226 strip_watts Strip all dictionary values of the 227 trailing " Watts" substring. 228 """ 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 234 if strip_watts: 235 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items()) 236 237 return result 238 239 240def get_mc_info(): 241 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 297 298 299def get_sdr_info(): 300 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 339 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): 346 version_id The data is obtained by from BMC 347 /etc/os-release 348 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585"). 349 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. 357 count = re.findall("-(\\d{1,4})-", version_id) 358 359 # Release version. 360 release = re.findall("-r(\\d{1,4})", version_id) 361 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 367 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 381 Board Mfg : <Manufacturer Name> 382 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 388 Board Mfg : <Manufacturer Name> 389 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 403 [board_mfg]: <Manufacturer Name> 404 [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 410 [board_mfg]: <Manufacturer Name> 411 [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", 424 ret_values)] 425 426 427def get_component_fru_info(component='cpu', 428 fru_objs=None): 429 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): 437 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. 445 """ 446 447 if fru_objs is None: 448 fru_objs = get_fru_info() 449 return\ 450 [x for x in fru_objs 451 if re.match(component + '([0-9]+)? ', x['fru_device_description'])] 452 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 498 499 500def channel_getciphers_ipmi(): 501 502 r""" 503 Run 'channel getciphers ipmi' command and return the result as a list of dictionaries. 504 505 Example robot code: 506 ${ipmi_channel_ciphers}= Channel Getciphers IPMI 507 Rprint Vars ipmi_channel_ciphers 508 509 Example output: 510 ipmi_channel_ciphers: 511 [0]: 512 [id]: 3 513 [iana]: N/A 514 [auth_alg]: hmac_sha1 515 [integrity_alg]: hmac_sha1_96 516 [confidentiality_alg]: aes_cbc_128 517 [1]: 518 [id]: 17 519 [iana]: N/A 520 [auth_alg]: hmac_sha256 521 [integrity_alg]: sha256_128 522 [confidentiality_alg]: aes_cbc_128 523 """ 524 525 cmd_buf = "channel getciphers ipmi | sed -re 's/ Alg/_Alg/g'" 526 stdout, stderr, rc = execute_ipmi_cmd(cmd_buf, "external", print_output=0) 527 return vf.outbuf_to_report(stdout) 528