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