1#!/usr/bin/env python3 2 3r""" 4Companion file to utils.robot. 5""" 6 7import collections 8import json 9import os 10 11import bmc_ssh_utils as bsu 12import gen_print as gp 13import gen_robot_keyword as grk 14import var_funcs as vf 15from robot.libraries import DateTime 16from robot.libraries.BuiltIn import BuiltIn 17 18try: 19 from robot.utils import DotDict 20except ImportError: 21 pass 22import re 23 24# The code base directory will be one level up from the directory containing this module. 25code_base_dir_path = os.path.dirname(os.path.dirname(__file__)) + os.sep 26 27 28def get_code_base_dir_path(): 29 r""" 30 Return the dir path of our code base. 31 """ 32 33 return code_base_dir_path 34 35 36def set_power_policy_method(): 37 r""" 38 Set the global bmc_power_policy_method to either 'Old' or 'New'. 39 40 The power policy data has moved from an 'org' location to an 'xyz' 41 location. This keyword will determine whether the new method of getting 42 the power policy is valid and will set the global bmc_power_policy_method 43 variable accordingly. If power_policy_setup is already set (by a prior 44 call to this function), this keyword will simply return. 45 46 If bmc_power_policy_method is "Old", this function will adjust the global 47 policy variables from data/variables.py: RESTORE_LAST_STATE, 48 ALWAYS_POWER_ON, ALWAYS_POWER_OFF. 49 """ 50 51 # Retrieve global variables. 52 power_policy_setup = int( 53 BuiltIn().get_variable_value("${power_policy_setup}", default=0) 54 ) 55 bmc_power_policy_method = BuiltIn().get_variable_value( 56 "${bmc_power_policy_method}", default=0 57 ) 58 gp.dpvar(power_policy_setup) 59 60 # If this function has already been run once, we need not continue. 61 if power_policy_setup: 62 return 63 64 gp.dpvar(bmc_power_policy_method, 1) 65 66 # The user has not set bmc_power_policy_method via a -v parm so we will 67 # determine what it should be. 68 if bmc_power_policy_method == "": 69 status, ret_values = grk.run_key_u("New Get Power Policy", ignore=1) 70 if status == "PASS": 71 bmc_power_policy_method = "New" 72 else: 73 bmc_power_policy_method = "Old" 74 75 gp.qpvar(bmc_power_policy_method) 76 # For old style, we will rewrite these global variable settings to old 77 # values. 78 if bmc_power_policy_method == "Old": 79 BuiltIn().set_global_variable( 80 "${RESTORE_LAST_STATE}", "RESTORE_LAST_STATE" 81 ) 82 BuiltIn().set_global_variable("${ALWAYS_POWER_ON}", "ALWAYS_POWER_ON") 83 BuiltIn().set_global_variable( 84 "${ALWAYS_POWER_OFF}", "ALWAYS_POWER_OFF" 85 ) 86 87 # Set global variables to control subsequent calls to this function. 88 BuiltIn().set_global_variable( 89 "${bmc_power_policy_method}", bmc_power_policy_method 90 ) 91 BuiltIn().set_global_variable("${power_policy_setup}", 1) 92 93 94def translate_power_policy_value(policy): 95 r""" 96 Translate the policy value and return the result. 97 98 Using old style functions, callers might call like this with a hard- 99 code value for policy: 100 101 Set BMC Power Policy ALWAYS_POWER_OFF 102 103 This function will get the value of the corresponding global variable (if 104 it exists) and return it. 105 106 This will allow the old style call to still work on systems using the new 107 method of storing the policy value. 108 """ 109 110 valid_power_policy_vars = BuiltIn().get_variable_value( 111 "${valid_power_policy_vars}" 112 ) 113 114 if policy not in valid_power_policy_vars: 115 return policy 116 117 status, ret_values = grk.run_key_u( 118 "Get Variable Value ${" + policy + "}", quiet=1 119 ) 120 return ret_values 121 122 123def get_bmc_date_time(): 124 r""" 125 Get date/time info from BMC and return as a dictionary. 126 127 Example of dictionary data returned by this keyword. 128 time_dict: 129 [local_time]: Fri 2017-11-03 152756 UTC 130 [local_time_seconds]: 1509740876 131 [universal_time]: Fri 2017-11-03 152756 UTC 132 [universal_time_seconds]: 1509740876 133 [rtc_time]: Fri 2016-05-20 163403 134 [rtc_time_seconds]: 1463780043 135 [time_zone]: n/a (UTC, +0000) 136 [network_time_on]: yes 137 [ntp_synchronized]: no 138 [rtc_in_local_tz]: no 139 """ 140 141 out_buf, stderr, rc = bsu.bmc_execute_command("timedatectl") 142 # Example of output returned by call to timedatectl: 143 # Local time: Fri 2017-11-03 15:27:56 UTC 144 # Universal time: Fri 2017-11-03 15:27:56 UTC 145 # RTC time: Fri 2016-05-20 16:34:03 146 # Time zone: n/a (UTC, +0000) 147 # Network time on: yes 148 # NTP synchronized: no 149 # RTC in local TZ: no 150 151 # Convert the out_buf to a dictionary. 152 initial_time_dict = vf.key_value_outbuf_to_dict(out_buf) 153 154 # For each "_time" entry in the dictionary, we will create a corresponding 155 # "_time_seconds" entry. We create a new dictionary so that the entries 156 # are kept in a nice order for printing. 157 try: 158 result_time_dict = collections.OrderedDict() 159 except AttributeError: 160 result_time_dict = DotDict() 161 162 for key, value in initial_time_dict.items(): 163 result_time_dict[key] = value 164 if not key.endswith("_time"): 165 continue 166 result_time_dict[key + "_seconds"] = int( 167 DateTime.convert_date(value, result_format="epoch") 168 ) 169 170 return result_time_dict 171 172 173def get_bmc_df(df_parm_string=""): 174 r""" 175 Get df report from BMC and return as a report "object". 176 177 A df report object is a list where each entry is a dictionary whose keys 178 are the field names from the first entry in report_list. 179 180 Example df report object: 181 182 df_report: 183 df_report[0]: 184 [filesystem]: dev 185 [1k-blocks]: 247120 186 [used]: 0 187 [available]: 247120 188 [use%]: 0% 189 [mounted]: /dev 190 df_report[1]: 191 [filesystem]: dev 192 [1k-blocks]: 247120 193 [used]: 0 194 [available]: 247120 195 [use%]: 0% 196 [mounted]: /dev 197 198 . Description of argument(s): 199 df_parm_string A string containing valid df command parms (e.g. 200 "-h /var"). 201 """ 202 203 out_buf, stderr, rc = bsu.bmc_execute_command("df " + df_parm_string) 204 return vf.outbuf_to_report(out_buf) 205 206 207def get_sbe(cfam_addrs): 208 r""" 209 Return CFAM value which contains such things as SBE side bit. 210 211 Description of argument(s): 212 cfam_addrs Provide the CFAM address 213 """ 214 215 cmd_buf = ( 216 "pdbg -d p9w -p0 getcfam " + cfam_addrs + " | sed -re 's/.* = //g'" 217 ) 218 out_buf, stderr, rc = bsu.bmc_execute_command(cmd_buf) 219 220 return int(out_buf, 16) 221 222 223def compare_mac_address(sys_mac_addr, user_mac_addr): 224 r""" 225 Return 1 if the MAC value matched, otherwise 0. 226 227 . Description of argument(s): 228 sys_mac_addr A valid system MAC string (e.g. "70:e2:84:14:2a:08") 229 user_mac_addr A user provided MAC string (e.g. "70:e2:84:14:2a:08") 230 """ 231 232 index = 0 233 # Example: ['70', 'e2', '84', '14', '2a', '08'] 234 mac_list = user_mac_addr.split(":") 235 for item in sys_mac_addr.split(":"): 236 if int(item, 16) == int(mac_list[index], 16): 237 index = index + 1 238 continue 239 return 0 240 241 return 1 242 243 244def get_os_ethtool(interface_name): 245 r""" 246 Get OS 'ethtool' output for the given interface_name and return it as a 247 dictionary. 248 249 Settings for enP52p1s0f0: 250 Supported ports: [ TP ] 251 Supported link modes: 10baseT/Half 10baseT/Full 252 100baseT/Half 100baseT/Full 253 1000baseT/Half 1000baseT/Full 254 Supported pause frame use: No 255 Supports auto-negotiation: Yes 256 Supported FEC modes: Not reported 257 Advertised link modes: 10baseT/Half 10baseT/Full 258 100baseT/Half 100baseT/Full 259 1000baseT/Half 1000baseT/Full 260 Advertised pause frame use: Symmetric 261 Advertised auto-negotiation: Yes 262 Advertised FEC modes: Not reported 263 Speed: Unknown! 264 Duplex: Unknown! (255) 265 Port: Twisted Pair 266 PHYAD: 1 267 Transceiver: internal 268 Auto-negotiation: on 269 MDI-X: Unknown 270 Supports Wake-on: g 271 Wake-on: g 272 Current message level: 0x000000ff (255) 273 drv probe link timer ifdown ifup rx_err tx_err 274 Link detected: no 275 276 Given that data, this function will return the following dictionary. 277 278 ethtool_dict: 279 [supported_ports]: [ TP ] 280 [supported_link_modes]: 281 [supported_link_modes][0]: 10baseT/Half 10baseT/Full 282 [supported_link_modes][1]: 100baseT/Half 100baseT/Full 283 [supported_link_modes][2]: 1000baseT/Half 1000baseT/Full 284 [supported_pause_frame_use]: No 285 [supports_auto-negotiation]: Yes 286 [supported_fec_modes]: Not reported 287 [advertised_link_modes]: 288 [advertised_link_modes][0]: 10baseT/Half 10baseT/Full 289 [advertised_link_modes][1]: 100baseT/Half 100baseT/Full 290 [advertised_link_modes][2]: 1000baseT/Half 1000baseT/Full 291 [advertised_pause_frame_use]: Symmetric 292 [advertised_auto-negotiation]: Yes 293 [advertised_fec_modes]: Not reported 294 [speed]: Unknown! 295 [duplex]: Unknown! (255) 296 [port]: Twisted Pair 297 [phyad]: 1 298 [transceiver]: internal 299 [auto-negotiation]: on 300 [mdi-x]: Unknown 301 [supports_wake-on]: g 302 [wake-on]: g 303 [current_message_level]: 0x000000ff (255) 304 [drv_probe_link_timer_ifdown_ifup_rx_err_tx_err]:<blank> 305 [link_detected]: no 306 """ 307 308 # Using sed and tail to massage the data a bit before running 309 # key_value_outbuf_to_dict. 310 cmd_buf = ( 311 "ethtool " 312 + interface_name 313 + " | sed -re 's/(.* link modes:)(.*)/\\1\\n\\2/g' | tail -n +2" 314 ) 315 stdout, stderr, rc = bsu.os_execute_command(cmd_buf) 316 result = vf.key_value_outbuf_to_dict(stdout, process_indent=1, strip=" \t") 317 318 return result 319 320 321def to_json_ordered(json_str): 322 r""" 323 Parse the JSON string data and return an ordered JSON dictionary object. 324 325 Description of argument(s): 326 json_str The string containing the JSON data. 327 """ 328 329 try: 330 return json.loads(json_str, object_pairs_hook=DotDict) 331 except TypeError: 332 return json.loads(json_str.decode("utf-8"), object_pairs_hook=DotDict) 333 334 335def get_bmc_release_info(): 336 r""" 337 Get release info from the BMC and return as a dictionary. 338 339 Example: 340 341 ${release_info}= Get BMC Release Info 342 Rprint Vars release_info 343 344 Output: 345 346 release_info: 347 [id]: openbmc-phosphor 348 [name]: Phosphor OpenBMC (Phosphor OpenBMC Project Reference... 349 [version]: 2.8.0-dev 350 [version_id]: 2.8.0-dev-1083-g8954c3505 351 [pretty_name]: Phosphor OpenBMC (Phosphor OpenBMC Project Reference... 352 [build_id]: 2.8.0-dev 353 [openbmc_target_machine]: witherspoon 354 """ 355 356 out_buf, stderr, rc = bsu.bmc_execute_command("cat /etc/os-release") 357 return vf.key_value_outbuf_to_dict(out_buf, delim="=", strip='"') 358 359 360def get_os_release_info(): 361 r""" 362 Get release info from the OS and return as a dictionary. 363 364 Example: 365 366 ${release_info}= Get OS Release Info 367 Rprint Vars release_info 368 369 Output: 370 release_info: 371 [name]: Red Hat Enterprise Linux Server 372 [version]: 7.6 (Maipo) 373 [id]: rhel 374 [id_like]: fedora 375 [variant]: Server 376 [variant_id]: server 377 [version_id]: 7.6 378 [pretty_name]: Red Hat Enterprise Linux Server 7.6 (Maipo) 379 [ansi_color]: 0;31 380 [cpe_name]: cpe:/o:redhat:enterprise_linux:7.6:GA:server 381 [home_url]: https://www.redhat.com/ 382 [bug_report_url]: https://bugzilla.redhat.com/ 383 [redhat_bugzilla_product]: Red Hat Enterprise Linux 7 384 [redhat_bugzilla_product_version]: 7.6 385 [redhat_support_product]: Red Hat Enterprise Linux 386 [redhat_support_product_version]: 7.6 387 """ 388 389 out_buf, stderr, rc = bsu.os_execute_command("cat /etc/os-release") 390 return vf.key_value_outbuf_to_dict(out_buf, delim="=", strip='"') 391 392 393def pdbg(option_string, **bsu_options): 394 r""" 395 Run pdbg on the BMC with the caller's option string and return the output. 396 397 Description of argument(s): 398 option_string A string of options which are to be processed by the pdbg command. 399 bsu_options Options to be passed directly to bmc_execute_command. See its prolog for 400 details. 401 """ 402 403 # Default print_out to 1. 404 if "print_out" not in bsu_options: 405 bsu_options["print_out"] = 1 406 407 stdout, stderr, rc = bsu.bmc_execute_command( 408 "pdbg " + option_string, **bsu_options 409 ) 410 return stdout 411 412 413def ecmd(option_string, **bsu_options): 414 r""" 415 Run ecmd command on the BMC with the caller's option string and return the output. 416 417 Description of argument(s): 418 option_string A string of options which are to be executed on BMC. 419 (e.g. getscom pu 20010a40 -all, 420 putscom pu 20010a40 4000000000000000 -p0). 421 bsu_options Options to be passed directly to bmc_execute_command. See its prolog for 422 details. 423 """ 424 425 # Default print_out to 1. 426 if "print_out" not in bsu_options: 427 bsu_options["print_out"] = 1 428 429 stdout, stderr, rc = bsu.bmc_execute_command(option_string, **bsu_options) 430 return stdout 431 432 433def split_string_with_index(stri, n): 434 r""" 435 To split every n characters and forms an element for every nth index 436 437 Example : Given ${stri} = "abcdef", then the function call, 438 ${data}= Split List With Index ${stri} 2 439 then, result will be data = ['ab', 'cd', 'ef'] 440 """ 441 442 n = int(n) 443 data = [stri[index : index + n] for index in range(0, len(stri), n)] 444 return data 445 446 447def remove_whitespace(instring): 448 r""" 449 Removes the white spaces around the string 450 451 Example: instring = " xxx ", then returns instring = "xxx" 452 """ 453 454 return instring.strip() 455 456 457def zfill_data(data, num): 458 r""" 459 zfill() method adds zeros (0) at the beginning of the string, until it 460 reaches the specified length. 461 462 Usage : ${anystr}= Zfill Data ${data} num 463 464 Example : Binary of one Byte has 8 bits - xxxx xxxx 465 466 Consider ${binary} has only 3 bits after converting from Hexadecimal/decimal to Binary 467 Say ${binary} = 110 then, 468 ${binary}= Zfill Data ${binary} 8 469 Now ${binary} will be 0000 0110 470 """ 471 472 return data.zfill(int(num)) 473 474 475def get_subsequent_value_from_list(list, value): 476 r""" 477 returns first index of the element occurrence. 478 """ 479 480 index = [list.index(i) for i in list if value in i] 481 return index 482 483 484def return_decoded_string(input): 485 r""" 486 returns decoded string of encoded byte. 487 """ 488 489 encoded_string = input.encode("ascii", "ignore") 490 decoded_string = encoded_string.decode() 491 return decoded_string 492 493 494def remove_unicode_from_uri(uri): 495 r""" 496 returns dbus uri without unicode in prefix 497 """ 498 499 return re.sub("`-|\\|-", "", uri) 500 501 502def get_bmc_major_minor_version(version): 503 r""" 504 returns major version and minor version 505 from cat /etc/os-release command. 506 For example, 507 xyz23.01 --> [23, 01] 508 xyz.0-112 --> [0, 112] 509 ZERzzYY-23.04-1-xx3 --> [23, 04, 1, 3] 510 """ 511 512 return re.findall(r"\d+", re.sub("[A-Z]|[a-z]", "", version)) 513 514 515def convert_name_into_bytes_with_prefix(name): 516 r""" 517 Convert name into bytes with prefix 0x 518 """ 519 520 bytes_list = [] 521 522 for letter in name: 523 bytes_list.append(hex(ord(letter))) 524 525 return bytes_list 526 527 528def convert_name_into_bytes_without_prefix(name): 529 r""" 530 Convert name into bytes 531 """ 532 533 tmp_lst = [] 534 535 for letter in name: 536 value = convert_to_hex_value_without_prefix(letter) 537 tmp_lst.append(value) 538 539 return tmp_lst 540 541 542def convert_to_hex_value_without_prefix(letter): 543 r""" 544 Convert into hex 545 """ 546 547 value = hex(ord(letter)) 548 if value[:2] == "0x": 549 value = value[2:] 550 551 return value 552 553 554def convert_prefix_hex_list_to_non_prefix_hex_list(list): 555 r""" 556 Convert into list of hex with prefix to list of hex without prefix. 557 """ 558 559 tmp_list = [] 560 561 for value in list: 562 if value[:2] == "0x": 563 tmp_list.append(value[2:]) 564 565 return tmp_list 566