1#!/usr/bin/env python 2 3r""" 4This module provides functions which are useful to plug-in call point programs. 5""" 6 7import sys 8import os 9import re 10import collections 11 12import gen_print as gp 13import gen_misc as gm 14import gen_cmd as gc 15 16PLUG_VAR_PREFIX = os.environ.get("PLUG_VAR_PREFIX", "AUTOBOOT") 17 18 19def get_plug_in_package_name(case=None): 20 r""" 21 Return the plug-in package name (e.g. "OS_Console", "DB_Logging"). 22 23 Description of argument(s): 24 case Indicates whether the value returned should be converted to upper or 25 lower case. Valid values are "upper", "lower" or None. 26 """ 27 28 plug_in_package_name = os.path.basename(gp.pgm_dir_path[:-1]) 29 if case == "upper": 30 return plug_in_package_name.upper() 31 elif case == "lower": 32 return plug_in_package_name.lower() 33 else: 34 return plug_in_package_name 35 36 37def return_plug_vars(general=True, 38 custom=True): 39 r""" 40 Return an OrderedDict which is sorted by key and which contains all of the plug-in environment variables. 41 42 Example excerpt of resulting dictionary: 43 44 plug_var_dict: 45 [AUTOBOOT_BASE_TOOL_DIR_PATH]: /tmp/ 46 [AUTOBOOT_BB_LEVEL]: <blank> 47 [AUTOBOOT_BOOT_FAIL]: 0 48 ... 49 50 This function also does the following: 51 - Set a default value for environment variable AUTOBOOT_OPENBMC_NICKNAME/AUTOIPL_FSP1_NICKNAME if it is 52 not already set. 53 - Register PASSWORD variables to prevent their values from being printed. 54 55 Note: The programmer may set a default for any given environment variable by declaring a global variable 56 of the same name and setting its value. For example, let's say the calling program has this global 57 declaration: 58 59 PERF_EXERCISERS_TOTAL_TIMEOUT = '180' 60 61 If environment variable PERF_EXERCISERS_TOTAL_TIMEOUT is blank or not set, this function will set it to 62 '180'. 63 64 Furthermore, if such a default variable declaration is not a string, this function will preserve that 65 non-string type in setting global variables (with the exception of os.environ values which must be 66 string). Example: 67 68 NVDIMM_ENCRYPT = 0 69 70 Description of argument(s): 71 general Return general plug-in parms (e.g. those beginning with "AUTOBOOT" or 72 "AUTOGUI"). 73 custom Return custom plug-in parms (i.e. those beginning with the upper case 74 name of the plug-in package, for example "OBMC_SAMPLE_PARM1"). 75 """ 76 77 regex_list = [] 78 if not (general or custom): 79 return collections.OrderedDict() 80 plug_in_package_name = get_plug_in_package_name(case="upper") 81 if general: 82 regex_list = [PLUG_VAR_PREFIX, "AUTOGUI"] 83 if custom: 84 regex_list.append(plug_in_package_name) 85 86 regex = "^(" + "|".join(regex_list) + ")_" 87 88 # Set a default for nickname. 89 if os.environ.get("AUTOBOOT_OPENBMC_NICKNAME", "") == "": 90 os.environ['AUTOBOOT_OPENBMC_NICKNAME'] = \ 91 os.environ.get("AUTOBOOT_OPENBMC_HOST", "") 92 93 if os.environ.get("AUTOIPL_FSP1_NICKNAME", "") == "": 94 os.environ['AUTOIPL_FSP1_NICKNAME'] = \ 95 os.environ.get("AUTOIPL_FSP1_NAME", "").split(".")[0] 96 97 # For all variables specified in the parm_def file, we want them to default to "" rather than being unset. 98 # Process the parm_def file if it exists. 99 parm_def_file_path = gp.pgm_dir_path + "parm_def" 100 if os.path.exists(parm_def_file_path): 101 parm_defs = gm.my_parm_file(parm_def_file_path) 102 else: 103 parm_defs = collections.OrderedDict() 104 # Example parm_defs: 105 # parm_defs: 106 # parm_defs[rest_fail]: boolean 107 # parm_defs[command]: string 108 # parm_defs[esel_stop_file_path]: string 109 110 # Create a list of plug-in environment variables by pre-pending <all caps plug-in package name>_<all 111 # caps var name> 112 plug_in_parm_names = [plug_in_package_name + "_" + x for x in 113 map(str.upper, parm_defs.keys())] 114 # Example plug_in_parm_names: 115 # plug_in_parm_names: 116 # plug_in_parm_names[0]: STOP_REST_FAIL 117 # plug_in_parm_names[1]: STOP_COMMAND 118 # plug_in_parm_names[2]: STOP_ESEL_STOP_FILE_PATH 119 120 # os.environ only accepts string values. However, if the user defines default values of other types 121 # (e.g. int), we wish to preserve the type. 122 non_string_defaults = {} 123 # Initialize unset plug-in vars. 124 for var_name in plug_in_parm_names: 125 # If there is a global variable with the same name as the environment variable, use its value as a 126 # default. 127 default_value = gm.get_mod_global(var_name, "") 128 if type(default_value) is not str: 129 non_string_defaults[var_name] = type(default_value) 130 os.environ[var_name] = os.environ.get(var_name, str(default_value)) 131 if os.environ[var_name] == "": 132 os.environ[var_name] = str(default_value) 133 134 plug_var_dict = \ 135 collections.OrderedDict(sorted({k: v for (k, v) in 136 os.environ.items() 137 if re.match(regex, k)}.items())) 138 # Restore the types of any variables where the caller had defined default values. 139 for key, value in non_string_defaults.items(): 140 cmd_buf = "plug_var_dict[key] = " + str(value).split("'")[1] + "(plug_var_dict[key]" 141 if value is int: 142 # Use int base argument of 0 to allow it to interpret hex strings. 143 cmd_buf += ", 0)" 144 else: 145 cmd_buf += ")" 146 exec(cmd_buf) in globals(), locals() 147 # Register password values to prevent printing them out. Any plug var whose name ends in PASSWORD will 148 # be registered. 149 password_vals = {k: v for (k, v) in plug_var_dict.items() 150 if re.match(r".*_PASSWORD$", k)}.values() 151 map(gp.register_passwords, password_vals) 152 153 return plug_var_dict 154 155 156def sprint_plug_vars(headers=1, **kwargs): 157 r""" 158 Sprint the plug-in environment variables (i.e. those that begin with the global PLUG_VAR_PREFIX value or 159 those that begin with <plug-in package_name>_ in upper case letters.). 160 161 Example excerpt of output: 162 AUTOBOOT_BASE_TOOL_DIR_PATH=/tmp/ 163 AUTOBOOT_BB_LEVEL= 164 AUTOBOOT_BOOT_FAIL=0 165 AUTOBOOT_BOOT_FAIL_THRESHOLD=1000000 166 167 Description of argument(s): 168 headers Print a header and a footer. 169 kwargs These are passed directly to return_plug_vars. See return_plug_vars doc 170 string for details. 171 """ 172 plug_var_dict = return_plug_vars(**kwargs) 173 buffer = "" 174 if headers: 175 buffer += "\n" + gp.sprint_dashes() 176 for key, value in plug_var_dict.items(): 177 buffer += gp.sprint_varx(key, value) 178 if headers: 179 buffer += gp.sprint_dashes() + "\n" 180 181 return buffer 182 183 184def print_plug_in_header(): 185 r""" 186 Print plug-in header. 187 188 When debug is set, print all plug_prefix variables (e.g. AUTOBOOT_OPENBMC_HOST, etc.) and all plug-in 189 environment variables (e.g. OBMC_SAMPLE_PARM1) with surrounding dashed lines. When debug is not set, 190 print only the plug-in environment variables (e.g. OBMC_SAMPLE_PARM1) with no surrounding dashed lines. 191 192 NOTE: plug-in environment variables means any variable defined in the <plug-in dir>/parm_def file plus 193 any environment variables whose names begin with the upper-case plug-in package name. 194 """ 195 196 dprint_plug_vars() 197 if not debug: 198 qprint_plug_vars(headers=0, general=False, custom=True) 199 200 201def get_plug_vars(mod_name="__main__"): 202 r""" 203 Get all plug-in variables and put them in corresponding global variables. 204 205 This would include all environment variables beginning with either the global PLUG_VAR_PREFIX value or 206 with the upper case version of the plug-in package name + underscore (e.g. OP_SAMPLE_VAR1 for plug-in 207 OP_Sample). 208 209 The global variables to be set will be both with and without the global PLUG_VAR_PREFIX value prefix. 210 For example, if the environment variable in question is AUTOBOOT_OPENBMC_HOST, this function will set 211 global variable AUTOBOOT_OPENBMC_HOST and global variable OPENBMC_HOST. 212 213 Description of argument(s): 214 mod_name The name of the module whose global plug-in variables should be retrieved. 215 """ 216 217 module = sys.modules[mod_name] 218 plug_var_dict = return_plug_vars() 219 220 # Get all PLUG_VAR_PREFIX environment variables and put them into globals. 221 for key, value in plug_var_dict.items(): 222 setattr(module, key, value) 223 setattr(module, re.sub("^" + PLUG_VAR_PREFIX + "_", "", key), value) 224 225 226def get_plug_default(var_name, 227 default=None): 228 r""" 229 Derive and return a default value for the given parm variable. 230 231 Dependencies: 232 Global variable PLUG_VAR_PREFIX must be set. 233 234 This function will assign a default by checking the following environment variables in the order shown. 235 The first one that has a value will be used. 236 - <upper case package_name>_<var_name> 237 - <PLUG_VAR_PREFIX>_OVERRIDE_<var_name> 238 - <PLUG_VAR_PREFIX>_<var_name> 239 240 If none of these are found, this function will return the value passed by the caller in the "default" 241 parm. 242 243 Example: 244 245 Let's say your plug-in is named "OS_Console" and you call this function as follows: 246 247 get_plug_default("quiet", 0) 248 249 The first of these environment variables that is found to be set will be used to provide the default 250 value. 251 - OS_CONSOLE_QUIET 252 - AUTOBOOT_OVERRIDE_QUIET 253 - AUTOBOOT_QUIET 254 255 If none of those has a value, 0 (as specified by the caller in this example) is returned. 256 257 Let's say the master driver program is named obmc_boot. obmc_boot program is responsible for calling 258 plug-ins. Let's further suppose that the user wishes to run the master program with --debug=0 but wishes 259 to have all plug-ins run with --debug=1. This could be accomplished with the following call: 260 export AUTOBOOT_OVERRIDE_DEBUG=1 ; obmc_boot --debug=0 --plug_in_dir_paths=<list of plug ins> 261 262 As another example, let's suppose that the user wishes to have just the OS_Console plug-in run with debug 263 and everything else to default to debug=0. This could be accomplished as follows: 264 export OS_CONSOLE_DEBUG=1 ; obmc_boot --debug=0 --plug_in_dir_paths=<list of plug ins> 265 266 And as one more example, let's say the user wishes to have obmc_boot and OS_Console run without debug but 267 have all other plug-ins run with debug: 268 export AUTOBOOT_OVERRIDE_DEBUG=1 ; export OS_CONSOLE_DEBUG=0 ; obmc_boot --debug=0 269 --plug_in_dir_paths=<list of plug ins> 270 271 Description of argument(s): 272 var_name The name of the variable for which a default value is to be calculated. 273 default The default value if one cannot be determined. 274 """ 275 276 var_name = var_name.upper() 277 plug_in_package_name = get_plug_in_package_name(case="upper") 278 279 package_var_name = plug_in_package_name + "_" + var_name 280 default_value = os.environ.get(package_var_name, None) 281 if default_value is not None: 282 # A package-name version of the variable was found so return its value. 283 return(default_value) 284 285 plug_var_name = PLUG_VAR_PREFIX + "_OVERRIDE_" + var_name 286 default_value = os.environ.get(plug_var_name, None) 287 if default_value is not None: 288 # A PLUG_VAR_PREFIX version of the variable was found so return its value. 289 return default_value 290 291 plug_var_name = PLUG_VAR_PREFIX + "_" + var_name 292 default_value = os.environ.get(plug_var_name, None) 293 if default_value is not None: 294 # A PLUG_VAR_PREFIX version of the variable was found so return its value. 295 return default_value 296 297 return default 298 299 300def srequired_plug_in(req_plug_in_names, 301 plug_in_dir_paths=None): 302 r""" 303 Return an empty string if the required plug-ins are found in plug_in_dir_paths. Otherwise, return an 304 error string. 305 306 Example call: 307 error_message = srequired_plug_in(req_plug_in_names, plug_in_dir_paths) 308 309 Description of argument(s): 310 req_plug_in_names A list of plug_in names that the caller requires (e.g. ['OS_Console']). 311 plug_in_dir_paths A string which is a colon-delimited list of plug-ins specified by the 312 user (e.g. DB_Logging:FFDC:OS_Console:Perf). Path values (e.g. 313 "/home/robot/dir1") will be stripped from this list to do the analysis. 314 Default value is the <PLUG_VAR_PREFIX>_PLUG_IN_DIR_PATHS environment 315 variable. 316 """ 317 318 # Calculate default value for plug_in_dir_paths. 319 if plug_in_dir_paths is None: 320 plug_in_dir_paths = os.environ.get(PLUG_VAR_PREFIX 321 + "_PLUG_IN_DIR_PATHS", "") 322 323 error_message = "" 324 325 # Convert plug_in_dir_paths to a list of base names. 326 plug_in_dir_paths = \ 327 list(filter(None, map(os.path.basename, plug_in_dir_paths.split(":")))) 328 329 # Check for each of the user's required plug-ins. 330 for plug_in_name in req_plug_in_names: 331 if plug_in_name not in plug_in_dir_paths: 332 error_message = "The \"" + get_plug_in_package_name() +\ 333 "\" plug-in cannot run unless the user also selects the \"" +\ 334 plug_in_name + "\" plug in:\n" +\ 335 gp.sprint_var(plug_in_dir_paths) 336 337 return error_message 338 339 340def required_plug_in(req_plug_in_names, 341 plug_in_dir_paths=None): 342 r""" 343 Return True if each of the plug-ins in req_plug_in_names can be found in plug_in_dir_paths Otherwise, 344 return False and print an error message to stderr. 345 346 Example call: 347 if not required_plug_in(['OS_Console'], AUTOBOOT_PLUG_IN_DIR_PATHS): 348 return False 349 350 Description of argument(s): 351 (See Description of arguments for srequired_plug_in (above)). 352 """ 353 354 error_message = srequired_plug_in(req_plug_in_names, plug_in_dir_paths) 355 if not error_message == "": 356 gp.print_error_report(error_message) 357 return False 358 359 return True 360 361 362def compose_plug_in_save_dir_path(plug_in_package_name=None): 363 r""" 364 Create and return a directory path name that is suitable for saving plug-in data. 365 366 The name will be comprised of things such as plug_in package name, pid, etc. in order to guarantee that 367 it is unique for a given test run. 368 369 Description of argument(s): 370 plug_in_package_name The plug-in package name. This defaults to the name of the caller's 371 plug-in package. However, the caller can specify another value in order 372 to retrieve data saved by another plug-in package. 373 """ 374 375 plug_in_package_name = gm.dft(plug_in_package_name, 376 get_plug_in_package_name()) 377 378 BASE_TOOL_DIR_PATH = \ 379 gm.add_trailing_slash(os.environ.get(PLUG_VAR_PREFIX 380 + "_BASE_TOOL_DIR_PATH", 381 "/tmp/")) 382 NICKNAME = os.environ.get("AUTOBOOT_OPENBMC_NICKNAME", "") 383 if NICKNAME == "": 384 NICKNAME = os.environ["AUTOIPL_FSP1_NICKNAME"] 385 MASTER_PID = os.environ[PLUG_VAR_PREFIX + "_MASTER_PID"] 386 gp.qprint_vars(BASE_TOOL_DIR_PATH, NICKNAME, plug_in_package_name, 387 MASTER_PID) 388 return BASE_TOOL_DIR_PATH + gm.username() + "/" + NICKNAME + "/" +\ 389 plug_in_package_name + "/" + str(MASTER_PID) + "/" 390 391 392def create_plug_in_save_dir(plug_in_package_name=None): 393 r""" 394 Create a directory suitable for saving plug-in processing data and return its path name. 395 396 See compose_plug_in_save_dir_path for details. 397 398 Description of argument(s): 399 plug_in_package_name See compose_plug_in_save_dir_path for details. 400 """ 401 402 plug_in_save_dir_path = compose_plug_in_save_dir_path(plug_in_package_name) 403 if os.path.isdir(plug_in_save_dir_path): 404 return plug_in_save_dir_path 405 gc.shell_cmd("mkdir -p " + plug_in_save_dir_path) 406 return plug_in_save_dir_path 407 408 409def delete_plug_in_save_dir(plug_in_package_name=None): 410 r""" 411 Delete the plug_in save directory. See compose_plug_in_save_dir_path for details. 412 413 Description of argument(s): 414 plug_in_package_name See compose_plug_in_save_dir_path for details. 415 """ 416 417 gc.shell_cmd("rm -rf " 418 + compose_plug_in_save_dir_path(plug_in_package_name)) 419 420 421def save_plug_in_value(value, plug_in_package_name=None): 422 r""" 423 Save a value in a plug-in save file. The value may be retrieved later via a call to the 424 restore_plug_in_value function. 425 426 This function will figure out the variable name of the value passed and use that name in creating the 427 plug-in save file. 428 429 Example call: 430 431 my_var1 = 5 432 save_plug_in_value(my_var1) 433 434 In this example, the value "5" would be saved to the "my_var1" file in the plug-in save directory. 435 436 Description of argument(s): 437 value The value to be saved. 438 plug_in_package_name See compose_plug_in_save_dir_path for details. 439 """ 440 441 # Get the name of the variable used as argument one to this function. 442 var_name = gp.get_arg_name(0, 1, stack_frame_ix=2) 443 plug_in_save_dir_path = create_plug_in_save_dir(plug_in_package_name) 444 save_file_path = plug_in_save_dir_path + var_name 445 gp.qprint_timen("Saving \"" + var_name + "\" value.") 446 gc.shell_cmd("echo '" + str(value) + "' > " + save_file_path) 447 448 449def restore_plug_in_value(default="", plug_in_package_name=None): 450 r""" 451 Return a value from a plug-in save file. 452 453 The name of the value to be restored will be determined by this function based on the lvalue being 454 assigned. Consider the following example: 455 456 my_var1 = restore_plug_in_value(2) 457 458 In this example, this function would look for the "my_var1" file in the plug-in save directory, read its 459 value and return it. If no such file exists, the default value of 2 would be returned. 460 461 Description of argument(s): 462 default The default value to be returned if there is no plug-in save file for the 463 value in question. 464 plug_in_package_name See compose_plug_in_save_dir_path for details. 465 """ 466 467 # Get the lvalue from the caller's invocation of this function. 468 lvalue = gp.get_arg_name(0, -1, stack_frame_ix=2) 469 plug_in_save_dir_path = create_plug_in_save_dir(plug_in_package_name) 470 save_file_path = plug_in_save_dir_path + lvalue 471 if os.path.isfile(save_file_path): 472 gp.qprint_timen("Restoring " + lvalue + " value from " 473 + save_file_path + ".") 474 value = gm.file_to_list(save_file_path, newlines=0, comments=0, 475 trim=1)[0] 476 if type(default) is bool: 477 # Convert from string to bool. 478 value = (value == 'True') 479 if type(default) is int: 480 # Convert from string to int. 481 value = int(value) 482 gp.qprint_varx(lvalue, value) 483 return value 484 else: 485 gp.qprint_timen("Save file " + save_file_path 486 + " does not exist so returning default value.") 487 gp.qprint_var(default) 488 return default 489 490 491def exit_not_master(): 492 r""" 493 Exit the program with return code zero if this program was NOT called by the master program. 494 495 There are cases where plug-ins are called by a multi-layered stack: 496 497 master_wrapper 498 obmc_boot_test.py 499 Example_plug_in/cp_setup 500 501 In a scenario like this, Example_plug_in/cp_setup may be called once directly by master_wrapper (the 502 master) and and then called again directly by obmc_boot_test.py (the child). Some plug-in programs may 503 wish to avoid doing any processing on the second such call. This function will achieve that purpose. 504 505 This function will print a standard message to stdout prior to exiting. 506 """ 507 508 AUTOBOOT_MASTER_PID = gm.get_mod_global("AUTOBOOT_MASTER_PID") 509 AUTOBOOT_PROGRAM_PID = gm.get_mod_global("AUTOBOOT_PROGRAM_PID") 510 511 if AUTOBOOT_MASTER_PID != AUTOBOOT_PROGRAM_PID: 512 message = get_plug_in_package_name() + "/" + gp.pgm_name + " is not" \ 513 + " being called by the master program in the stack so no action" \ 514 + " will be taken." 515 gp.qprint_timen(message) 516 gp.qprint_vars(AUTOBOOT_MASTER_PID, AUTOBOOT_PROGRAM_PID) 517 exit(0) 518 519 520def stop_test_rc(): 521 r""" 522 Return the constant stop test return code value. 523 524 When a plug-in call point program returns this value, it indicates that master program should stop 525 running. 526 """ 527 528 return 0x00000002 529 530 531# Create print wrapper functions for all sprint functions defined above. 532# func_names contains a list of all print functions which should be created from their sprint counterparts. 533func_names = ['print_plug_vars'] 534 535# stderr_func_names is a list of functions whose output should go to stderr rather than stdout. 536stderr_func_names = [] 537 538replace_dict = dict(gp.replace_dict) 539replace_dict['mod_qualifier'] = 'gp.' 540func_defs = gp.create_print_wrapper_funcs(func_names, stderr_func_names, 541 replace_dict) 542gp.gp_debug_print(func_defs) 543exec(func_defs) 544