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_robot_keyword as grk 11import gen_robot_utils as gru 12import bmc_ssh_utils as bsu 13import var_funcs as vf 14import tempfile 15gru.my_import_resource("ipmi_client.robot") 16from robot.libraries.BuiltIn import BuiltIn 17 18 19def get_sol_info(): 20 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: 31 sol_info[Info]: SOL parameter 'Payload Channel (7)' not supported - defaulting to 0x0e 32 sol_info[Character Send Threshold]: 1 33 sol_info[Force Authentication]: true 34 sol_info[Privilege Level]: USER 35 sol_info[Set in progress]: set-complete 36 sol_info[Retry Interval (ms)]: 100 37 sol_info[Non-Volatile Bit Rate (kbps)]: IPMI-Over-Serial-Setting 38 sol_info[Character Accumulate Level (ms)]: 100 39 sol_info[Enabled]: true 40 sol_info[Volatile Bit Rate (kbps)]: IPMI-Over-Serial-Setting 41 sol_info[Payload Channel]: 14 (0x0e) 42 sol_info[Payload Port]: 623 43 sol_info[Force Encryption]: true 44 sol_info[Retry Count]: 7 45 """ 46 47 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol info") 48 49 # Create temp file path. 50 temp = tempfile.NamedTemporaryFile() 51 temp_file_path = temp.name 52 53 # Write sol info to temp file path. 54 text_file = open(temp_file_path, "w") 55 text_file.write(ret_values) 56 text_file.close() 57 58 # Use my_parm_file to interpret data. 59 sol_info = gm.my_parm_file(temp_file_path) 60 61 return sol_info 62 63 64def set_sol_setting(setting_name, setting_value): 65 r""" 66 Set SOL setting with given value. 67 68 # Description of argument(s): 69 # setting_name SOL setting which needs to be set (e.g. "retry-count"). 70 # setting_value Value which needs to be set (e.g. "7"). 71 """ 72 73 status, ret_values = grk.run_key_u("Run IPMI Standard Command sol set " + 74 setting_name + " " + setting_value) 75 76 return status 77 78 79def get_lan_print_dict(): 80 r""" 81 Get IPMI 'lan print' output and return it as a dictionary. 82 83 Here is an example of the IPMI lan print output: 84 85 Set in Progress : Set Complete 86 Auth Type Support : MD5 87 Auth Type Enable : Callback : MD5 88 : User : MD5 89 : Operator : MD5 90 : Admin : MD5 91 : OEM : MD5 92 IP Address Source : Static Address 93 IP Address : x.x.x.x 94 Subnet Mask : x.x.x.x 95 MAC Address : xx:xx:xx:xx:xx:xx 96 Default Gateway IP : x.x.x.x 97 802.1q VLAN ID : Disabled 98 Cipher Suite Priv Max : Not Available 99 Bad Password Threshold : Not Available 100 101 Given that data, this function will return the following dictionary. 102 103 lan_print_dict: 104 [Set in Progress]: Set Complete 105 [Auth Type Support]: MD5 106 [Auth Type Enable]: 107 [Callback]: MD5 108 [User]: MD5 109 [Operator]: MD5 110 [Admin]: MD5 111 [OEM]: MD5 112 [IP Address Source]: Static Address 113 [IP Address]: x.x.x.x 114 [Subnet Mask]: x.x.x.x 115 [MAC Address]: xx:xx:xx:xx:xx:xx 116 [Default Gateway IP]: x.x.x.x 117 [802.1q VLAN ID]: Disabled 118 [Cipher Suite Priv Max]: Not Available 119 [Bad Password Threshold]: Not Available 120 121 """ 122 123 IPMI_INBAND_CMD = BuiltIn().get_variable_value("${IPMI_INBAND_CMD}") 124 125 # Notice in the example of data above that 'Auth Type Enable' needs some 126 # special processing. We essentially want to isolate its data and remove 127 # the 'Auth Type Enable' string so that key_value_outbuf_to_dict can 128 # process it as a sub-dictionary. 129 cmd_buf = IPMI_INBAND_CMD + " lan print | grep -E '^(Auth Type Enable)" +\ 130 "?[ ]+: ' | sed -re 's/^(Auth Type Enable)?[ ]+: //g'" 131 stdout1, stderr, rc = bsu.os_execute_command(cmd_buf) 132 133 # Now get the remainder of the data and exclude the lines with no field 134 # names (i.e. the 'Auth Type Enable' sub-fields). 135 cmd_buf = IPMI_INBAND_CMD + " lan print | grep -E -v '^[ ]+: '" 136 stdout2, stderr, rc = bsu.os_execute_command(cmd_buf) 137 138 # Make auth_type_enable_dict sub-dictionary... 139 auth_type_enable_dict = vf.key_value_outbuf_to_dict(stdout1, to_lower=0, 140 underscores=0) 141 142 # Create the lan_print_dict... 143 lan_print_dict = vf.key_value_outbuf_to_dict(stdout2, to_lower=0, 144 underscores=0) 145 # Re-assign 'Auth Type Enable' to contain the auth_type_enable_dict. 146 lan_print_dict['Auth Type Enable'] = auth_type_enable_dict 147 148 return lan_print_dict 149 150 151def get_ipmi_power_reading(strip_watts=1): 152 r""" 153 Get IPMI power reading data and return it as a dictionary. 154 155 The data is obtained by issuing the IPMI "power reading" command. An 156 example is shown below: 157 158 Instantaneous power reading: 234 Watts 159 Minimum during sampling period: 234 Watts 160 Maximum during sampling period: 234 Watts 161 Average power reading over sample period: 234 Watts 162 IPMI timestamp: Thu Jan 1 00:00:00 1970 163 Sampling period: 00000000 Seconds. 164 Power reading state is: deactivated 165 166 For the data shown above, the following dictionary will be returned. 167 168 result: 169 [instantaneous_power_reading]: 238 Watts 170 [minimum_during_sampling_period]: 238 Watts 171 [maximum_during_sampling_period]: 238 Watts 172 [average_power_reading_over_sample_period]: 238 Watts 173 [ipmi_timestamp]: Thu Jan 1 00:00:00 1970 174 [sampling_period]: 00000000 Seconds. 175 [power_reading_state_is]: deactivated 176 177 Description of argument(s): 178 strip_watts Strip all dictionary values of the trailing " Watts" 179 substring. 180 """ 181 182 status, ret_values = \ 183 grk.run_key_u("Run IPMI Standard Command dcmi power reading") 184 result = vf.key_value_outbuf_to_dict(ret_values) 185 186 if strip_watts: 187 result.update((k, re.sub(' Watts$', '', v)) for k, v in result.items()) 188 189 return result 190 191 192def get_mc_info(): 193 r""" 194 Get IPMI mc info data and return it as a dictionary. 195 196 The data is obtained by issuing the IPMI "mc info" command. An 197 example is shown below: 198 199 Device ID : 0 200 Device Revision : 0 201 Firmware Revision : 2.01 202 IPMI Version : 2.0 203 Manufacturer ID : 42817 204 Manufacturer Name : Unknown (0xA741) 205 Product ID : 16975 (0x424f) 206 Product Name : Unknown (0x424F) 207 Device Available : yes 208 Provides Device SDRs : yes 209 Additional Device Support : 210 Sensor Device 211 SEL Device 212 FRU Inventory Device 213 Chassis Device 214 Aux Firmware Rev Info : 215 0x00 216 0x00 217 0x00 218 0x00 219 220 For the data shown above, the following dictionary will be returned. 221 mc_info: 222 [device_id]: 0 223 [device_revision]: 0 224 [firmware_revision]: 2.01 225 [ipmi_version]: 2.0 226 [manufacturer_id]: 42817 227 [manufacturer_name]: Unknown (0xA741) 228 [product_id]: 16975 (0x424f) 229 [product_name]: Unknown (0x424F) 230 [device_available]: yes 231 [provides_device_sdrs]: yes 232 [additional_device_support]: 233 [additional_device_support][0]: Sensor Device 234 [additional_device_support][1]: SEL Device 235 [additional_device_support][2]: FRU Inventory Device 236 [additional_device_support][3]: Chassis Device 237 [aux_firmware_rev_info]: 238 [aux_firmware_rev_info][0]: 0x00 239 [aux_firmware_rev_info][1]: 0x00 240 [aux_firmware_rev_info][2]: 0x00 241 [aux_firmware_rev_info][3]: 0x00 242 """ 243 244 status, ret_values = \ 245 grk.run_key_u("Run IPMI Standard Command mc info") 246 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1) 247 248 return result 249 250 251def get_sdr_info(): 252 r""" 253 Get IPMI sdr info data and return it as a dictionary. 254 255 The data is obtained by issuing the IPMI "sdr info" command. An 256 example is shown below: 257 258 SDR Version : 0x51 259 Record Count : 216 260 Free Space : unspecified 261 Most recent Addition : 262 Most recent Erase : 263 SDR overflow : no 264 SDR Repository Update Support : unspecified 265 Delete SDR supported : no 266 Partial Add SDR supported : no 267 Reserve SDR repository supported : no 268 SDR Repository Alloc info supported : no 269 270 For the data shown above, the following dictionary will be returned. 271 mc_info: 272 273 [sdr_version]: 0x51 274 [record_Count]: 216 275 [free_space]: unspecified 276 [most_recent_addition]: 277 [most_recent_erase]: 278 [sdr_overflow]: no 279 [sdr_repository_update_support]: unspecified 280 [delete_sdr_supported]: no 281 [partial_add_sdr_supported]: no 282 [reserve_sdr_repository_supported]: no 283 [sdr_repository_alloc_info_supported]: no 284 """ 285 286 status, ret_values = \ 287 grk.run_key_u("Run IPMI Standard Command sdr info") 288 result = vf.key_value_outbuf_to_dict(ret_values, process_indent=1) 289 290 return result 291 292 293def get_aux_version(version_id): 294 r""" 295 Get IPMI Aux version info data and return it. 296 297 Description of argument(s): 298 version_id The data is obtained by from BMC /etc/os-release 299 (e.g. "xxx-v2.1-438-g0030304-r3-gfea8585"). 300 301 In the prior example, the 3rd field is "438" is the commit version and 302 the 5th field is "r3" and value "3" is the release version. 303 304 Aux version return from this function 4380003. 305 """ 306 307 # Commit version. 308 count = re.findall("-(\d{1,4})-", version_id) 309 310 # Release version. 311 release = re.findall("-r(\d{1,4})", version_id) 312 if release: 313 aux_version = count[0] + "{0:0>4}".format(release[0]) 314 else: 315 aux_version = count[0] + "0000" 316 317 return aux_version 318 319 320def get_fru_info(): 321 r""" 322 Get fru info and return it as a list of dictionaries. 323 324 The data is obtained by issuing the IPMI "fru print -N 50" command. An 325 example is shown below: 326 327 FRU Device Description : Builtin FRU Device (ID 0) 328 Device not present (Unspecified error) 329 330 FRU Device Description : cpu0 (ID 1) 331 Board Mfg Date : Sun Dec 31 18:00:00 1995 332 Board Mfg : <Manufacturer Name> 333 Board Product : PROCESSOR MODULE 334 Board Serial : YA1934315964 335 Board Part Number : 02CY209 336 337 FRU Device Description : cpu1 (ID 2) 338 Board Mfg Date : Sun Dec 31 18:00:00 1995 339 Board Mfg : <Manufacturer Name> 340 Board Product : PROCESSOR MODULE 341 Board Serial : YA1934315965 342 Board Part Number : 02CY209 343 344 For the data shown above, the following list of dictionaries will be 345 returned. 346 347 fru_obj: 348 fru_obj[0]: 349 [fru_device_description]: Builtin FRU Device (ID 0) 350 [state]: Device not present (Unspecified error) 351 fru_obj[1]: 352 [fru_device_description]: cpu0 (ID 1) 353 [board_mfg_date]: Sun Dec 31 18:00:00 1995 354 [board_mfg]: <Manufacturer Name> 355 [board_product]: PROCESSOR MODULE 356 [board_serial]: YA1934315964 357 [board_part_number]: 02CY209 358 fru_obj[2]: 359 [fru_device_description]: cpu1 (ID 2) 360 [board_mfg_date]: Sun Dec 31 18:00:00 1995 361 [board_mfg]: <Manufacturer Name> 362 [board_product]: PROCESSOR MODULE 363 [board_serial]: YA1934315965 364 [board_part_number]: 02CY209 365 """ 366 367 status, ret_values = \ 368 grk.run_key_u("Run IPMI Standard Command fru print -N 50") 369 370 # Manipulate the "Device not present" line to create a "state" key. 371 ret_values = re.sub("Device not present", "state : Device not present", 372 ret_values) 373 374 return [vf.key_value_outbuf_to_dict(x) for x in re.split("\n\n", 375 ret_values)] 376 377 378def get_component_fru_info(component='cpu', 379 fru_objs=None): 380 r""" 381 Get fru info for the given component and return it as a list of 382 dictionaries. 383 384 This function calls upon get_fru_info and then filters out the unwanted 385 entries. See get_fru_info's prolog for a layout of the data. 386 387 Description of argument(s): 388 component The component (e.g. "cpu", "dimm", etc.). 389 fru_objs A fru_objs list such as the one returned by get_fru_info. If 390 this is None, then this function will call get_fru_info to 391 obtain such a list. Supplying this argument may improve 392 performance if this function is to be called multiple times. 393 """ 394 395 if fru_objs is None: 396 fru_objs = get_fru_info() 397 return\ 398 [x for x in fru_objs 399 if re.match(component + '([0-9]+)? ', x['fru_device_description'])] 400