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(): 208 r""" 209 Return CFAM value which contains such things as SBE side bit. 210 """ 211 212 cmd_buf = "pdbg -d p9w -p0 getcfam 0x2808 | sed -re 's/.* = //g'" 213 out_buf, stderr, rc = bsu.bmc_execute_command(cmd_buf) 214 215 return int(out_buf, 16) 216 217 218def compare_mac_address(sys_mac_addr, user_mac_addr): 219 r""" 220 Return 1 if the MAC value matched, otherwise 0. 221 222 . Description of argument(s): 223 sys_mac_addr A valid system MAC string (e.g. "70:e2:84:14:2a:08") 224 user_mac_addr A user provided MAC string (e.g. "70:e2:84:14:2a:08") 225 """ 226 227 index = 0 228 # Example: ['70', 'e2', '84', '14', '2a', '08'] 229 mac_list = user_mac_addr.split(":") 230 for item in sys_mac_addr.split(":"): 231 if int(item, 16) == int(mac_list[index], 16): 232 index = index + 1 233 continue 234 return 0 235 236 return 1 237 238 239def get_os_ethtool(interface_name): 240 r""" 241 Get OS 'ethtool' output for the given interface_name and return it as a 242 dictionary. 243 244 Settings for enP52p1s0f0: 245 Supported ports: [ TP ] 246 Supported link modes: 10baseT/Half 10baseT/Full 247 100baseT/Half 100baseT/Full 248 1000baseT/Half 1000baseT/Full 249 Supported pause frame use: No 250 Supports auto-negotiation: Yes 251 Supported FEC modes: Not reported 252 Advertised link modes: 10baseT/Half 10baseT/Full 253 100baseT/Half 100baseT/Full 254 1000baseT/Half 1000baseT/Full 255 Advertised pause frame use: Symmetric 256 Advertised auto-negotiation: Yes 257 Advertised FEC modes: Not reported 258 Speed: Unknown! 259 Duplex: Unknown! (255) 260 Port: Twisted Pair 261 PHYAD: 1 262 Transceiver: internal 263 Auto-negotiation: on 264 MDI-X: Unknown 265 Supports Wake-on: g 266 Wake-on: g 267 Current message level: 0x000000ff (255) 268 drv probe link timer ifdown ifup rx_err tx_err 269 Link detected: no 270 271 Given that data, this function will return the following dictionary. 272 273 ethtool_dict: 274 [supported_ports]: [ TP ] 275 [supported_link_modes]: 276 [supported_link_modes][0]: 10baseT/Half 10baseT/Full 277 [supported_link_modes][1]: 100baseT/Half 100baseT/Full 278 [supported_link_modes][2]: 1000baseT/Half 1000baseT/Full 279 [supported_pause_frame_use]: No 280 [supports_auto-negotiation]: Yes 281 [supported_fec_modes]: Not reported 282 [advertised_link_modes]: 283 [advertised_link_modes][0]: 10baseT/Half 10baseT/Full 284 [advertised_link_modes][1]: 100baseT/Half 100baseT/Full 285 [advertised_link_modes][2]: 1000baseT/Half 1000baseT/Full 286 [advertised_pause_frame_use]: Symmetric 287 [advertised_auto-negotiation]: Yes 288 [advertised_fec_modes]: Not reported 289 [speed]: Unknown! 290 [duplex]: Unknown! (255) 291 [port]: Twisted Pair 292 [phyad]: 1 293 [transceiver]: internal 294 [auto-negotiation]: on 295 [mdi-x]: Unknown 296 [supports_wake-on]: g 297 [wake-on]: g 298 [current_message_level]: 0x000000ff (255) 299 [drv_probe_link_timer_ifdown_ifup_rx_err_tx_err]:<blank> 300 [link_detected]: no 301 """ 302 303 # Using sed and tail to massage the data a bit before running 304 # key_value_outbuf_to_dict. 305 cmd_buf = ( 306 "ethtool " 307 + interface_name 308 + " | sed -re 's/(.* link modes:)(.*)/\\1\\n\\2/g' | tail -n +2" 309 ) 310 stdout, stderr, rc = bsu.os_execute_command(cmd_buf) 311 result = vf.key_value_outbuf_to_dict(stdout, process_indent=1, strip=" \t") 312 313 return result 314 315 316def to_json_ordered(json_str): 317 r""" 318 Parse the JSON string data and return an ordered JSON dictionary object. 319 320 Description of argument(s): 321 json_str The string containing the JSON data. 322 """ 323 324 try: 325 return json.loads(json_str, object_pairs_hook=DotDict) 326 except TypeError: 327 return json.loads(json_str.decode("utf-8"), object_pairs_hook=DotDict) 328 329 330def get_bmc_release_info(): 331 r""" 332 Get release info from the BMC and return as a dictionary. 333 334 Example: 335 336 ${release_info}= Get BMC Release Info 337 Rprint Vars release_info 338 339 Output: 340 341 release_info: 342 [id]: openbmc-phosphor 343 [name]: Phosphor OpenBMC (Phosphor OpenBMC Project Reference... 344 [version]: 2.8.0-dev 345 [version_id]: 2.8.0-dev-1083-g8954c3505 346 [pretty_name]: Phosphor OpenBMC (Phosphor OpenBMC Project Reference... 347 [build_id]: 2.8.0-dev 348 [openbmc_target_machine]: witherspoon 349 """ 350 351 out_buf, stderr, rc = bsu.bmc_execute_command("cat /etc/os-release") 352 return vf.key_value_outbuf_to_dict(out_buf, delim="=", strip='"') 353 354 355def get_os_release_info(): 356 r""" 357 Get release info from the OS and return as a dictionary. 358 359 Example: 360 361 ${release_info}= Get OS Release Info 362 Rprint Vars release_info 363 364 Output: 365 release_info: 366 [name]: Red Hat Enterprise Linux Server 367 [version]: 7.6 (Maipo) 368 [id]: rhel 369 [id_like]: fedora 370 [variant]: Server 371 [variant_id]: server 372 [version_id]: 7.6 373 [pretty_name]: Red Hat Enterprise Linux Server 7.6 (Maipo) 374 [ansi_color]: 0;31 375 [cpe_name]: cpe:/o:redhat:enterprise_linux:7.6:GA:server 376 [home_url]: https://www.redhat.com/ 377 [bug_report_url]: https://bugzilla.redhat.com/ 378 [redhat_bugzilla_product]: Red Hat Enterprise Linux 7 379 [redhat_bugzilla_product_version]: 7.6 380 [redhat_support_product]: Red Hat Enterprise Linux 381 [redhat_support_product_version]: 7.6 382 """ 383 384 out_buf, stderr, rc = bsu.os_execute_command("cat /etc/os-release") 385 return vf.key_value_outbuf_to_dict(out_buf, delim="=", strip='"') 386 387 388def pdbg(option_string, **bsu_options): 389 r""" 390 Run pdbg on the BMC with the caller's option string and return the output. 391 392 Description of argument(s): 393 option_string A string of options which are to be processed by the pdbg command. 394 bsu_options Options to be passed directly to bmc_execute_command. See its prolog for 395 details. 396 """ 397 398 # Default print_out to 1. 399 if "print_out" not in bsu_options: 400 bsu_options["print_out"] = 1 401 402 stdout, stderr, rc = bsu.bmc_execute_command( 403 "pdbg " + option_string, **bsu_options 404 ) 405 return stdout 406 407 408def ecmd(option_string, **bsu_options): 409 r""" 410 Run ecmd command on the BMC with the caller's option string and return the output. 411 412 Description of argument(s): 413 option_string A string of options which are to be executed on BMC. 414 (e.g. getscom pu 20010a40 -all, 415 putscom pu 20010a40 4000000000000000 -p0). 416 bsu_options Options to be passed directly to bmc_execute_command. See its prolog for 417 details. 418 """ 419 420 # Default print_out to 1. 421 if "print_out" not in bsu_options: 422 bsu_options["print_out"] = 1 423 424 stdout, stderr, rc = bsu.bmc_execute_command(option_string, **bsu_options) 425 return stdout 426 427 428def split_string_with_index(stri, n): 429 r""" 430 To split every n characters and forms an element for every nth index 431 432 Example : Given ${stri} = "abcdef", then the function call, 433 ${data}= Split List With Index ${stri} 2 434 then, result will be data = ['ab', 'cd', 'ef'] 435 """ 436 437 n = int(n) 438 data = [stri[index : index + n] for index in range(0, len(stri), n)] 439 return data 440 441 442def remove_whitespace(instring): 443 r""" 444 Removes the white spaces around the string 445 446 Example: instring = " xxx ", then returns instring = "xxx" 447 """ 448 449 return instring.strip() 450 451 452def zfill_data(data, num): 453 r""" 454 zfill() method adds zeros (0) at the beginning of the string, until it 455 reaches the specified length. 456 457 Usage : ${anystr}= Zfill Data ${data} num 458 459 Example : Binary of one Byte has 8 bits - xxxx xxxx 460 461 Consider ${binary} has only 3 bits after converting from Hexadecimal/decimal to Binary 462 Say ${binary} = 110 then, 463 ${binary}= Zfill Data ${binary} 8 464 Now ${binary} will be 0000 0110 465 """ 466 467 return data.zfill(int(num)) 468 469 470def get_subsequent_value_from_list(list, value): 471 r""" 472 returns first index of the element occurrence. 473 """ 474 475 index = [list.index(i) for i in list if value in i] 476 return index 477 478 479def return_decoded_string(input): 480 r""" 481 returns decoded string of encoded byte. 482 """ 483 484 encoded_string = input.encode("ascii", "ignore") 485 decoded_string = encoded_string.decode() 486 return decoded_string 487 488 489def remove_unicode_from_uri(uri): 490 r""" 491 returns dbus uri without unicode in prefix 492 """ 493 494 return re.sub("`-|\\|-", "", uri) 495 496 497def get_bmc_major_minor_version(version): 498 r""" 499 returns major version and minor version 500 from cat /etc/os-release command. 501 For example, 502 xyz23.01 --> [23, 01] 503 xyz.0-112 --> [0, 112] 504 ZERzzYY-23.04-1-xx3 --> [23, 04, 1, 3] 505 """ 506 507 return re.findall(r"\d+", re.sub("[A-Z]|[a-z]", "", version)) 508 509 510def convert_name_into_bytes_with_prefix(name): 511 r""" 512 Convert name into bytes with prefix 0x 513 """ 514 515 bytes_list = [] 516 517 for letter in name: 518 bytes_list.append(hex(ord(letter))) 519 520 return bytes_list 521 522 523def convert_name_into_bytes_without_prefix(name): 524 r""" 525 Convert name into bytes 526 """ 527 528 tmp_lst = [] 529 530 for letter in name: 531 value = convert_to_hex_value_without_prefix(letter) 532 tmp_lst.append(value) 533 534 return tmp_lst 535 536 537def convert_to_hex_value_without_prefix(letter): 538 r""" 539 Convert into hex 540 """ 541 542 value = hex(ord(letter)) 543 if value[:2] == "0x": 544 value = value[2:] 545 546 return value 547 548 549def convert_prefix_hex_list_to_non_prefix_hex_list(list): 550 r""" 551 Convert into list of hex with prefix to list of hex without prefix. 552 """ 553 554 tmp_list = [] 555 556 for value in list: 557 if value[:2] == "0x": 558 tmp_list.append(value[2:]) 559 560 return tmp_list 561