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