def validate_parms(): r""" Validate all program parameters. """ process_pgm_parms() gp.qprintn() global openbmc_model grv.rvalid_value("openbmc_host") grv.rvalid_value("openbmc_username") grv.rvalid_value("openbmc_password") if os_host != "": grv.rvalid_value("os_username") grv.rvalid_value("os_password") if pdu_host != "": grv.rvalid_value("pdu_username") grv.rvalid_value("pdu_password") grv.rvalid_integer("pdu_slot_no") if openbmc_serial_host != "": grv.rvalid_integer("openbmc_serial_port") if openbmc_model == "": status, ret_values =\ grk.run_key_u("Get BMC System Model") openbmc_model = ret_values BuiltIn().set_global_variable("${openbmc_model}", openbmc_model) grv.rvalid_value("openbmc_model") grv.rvalid_integer("max_num_tests") grv.rvalid_integer("boot_pass") grv.rvalid_integer("boot_fail") plug_in_packages_list = grpi.rvalidate_plug_ins(plug_in_dir_paths) BuiltIn().set_global_variable("${plug_in_packages_list}", plug_in_packages_list) grv.rvalid_value("stack_mode", valid_values=['normal', 'skip']) if len(boot_list) == 0 and len(boot_stack) == 0 and not ffdc_only: error_message = "You must provide either a value for either the" +\ " boot_list or the boot_stack parm.\n" BuiltIn().fail(gp.sprint_error(error_message)) valid_boot_list(boot_list, valid_boot_types) valid_boot_list(boot_stack, valid_boot_types) selected_PDU_boots = list(set(boot_list + boot_stack) & set(boot_lists['PDU_reboot'])) if len(selected_PDU_boots) > 0 and pdu_host == "": error_message = "You have selected the following boots which" +\ " require a PDU host but no value for pdu_host:\n" error_message += gp.sprint_var(selected_PDU_boots) error_message += gp.sprint_var(pdu_host, 2) BuiltIn().fail(gp.sprint_error(error_message)) return
def sprint_obj(self): r""" sprint the fields of this object. This would normally be for debug purposes only. """ buffer = "" buffer += "class name: " + self.__class__.__name__ + "\n" buffer += gp.sprint_var(self.__obj_name) buffer += self.__boot_results.sprint_obj() buffer += gp.sprint_var(self.__initial_boot_pass) buffer += gp.sprint_var(self.__initial_boot_fail) return buffer
def get_host_name_ip(host, short_name=0): r""" Get the host name and the IP address for the given host and return them as a tuple. Description of argument(s): host The host name or IP address to be obtained. short_name Include the short host name in the returned tuple, i.e. return host, ip and short_host. """ host_name = socket.getfqdn(host) try: host_ip = socket.gethostbyname(host) except socket.gaierror as my_gaierror: message = "Unable to obtain the host name for the following host:" +\ "\n" + gp.sprint_var(host) gp.print_error_report(message) raise my_gaierror if short_name: host_short_name = host_name.split(".")[0] return host_name, host_ip, host_short_name else: return host_name, host_ip
def which(file_path): r""" Find the full path of an executable file and return it. The PATH environment variable dictates the results of this function. Description of arguments: file_path The relative file path (e.g. "my_file" or "lib/my_file"). """ shell_rc, out_buf = gc.cmd_fnc_u("which " + file_path, quiet=1, print_output=0, show_err=0) if shell_rc != 0: error_message = "Failed to find complete path for file \"" +\ file_path + "\".\n" error_message += gp.sprint_var(shell_rc, 1) error_message += out_buf if robot_env: BuiltIn().fail(gp.sprint_error(error_message)) else: gp.print_error_report(error_message) return False file_path = out_buf.rstrip("\n") return file_path
def get_mod_global(var_name, default=None, mod_name="__main__"): r""" Get module global variable value and return it. If we are running in a robot environment, the behavior will default to calling get_variable_value. Description of arguments: var_name The name of the variable whose value is sought. default The value to return if the global does not exist. mod_name The name of the module containing the global variable. """ if robot_env: return BuiltIn().get_variable_value("${" + var_name + "}", default) try: module = sys.modules[mod_name] except KeyError: gp.print_error_report("Programmer error - The mod_name passed to" + " this function is invalid:\n" + gp.sprint_var(mod_name)) raise ValueError('Programmer error.') if default is None: return getattr(module, var_name) else: return getattr(module, var_name, default)
def set_mod_global(var_value, mod_name="__main__", var_name=None): r""" Set a global variable for a given module. Description of arguments: var_value The value to set in the variable. mod_name The name of the module whose variable is to be set. var_name The name of the variable to set. This defaults to the name of the variable used for var_value when calling this function. """ try: module = sys.modules[mod_name] except KeyError: gp.print_error_report("Programmer error - The mod_name passed to" + " this function is invalid:\n" + gp.sprint_var(mod_name)) raise ValueError('Programmer error.') if var_name is None: var_name = gp.get_arg_name(None, 1, 2) setattr(module, var_name, var_value)
def valid_state(): r""" Verify that our state dictionary contains no blank values. If we don't get valid state data, we cannot continue to work. """ if st.compare_states(state, st.invalid_state_match, 'or'): error_message = "The state dictionary contains blank fields which" +\ " is illegal.\n" + gp.sprint_var(state) BuiltIn().fail(gp.sprint_error(error_message))
def get_health_check(verify=False): r""" Get the health_check information and return as a dictionary. Example robot code: ${health_check}= Get Health Check Rpvars 1 health_check Example result: health_check: [hardware_status]: OK [performance]: OK Description of argument(s): verify If set, verify that all all expected field_names are generated by the health_check command. """ rc, output = openbmctool_execute_command("health_check", print_output=False, ignore_err=False) health_check = vf.key_value_outbuf_to_dict(output, delim=":") if int(verify): # Create a list of files by stripping the dir names from the elements # of collect_service_data_file_paths. fields_obtained = health_check.keys() fields_expected = health_check_fields() fields_missing = list(set(fields_expected) - set(fields_obtained)) if len(fields_missing) > 0: err_msg = "The following fields are missing from the output of" err_msg += " health_check:\n" err_msg += gp.sprint_var(fields_missing) err_msg += gp.sprint_var(health_check) BuiltIn().fail(gp.sprint_error(err_msg)) return health_check
def validate_plug_in_package(plug_in_dir_path, mch_class="obmc"): r""" Validate the plug in package and return the normalized plug-in directory path. Description of arguments: plug_in_dir_path The "relative" or absolute path to a plug in package directory. mch_class The class of machine that we are testing (e.g. "op" = "open power", "obmc" = "open bmc", etc). """ gp.dprint_executing() if os.path.isabs(plug_in_dir_path): # plug_in_dir_path begins with a slash so it is an absolute path. candidate_plug_in_dir_path = os.path.normpath(plug_in_dir_path) +\ os.sep if not os.path.isdir(candidate_plug_in_dir_path): gp.print_error_report("Plug-in directory path \"" + plug_in_dir_path + "\" does not exist.\n") exit(1) else: # The plug_in_dir_path is actually a simple name (e.g. # "OBMC_Sample")... candidate_plug_in_dir_path = find_plug_in_package(plug_in_dir_path) if candidate_plug_in_dir_path == "": global PATH_LIST gp.print_error_report("Plug-in directory path \"" + plug_in_dir_path + "\" could not be found" + " in any of the following directories:\n" + gp.sprint_var(PATH_LIST)) exit(1) # Make sure that this plug-in supports us... supports_file_path = candidate_plug_in_dir_path + "supports_" + mch_class if not os.path.exists(supports_file_path): gp.print_error_report("The following file path could not be" + " found:\n" + gp.sprint_varx("supports_file_path", supports_file_path) + "\nThis file is necessary to indicate that" + " the given plug-in supports the class of" + " machine we are testing, namely \"" + mch_class + "\".\n") exit(1) return candidate_plug_in_dir_path
def valid_dump(dump_id, dump_dict=None, quiet=None): r""" Verify that dump_id is a valid. If it is not valid, issue robot failure message. A dump is valid if the indicated dump_id refers to an existing dump with a valid associated dump file. Description of argument(s): dump_id A dump ID (e.g. "1", "2", etc.) dump_dict A dump dictionary such as the one returned by get_dump_dict. If this value is None, this function will call get_dump_dict on the caller's behalf. quiet If quiet is set to 1, this function will NOT write status messages to stdout. """ if dump_dict is None: dump_dict = get_dump_dict(quiet=quiet) if dump_id not in dump_dict: message = "The specified dump ID was not found among the existing" \ + " dumps:\n" message += gp.sprint_var(dump_id) message += gp.sprint_var(dump_dict) BuiltIn().fail(gp.sprint_error(message)) if not dump_dict[dump_id].endswith("tar.xz"): message = "There is no \"tar.xz\" file associated with the given" \ + " dump_id:\n" message += gp.sprint_var(dump_id) dump_file_path = dump_dict[dump_id] message += gp.sprint_var(dump_file_path) BuiltIn().fail(gp.sprint_error(message))
def obmc_boot_test_py(alt_boot_stack=None): r""" Do main program processing. """ if alt_boot_stack is not None: BuiltIn().set_global_variable("${boot_stack}", alt_boot_stack) setup() if ffdc_only: gp.qprint_timen("Caller requested ffdc_only.") pre_boot_plug_in_setup() grk.run_key_u("my_ffdc") return # Process caller's boot_stack. while (len(boot_stack) > 0): test_loop_body() gp.qprint_timen("Finished processing stack.") # Process caller's boot_list. if len(boot_list) > 0: for ix in range(1, max_num_tests + 1): test_loop_body() gp.qprint_timen("Completed all requested boot tests.") boot_pass, boot_fail = boot_results.return_total_pass_fail() if boot_fail > boot_fail_threshold: error_message = "Boot failures exceed the boot failure" +\ " threshold:\n" +\ gp.sprint_var(boot_fail) +\ gp.sprint_var(boot_fail_threshold) BuiltIn().fail(gp.sprint_error(error_message))
def subscribe(self, dbus_path, enable_trace=False): r""" Subscribe to the given path and return a list of event notifications. For more details on "subscribe" and "events" go to https://github.com/openbmc/docs/blob/master/rest-api.md#event-subscription-protocol Example robot code: ${event_notifications}= Subscribe /xyz/openbmc_project/sensors Rprint Vars event_notifications Example output: event_notifications: [0]: [interface]: xyz.openbmc_project.Sensor.Value [path]: /xyz/openbmc_project/sensors/temperature/ambient [event]: PropertiesChanged [properties]: [Value]: 23813 Description of argument(s): dbus_path The subscribing event's path (e.g. "/xyz/openbmc_project/sensors"). enable_trace Enable or disable trace. """ session = self.login() cookies = session.cookies.get_dict() # Convert from dictionary to a string of the following format: # key=value;key=value... cookies = gp.sprint_var(cookies, fmt=gp.no_header() | gp.strip_brackets(), col1_width=0, trailing_char="", delim="=").replace("\n", ";") websocket.enableTrace(enable_trace) self.__websocket = websocket.create_connection( "wss://{host}/subscribe".format(host=self.__host), sslopt={"cert_reqs": ssl.CERT_NONE}, cookie=cookies) dbus_path = [path.strip() for path in dbus_path.split(',')] dbus_path = {"paths": dbus_path} self.__websocket.send(json.dumps(dbus_path)) event_notifications = json.loads(self.__websocket.recv()) self.__websocket.close() return event_notifications
def sprint_obj(self): r""" sprint the fields of this object. This would normally be for debug purposes. """ buffer = "" buffer += "class name: " + self.__class__.__name__ + "\n" buffer += gp.sprint_var(self.__obj_name) buffer += gp.sprint_var(self.__row_key_field_name) buffer += gp.sprint_var(self.__table) buffer += gp.sprint_var(self.__init_fields_dict) buffer += gp.sprint_var(self.__sum_fields) buffer += gp.sprint_var(self.__totals_line) buffer += gp.sprint_var(self.__calc_fields) buffer += gp.sprint_var(self.__table) return buffer
def svalid_list(var_value, valid_values=[], var_name=""): r""" Return an empty string if var_value is a valid list. Otherwise, return an error string. Description of arguments: var_value The value (i.e. list) being validated. valid_values A list of valid values. Each element in the var_value list must be equal to one of these values to be considered valid. var_name The name of the variable whose value is passed in var_value. This parameter is normally unnecessary as this function can figure out the var_name. This is provided for Robot callers. In this scenario, we are unable to get the variable name ourselves. """ error_message = "" if len(var_value) == 0: show_blanks = 1 error_message += "The \"" + get_var_name(var_name) error_message += "\" list is empty and is therefore invalid:\n" return error_message found_error = 0 display_var_value = list(var_value) for ix in range(0, len(var_value)): if var_value[ix] not in valid_values: found_error = 1 display_var_value[ix] = var_value[ix] + "*" if found_error: show_blanks = 1 error_message += "The list entries marked with \"*\" are not valid:\n" error_message += gp.sprint_varx(get_var_name(var_name), display_var_value, show_blanks) error_message += gp.sprint_var(valid_values) return error_message return ""
def get_host_name_ip(host): r""" Get the host name and the IP address for the given host and return them as a tuple. Description of argument(s): host The host name or IP address to be obtained. """ host_host_name = socket.getfqdn(host) try: host_ip = socket.gethostbyname(host) except socket.gaierror as my_gaierror: message = "Unable to obtain the host name for the following host:" +\ "\n" + gp.sprint_var(host) gp.print_error_report(message) raise my_gaierror return host_host_name, host_ip
def sprint_obj(self): r""" sprint the fields of this object. This would normally be for debug purposes. """ buffer = "" buffer += self.__class__.__name__ + ":\n" indent = 2 try: func_name = self.__func.__name__ except AttributeError: func_name = "" buffer += gp.sprint_var(func_name, indent=indent) buffer += gp.sprint_varx("time_out", self.__time_out, indent=indent) buffer += gp.sprint_varx("child_pid", self.__child_pid, indent=indent) buffer += gp.sprint_varx("original_SIGUSR1_handler", self.__original_SIGUSR1_handler, indent=indent) return buffer
def srequired_plug_in(req_plug_in_names, plug_in_dir_paths=None): r""" Return an empty string if the required plug-ins are found in plug_in_dir_paths. Otherwise, return an error string. Example call: error_message = srequired_plug_in(req_plug_in_names, plug_in_dir_paths) Description of argument(s): req_plug_in_names A list of plug_in names that the caller requires (e.g. ['OS_Console']). plug_in_dir_paths A string which is a colon-delimited list of plug-ins specified by the user (e.g. DB_Logging:FFDC:OS_Console:Perf). Path values (e.g. "/home/robot/dir1") will be stripped from this list to do the analysis. Default value is the <PLUG_VAR_PREFIX>_PLUG_IN_DIR_PATHS environment variable. """ # Calculate default value for plug_in_dir_paths. if plug_in_dir_paths is None: plug_in_dir_paths = os.environ.get(PLUG_VAR_PREFIX + "_PLUG_IN_DIR_PATHS", "") error_message = "" # Convert plug_in_dir_paths to a list of base names. plug_in_dir_paths = \ list(filter(None, map(os.path.basename, plug_in_dir_paths.split(":")))) # Check for each of the user's required plug-ins. for plug_in_name in req_plug_in_names: if plug_in_name not in plug_in_dir_paths: error_message = "The \"" + get_plug_in_package_name() +\ "\" plug-in cannot run unless the user also selects the \"" +\ plug_in_name + "\" plug in:\n" +\ gp.sprint_var(plug_in_dir_paths) return error_message
def srequired_plug_in(req_plug_in_names, plug_in_dir_paths=None): r""" Return an empty string if the required plug-ins are found in plug_in_dir_paths. Otherwise, return an error string. Example call: error_message = srequired_plug_in(req_plug_in_names, plug_in_dir_paths) Description of argument(s): req_plug_in_names A list of plug_in names that the caller requires (e.g. ['OS_Console']). plug_in_dir_paths A string which is a colon-delimited list of plug-ins specified by the user (e.g. DB_Logging:FFDC:OS_Console:Perf). Path values (e.g. "/home/robot/dir1") will be stripped from this list to do the analysis. Default value is the <PLUG_VAR_PREFIX>_PLUG_IN_DIR_PATHS environment variable. """ # Calculate default value for plug_in_dir_paths. if plug_in_dir_paths is None: plug_in_dir_paths = os.environ.get(PLUG_VAR_PREFIX + "_PLUG_IN_DIR_PATHS", "") error_message = "" # Convert plug_in_dir_paths to a list of base names. plug_in_dir_paths = \ filter(None, map(os.path.basename, plug_in_dir_paths.split(":"))) # Check for each of the user's required plug-ins. for plug_in_name in req_plug_in_names: if plug_in_name not in plug_in_dir_paths: error_message = "The \"" + get_plug_in_package_name() +\ "\" plug-in cannot run unless the user also selects the \"" +\ plug_in_name + "\" plug in:\n" +\ gp.sprint_var(plug_in_dir_paths) return error_message
def valid_dict(var_value, required_keys=[], var_name=None): r""" The variable value is valid if it is a dictionary containing all of the required keys. Description of argument(s): var_value The value being validated. required_keys A list of keys which must be found in the dictionary for it to be considered valid. """ error_message = "" missing_keys = list(set(required_keys) - set(var_value.keys())) if len(missing_keys) > 0: var_name = get_var_name(var_name) error_message += "The following dictionary is invalid because it is" error_message += " missing required keys:\n" error_message += gp.sprint_varx(var_name, var_value, gp.blank() | gp.show_type()) error_message += "\n" error_message += gp.sprint_var(missing_keys, gp.show_type()) return process_error_message(error_message)
def valid_program(var_value, var_name=None): r""" The variable value is valid if it contains the name of a program which can be located using the "which" command. Description of argument(s): var_value The value being validated. """ error_message = "" rc, out_buf = gc.shell_cmd("which " + var_value, quiet=1, show_err=0, ignore_err=1) if rc: var_name = get_var_name(var_name) error_message += "The following required program could not be found" error_message += " using the $PATH environment variable:\n" error_message += gp.sprint_varx(var_name, var_value, gp.blank()) PATH = os.environ.get("PATH", "").split(":") error_message += "\n" error_message += gp.sprint_var(PATH) return process_error_message(error_message)
def openbmctool_execute_command_json(command_string, *args, **kwargs): r""" Run the command string as an argument to the openbmctool.py program, parse the JSON output into a dictionary and return the dictionary. This function is a wrapper for openbmctool_execute_command (defined above). The caller may provide any command string where the output will be JSON data. This function will convert the JSON data to a python object, verify that the 'status' field = "ok" and return the 'data' sub-field to the caller. See openbmctool_execute_command (above) for all field descriptions. """ rc, output = openbmctool_execute_command(command_string, *args, **kwargs) json_object = utils.to_json_ordered(output) if json_object['status'] != "ok": err_msg = "Error found in JSON data returned by the openbmctool.py " err_msg += "command. Expected a 'status' field value of \"ok\":\n" err_msg += gp.sprint_var(json_object, 1) BuiltIn().fail(gp.sprint_error(err_msg)) return json_object['data']
def svalid_value(var_value, invalid_values=[], valid_values=[], var_name=""): r""" Return an empty string if var_value is a valid value. Otherwise, return an error string. Description of arguments: var_value The value being validated. invalid_values A list of invalid values. If var_value is equal to any of these, it is invalid. Note that if you specify anything for invalid_values (below), the valid_values list is not even processed. If you specify nothing for both invalid_values and valid_values, invalid_values will be set to a default value of [""]. valid_values A list of invalid values. var_value must be equal to one of these values to be considered valid. var_name The name of the variable whose value is passed in var_value. This parameter is normally unnecessary as this function can figure out the var_name. This is provided for Robot callers. In this scenario, we are unable to get the variable name ourselves. """ success_message = "" error_message = "" # Validate this function's arguments. len_valid_values = len(valid_values) len_invalid_values = len(invalid_values) if len_valid_values > 0 and len_invalid_values > 0: error_message += "Programmer error - You must provide either an" +\ " invalid_values list or a valid_values" +\ " list but NOT both.\n" +\ gp.sprint_var(invalid_values) +\ gp.sprint_var(valid_values) return error_message show_blanks = 1 if len_valid_values > 0: # Processing the valid_values list. if var_value in valid_values: return success_message error_message += "The following variable has an invalid" +\ " value:\n" +\ gp.sprint_varx(get_var_name(var_name), var_value, show_blanks) +\ "\nIt must be one of the following values:\n" +\ gp.sprint_varx("valid_values", valid_values, show_blanks) return error_message if len_invalid_values == 0: # Assign default value. invalid_values = [""] # Assertion: We have an invalid_values list. Processing it now. if var_value not in invalid_values: return success_message error_message += "The following variable has an invalid value:\n" +\ gp.sprint_varx(get_var_name(var_name), var_value, show_blanks) +\ "\nIt must NOT be one of the following values:\n" +\ gp.sprint_varx("invalid_values", invalid_values, show_blanks) return error_message
def gen_get_options(parser, stock_list=[]): r""" Parse the command line arguments using the parser object passed and return True/False (i.e. pass/fail). However, if gv.exit_on_error is set, simply exit the program on failure. Also set the following built in values: __builtin__.quiet This value is used by the qprint functions. __builtin__.test_mode This value is used by command processing functions. __builtin__.debug This value is used by the dprint functions. __builtin__.arg_obj This value is used by print_program_header, etc. __builtin__.parser This value is used by print_program_header, etc. Description of arguments: parser A parser object. See argparse module documentation for details. stock_list The caller can use this parameter to request certain stock parameters offered by this function. For example, this function will define a "quiet" option upon request. This includes stop help text and parm checking. The stock_list is a list of tuples each of which consists of an arg_name and a default value. Example: stock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)] """ # This is a list of stock parms that we support. master_stock_list = ["quiet", "test_mode", "debug", "loglevel"] # Process stock_list. for ix in range(0, len(stock_list)): if len(stock_list[ix]) < 1: error_message = "Programmer error - stock_list[" + str(ix) +\ "] is supposed to be a tuple containing at" +\ " least one element which is the name of" +\ " the desired stock parameter:\n" +\ gp.sprint_var(stock_list) return gv.process_error_message(error_message) if isinstance(stock_list[ix], tuple): arg_name = stock_list[ix][0] default = stock_list[ix][1] else: arg_name = stock_list[ix] default = None if arg_name not in master_stock_list: error_message = "Programmer error - arg_name \"" + arg_name +\ "\" not found found in stock list:\n" +\ gp.sprint_var(master_stock_list) return gv.process_error_message(error_message) if arg_name == "quiet": if default is None: default = 0 parser.add_argument( '--quiet', default=default, type=int, choices=[1, 0], help='If this parameter is set to "1", %(prog)s' + ' will print only essential information, i.e. it will' + ' not echo parameters, echo commands, print the total' + ' run time, etc.' + default_string) elif arg_name == "test_mode": if default is None: default = 0 parser.add_argument( '--test_mode', default=default, type=int, choices=[1, 0], help='This means that %(prog)s should go through all the' + ' motions but not actually do anything substantial.' + ' This is mainly to be used by the developer of' + ' %(prog)s.' + default_string) elif arg_name == "debug": if default is None: default = 0 parser.add_argument( '--debug', default=default, type=int, choices=[1, 0], help='If this parameter is set to "1", %(prog)s will print' + ' additional debug information. This is mainly to be' + ' used by the developer of %(prog)s.' + default_string) elif arg_name == "loglevel": if default is None: default = "info" parser.add_argument( '--loglevel', default=default, type=str, choices=[ 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'debug', 'info', 'warning', 'error', 'critical' ], help='If this parameter is set to "1", %(prog)s will print' + ' additional debug information. This is mainly to be' + ' used by the developer of %(prog)s.' + default_string) arg_obj = parser.parse_args() __builtin__.quiet = 0 __builtin__.test_mode = 0 __builtin__.debug = 0 __builtin__.loglevel = 'WARNING' for ix in range(0, len(stock_list)): if isinstance(stock_list[ix], tuple): arg_name = stock_list[ix][0] default = stock_list[ix][1] else: arg_name = stock_list[ix] default = None if arg_name == "quiet": __builtin__.quiet = arg_obj.quiet elif arg_name == "test_mode": __builtin__.test_mode = arg_obj.test_mode elif arg_name == "debug": __builtin__.debug = arg_obj.debug elif arg_name == "loglevel": __builtin__.loglevel = arg_obj.loglevel __builtin__.arg_obj = arg_obj __builtin__.parser = parser # For each command line parameter, create a corresponding global variable and assign it the appropriate # value. For example, if the command line contained "--last_name='Smith', we'll create a global variable # named "last_name" with the value "Smith". module = sys.modules['__main__'] for key in arg_obj.__dict__: setattr(module, key, getattr(__builtin__.arg_obj, key)) return True
def terminate_descendants(): r""" Terminate descendants of the current process according to the requirements layed out in global term_options variable. Note: If term_options is not null, gen_exit_function() will automatically call this function. When this function gets called, descendant processes may be running and may be printing to the same stdout stream being used by this process. If this function writes directly to stdout, its output can be interspersed with any output generated by descendant processes. This makes it very difficult to interpret the output. In order solve this problem, the activity of this process will be logged to a temporary file. After descendant processes have been terminated successfully, the temporary file will be printed to stdout and then deleted. However, if this function should fail to complete (i.e. get hung waiting for descendants to terminate gracefully), the temporary file will not be deleted and can be used by the developer for debugging. If no descendant processes are found, this function will return before creating the temporary file. Note that a general principal being observed here is that each process is responsible for the children it produces. """ message = "\n" + gp.sprint_dashes(width=120) \ + gp.sprint_executing() + "\n" current_process = psutil.Process() descendants, descendant_pids, process_report = get_descendant_info( current_process) if not descendants: # If there are no descendants, then we have nothing to do. return terminate_descendants_temp_file_path = gm.create_temp_file_path() gp.print_vars(terminate_descendants_temp_file_path) message += gp.sprint_varx("pgm_name", gp.pgm_name) \ + gp.sprint_vars(term_options) \ + process_report # Process the termination requests: if term_options['term_requests'] == 'children': term_processes = current_process.children(recursive=False) term_pids = [str(process.pid) for process in term_processes] elif term_options['term_requests'] == 'descendants': term_processes = descendants term_pids = descendant_pids else: # Process term requests by pgm_names. term_processes = [] for pgm_name in term_options['term_requests']['pgm_names']: term_processes.extend( select_processes_by_pgm_name(descendants, pgm_name)) term_pids = [str(process.pid) for process in term_processes] message += gp.sprint_timen("Processes to be terminated:") \ + gp.sprint_var(term_pids) for process in term_processes: process.terminate() message += gp.sprint_timen("Waiting on the following pids: " + ' '.join(descendant_pids)) gm.append_file(terminate_descendants_temp_file_path, message) psutil.wait_procs(descendants) # Checking after the fact to see whether any descendant processes are still alive. If so, a process # report showing this will be included in the output. descendants, descendant_pids, process_report = get_descendant_info( current_process) if descendants: message = "\n" + gp.sprint_timen("Not all of the processes terminated:") \ + process_report gm.append_file(terminate_descendants_temp_file_path, message) message = gp.sprint_dashes(width=120) gm.append_file(terminate_descendants_temp_file_path, message) gp.print_file(terminate_descendants_temp_file_path) os.remove(terminate_descendants_temp_file_path)
robot_prefix = "r" robot_func_names =\ [ 'print_error_report', 'print_pgm_header', 'print_issuing_keyword', 'print_vars', 'print_auto_vars' ] func_names = gp.func_names + robot_func_names explicit_definitions = ['print', 'printn'] func_names = list(my_ord_dict.fromkeys(func_names)) if gen_robot_print_debug: rprintn() BuiltIn().log_to_console(gp.sprint_var(func_names), no_newline=True) rprintn() for func_name in func_names: if func_name not in explicit_definitions: # The print_var function's job is to figure out the name of arg 1 and # then call print_varx. This is not currently supported for robot # programs. Though it IS supported for python modules. if func_name == "print_error" or func_name == "print_error_report": output_stream = "STDERR" else: output_stream = "STDIN" if func_name in robot_func_names: object_name = "__import__(__name__)" else:
def obmc_boot_test_py(loc_boot_stack=None, loc_stack_mode=None, loc_quiet=None): r""" Do main program processing. """ global save_stack # Process function parms. for parm_name in main_func_parm_list: # Get parm's value. cmd_buf = "parm_value = loc_" + parm_name exec(cmd_buf) gp.dpvar(parm_name) gp.dpvar(parm_value) if parm_value is None: # Parm was not specified by the calling function so set it to its # corresponding global value. cmd_buf = "loc_" + parm_name + " = BuiltIn().get_variable_value" +\ "(\"${" + parm_name + "}\")" gp.dpissuing(cmd_buf) exec(cmd_buf) else: # Save the global value on a stack. cmd_buf = "save_stack.push(BuiltIn().get_variable_value(\"${" +\ parm_name + "}\"), \"" + parm_name + "\")" gp.dpissuing(cmd_buf) exec(cmd_buf) # Set the global value to the passed value. cmd_buf = "BuiltIn().set_global_variable(\"${" + parm_name +\ "}\", loc_" + parm_name + ")" gp.dpissuing(cmd_buf) exec(cmd_buf) gp.dprintn(save_stack.sprint_obj()) setup() if ffdc_only: gp.qprint_timen("Caller requested ffdc_only.") pre_boot_plug_in_setup() grk.run_key_u("my_ffdc") return # Process caller's boot_stack. while (len(boot_stack) > 0): test_loop_body() gp.qprint_timen("Finished processing stack.") # Process caller's boot_list. if len(boot_list) > 0: for ix in range(1, max_num_tests + 1): test_loop_body() gp.qprint_timen("Completed all requested boot tests.") boot_pass, boot_fail = boot_results.return_total_pass_fail() if boot_fail > boot_fail_threshold: error_message = "Boot failures exceed the boot failure" +\ " threshold:\n" +\ gp.sprint_var(boot_fail) +\ gp.sprint_var(boot_fail_threshold) BuiltIn().fail(gp.sprint_error(error_message))
def shell_cmd(command_string, quiet=None, print_output=None, show_err=1, test_mode=0, time_out=None, max_attempts=1, retry_sleep_time=5, allowed_shell_rcs=[0], ignore_err=None, return_stderr=0, fork=0): r""" Run the given command string in a shell and return a tuple consisting of the shell return code and the output. Description of argument(s): command_string The command string to be run in a shell (e.g. "ls /tmp"). quiet If set to 0, this function will print "Issuing: <cmd string>" to stdout. When the quiet argument is set to None, this function will assign a default value by searching upward in the stack for the quiet variable value. If no such value is found, quiet is set to 0. print_output If this is set, this function will print the stdout/stderr generated by the shell command to stdout. show_err If show_err is set, this function will print a standardized error report if the shell command fails (i.e. if the shell command returns a shell_rc that is not in allowed_shell_rcs). Note: Error text is only printed if ALL attempts to run the command_string fail. In other words, if the command execution is ultimately successful, initial failures are hidden. test_mode If test_mode is set, this function will not actually run the command. If print_output is also set, this function will print "(test_mode) Issuing: <cmd string>" to stdout. A caller should call shell_cmd directly if they wish to have the command string run unconditionally. They should call the t_shell_cmd wrapper (defined below) if they wish to run the command string only if the prevailing test_mode variable is set to 0. time_out A time-out value expressed in seconds. If the command string has not finished executing within <time_out> seconds, it will be halted and counted as an error. max_attempts The max number of attempts that should be made to run the command string. retry_sleep_time The number of seconds to sleep between attempts. allowed_shell_rcs A list of integers indicating which shell_rc values are not to be considered errors. ignore_err Ignore error means that a failure encountered by running the command string will not be raised as a python exception. When the ignore_err argument is set to None, this function will assign a default value by searching upward in the stack for the ignore_err variable value. If no such value is found, ignore_err is set to 1. return_stderr If return_stderr is set, this function will process the stdout and stderr streams from the shell command separately. In such a case, the tuple returned by this function will consist of three values rather than just two: rc, stdout, stderr. fork Run the command string asynchronously (i.e. don't wait for status of the child process and don't try to get stdout/stderr). """ # Assign default values to some of the arguments to this function. quiet = int(gm.dft(quiet, gp.get_stack_var('quiet', 0))) print_output = int(gm.dft(print_output, not quiet)) show_err = int(show_err) global_ignore_err = gp.get_var_value(ignore_err, 1) stack_ignore_err = gp.get_stack_var('ignore_err', global_ignore_err) ignore_err = int(gm.dft(ignore_err, gm.dft(stack_ignore_err, 1))) err_msg = gv.valid_value(command_string) if err_msg != "": raise ValueError(err_msg) if not quiet: gp.print_issuing(command_string, test_mode) if test_mode: if return_stderr: return 0, "", "" else: return 0, "" # Convert each list entry to a signed value. allowed_shell_rcs = fa.source_to_object(allowed_shell_rcs) allowed_shell_rcs = [gm.to_signed(x) for x in allowed_shell_rcs] if return_stderr: stderr = subprocess.PIPE else: stderr = subprocess.STDOUT shell_rc = 0 out_buf = "" err_buf = "" # Write all output to func_history_stdout rather than directly to stdout. # This allows us to decide what to print after all attempts to run the # command string have been made. func_history_stdout will contain the # complete stdout history from the current invocation of this function. func_history_stdout = "" for attempt_num in range(1, max_attempts + 1): sub_proc = subprocess.Popen(command_string, bufsize=1, shell=True, executable='/bin/bash', stdout=subprocess.PIPE, stderr=stderr) out_buf = "" err_buf = "" # Output from this loop iteration is written to func_stdout for later # processing. func_stdout = "" if fork: break command_timed_out = False if time_out is not None: # Designate a SIGALRM handling function and set alarm. signal.signal(signal.SIGALRM, shell_cmd_timed_out) signal.alarm(time_out) try: if return_stderr: for line in sub_proc.stderr: try: err_buf += line except TypeError: line = line.decode("utf-8") err_buf += line if not print_output: continue func_stdout += line for line in sub_proc.stdout: try: out_buf += line except TypeError: line = line.decode("utf-8") out_buf += line if not print_output: continue func_stdout += line except IOError: command_timed_out = True sub_proc.communicate() shell_rc = sub_proc.returncode # Restore the original SIGALRM handler and clear the alarm. signal.signal(signal.SIGALRM, original_sigalrm_handler) signal.alarm(0) if shell_rc in allowed_shell_rcs: break err_msg = "The prior shell command failed.\n" if quiet: err_msg += gp.sprint_var(command_string) if command_timed_out: err_msg += gp.sprint_var(command_timed_out) err_msg += gp.sprint_var(time_out) err_msg += gp.sprint_varx("child_pid", sub_proc.pid) err_msg += gp.sprint_var(attempt_num) err_msg += gp.sprint_var(shell_rc, gp.hexa()) err_msg += gp.sprint_var(allowed_shell_rcs, gp.hexa()) if not print_output: if return_stderr: err_msg += "err_buf:\n" + err_buf err_msg += "out_buf:\n" + out_buf if show_err: func_stdout += gp.sprint_error_report(err_msg) func_history_stdout += func_stdout if attempt_num < max_attempts: func_history_stdout += gp.sprint_issuing("time.sleep(" + str(retry_sleep_time) + ")") time.sleep(retry_sleep_time) if shell_rc not in allowed_shell_rcs: func_stdout = func_history_stdout gp.gp_print(func_stdout) if shell_rc not in allowed_shell_rcs: if not ignore_err: if robot_env: BuiltIn().fail(err_msg) else: raise ValueError("The prior shell command failed.\n") if return_stderr: return shell_rc, out_buf, err_buf else: return shell_rc, out_buf
def obmc_boot_test_py(loc_boot_stack=None, loc_stack_mode=None, loc_quiet=None): r""" Do main program processing. """ global save_stack # Process function parms. for parm_name in main_func_parm_list: # Get parm's value. cmd_buf = "parm_value = loc_" + parm_name exec(cmd_buf) gp.dpvar(parm_name) gp.dpvar(parm_value) if parm_value is None: # Parm was not specified by the calling function so set it to its # corresponding global value. cmd_buf = "loc_" + parm_name + " = BuiltIn().get_variable_value" +\ "(\"${" + parm_name + "}\")" gp.dpissuing(cmd_buf) exec(cmd_buf) else: # Save the global value on a stack. cmd_buf = "save_stack.push(BuiltIn().get_variable_value(\"${" +\ parm_name + "}\"), \"" + parm_name + "\")" gp.dpissuing(cmd_buf) exec(cmd_buf) # Set the global value to the passed value. cmd_buf = "BuiltIn().set_global_variable(\"${" + parm_name +\ "}\", loc_" + parm_name + ")" gp.dpissuing(cmd_buf) exec(cmd_buf) gp.dprintn(save_stack.sprint_obj()) setup() init_boot_pass, init_boot_fail = boot_results.return_total_pass_fail() if ffdc_only: gp.qprint_timen("Caller requested ffdc_only.") pre_boot_plug_in_setup() grk.run_key_u("my_ffdc") return # Process caller's boot_stack. while (len(boot_stack) > 0): test_loop_body() gp.qprint_timen("Finished processing stack.") # Process caller's boot_list. if len(boot_list) > 0: for ix in range(1, max_num_tests + 1): test_loop_body() gp.qprint_timen("Completed all requested boot tests.") boot_pass, boot_fail = boot_results.return_total_pass_fail() new_fail = boot_fail - init_boot_fail if new_fail > boot_fail_threshold: error_message = "Boot failures exceed the boot failure" +\ " threshold:\n" +\ gp.sprint_var(new_fail) +\ gp.sprint_var(boot_fail_threshold) BuiltIn().fail(gp.sprint_error(error_message))
def run_boot(boot): r""" Run the specified boot. Description of arguments: boot The name of the boot test to be performed. """ global state print_test_start_message(boot) plug_in_setup() rc, shell_rc, failed_plug_in_name = \ grpi.rprocess_plug_in_packages(call_point="pre_boot") if rc != 0: error_message = "Plug-in failed with non-zero return code.\n" +\ gp.sprint_var(rc, 1) BuiltIn().fail(gp.sprint_error(error_message)) if test_mode: # In test mode, we'll pretend the boot worked by assigning its # required end state to the default state value. state = st.strip_anchor_state(boot_table[boot]['end']) else: # Assertion: We trust that the state data was made fresh by the # caller. gp.qprintn() if boot_table[boot]['method_type'] == "keyword": rk.my_run_keywords(boot_table[boot].get('lib_file_path', ''), boot_table[boot]['method'], quiet=quiet) if boot_table[boot]['bmc_reboot']: st.wait_for_comm_cycle(int(state['epoch_seconds'])) plug_in_setup() rc, shell_rc, failed_plug_in_name = \ grpi.rprocess_plug_in_packages(call_point="post_reboot") if rc != 0: error_message = "Plug-in failed with non-zero return code.\n" error_message += gp.sprint_var(rc, 1) BuiltIn().fail(gp.sprint_error(error_message)) else: match_state = st.anchor_state(state) del match_state['epoch_seconds'] # Wait for the state to change in any way. st.wait_state(match_state, wait_time=state_change_timeout, interval="10 seconds", invert=1) gp.qprintn() if boot_table[boot]['end']['chassis'] == "Off": boot_timeout = power_off_timeout else: boot_timeout = power_on_timeout st.wait_state(boot_table[boot]['end'], wait_time=boot_timeout, interval="10 seconds") plug_in_setup() rc, shell_rc, failed_plug_in_name = \ grpi.rprocess_plug_in_packages(call_point="post_boot") if rc != 0: error_message = "Plug-in failed with non-zero return code.\n" +\ gp.sprint_var(rc, 1) BuiltIn().fail(gp.sprint_error(error_message))
def obmc_boot_test_py(loc_boot_stack=None, loc_stack_mode=None, loc_quiet=None): r""" Do main program processing. """ global save_stack ga.set_term_options(term_requests={'pgm_names': ['process_plug_in_packages.py']}) gp.dprintn() # Process function parms. for parm_name in main_func_parm_list: # Get parm's value. parm_value = eval("loc_" + parm_name) gp.dpvars(parm_name, parm_value) if parm_value is not None: # Save the global value on a stack. cmd_buf = "save_stack.push(BuiltIn().get_variable_value(\"${" +\ parm_name + "}\"), \"" + parm_name + "\")" gp.dpissuing(cmd_buf) exec(cmd_buf) # Set the global value to the passed value. cmd_buf = "BuiltIn().set_global_variable(\"${" + parm_name +\ "}\", loc_" + parm_name + ")" gp.dpissuing(cmd_buf) exec(cmd_buf) gp.dprintn(save_stack.sprint_obj()) setup() init_boot_pass, init_boot_fail = boot_results.return_total_pass_fail() if ffdc_only: gp.qprint_timen("Caller requested ffdc_only.") if do_pre_boot_plug_in_setup: pre_boot_plug_in_setup() grk.run_key_u("my_ffdc") return if delete_errlogs: # print error logs before delete status, error_logs = grk.run_key_u("Get Error Logs") pels = pel.peltool("-l", ignore_err=1) log.print_error_logs(error_logs, "AdditionalData Message Severity") gp.qprint_var(pels) # Delete errlogs prior to doing any boot tests. grk.run_key(delete_errlogs_cmd, ignore=1) grk.run_key(delete_bmcdump_cmd, ignore=1) # Process caller's boot_stack. while (len(boot_stack) > 0): test_loop_body() gp.qprint_timen("Finished processing stack.") post_stack() # Process caller's boot_list. if len(boot_list) > 0: for ix in range(1, max_num_tests + 1): test_loop_body() gp.qprint_timen("Completed all requested boot tests.") boot_pass, boot_fail = boot_results.return_total_pass_fail() new_fail = boot_fail - init_boot_fail if new_fail > boot_fail_threshold: error_message = "Boot failures exceed the boot failure" +\ " threshold:\n" +\ gp.sprint_var(new_fail) +\ gp.sprint_var(boot_fail_threshold) BuiltIn().fail(gp.sprint_error(error_message))
def execute_ssh_command(cmd_buf, open_connection_args={}, login_args={}, print_out=0, print_err=0, ignore_err=1, fork=0, quiet=None, test_mode=None): r""" Run the given command in an SSH session and return the stdout, stderr and the return code. If there is no open SSH connection, this function will connect and login. Likewise, if the caller has not yet logged in to the connection, this function will do the login. NOTE: There is special handling when open_connection_args['alias'] equals "device_connection". - A write, rather than an execute_command, is done. - Only stdout is returned (no stderr or rc). - print_err, ignore_err and fork are not supported. Description of arguments: cmd_buf The command string to be run in an SSH session. open_connection_args A dictionary of arg names and values which are legal to pass to the SSHLibrary open_connection function as parms/args. At a minimum, this should contain a 'host' entry. login_args A dictionary containing the key/value pairs which are acceptable to the SSHLibrary login function as parms/args. At a minimum, this should contain a 'username' and a 'password' entry. print_out If this is set, this function will print the stdout/stderr generated by the shell command. print_err If show_err is set, this function will print a standardized error report if the shell command returns non-zero. ignore_err Indicates that errors encountered on the sshlib.execute_command are to be ignored. fork Indicates that sshlib.start is to be used rather than sshlib.execute_command. quiet Indicates whether this function should run the pissuing() function which prints an "Issuing: <cmd string>" to stdout. This defaults to the global quiet value. test_mode If test_mode is set, this function will not actually run the command. This defaults to the global test_mode value. """ gp.lprint_executing() # Obtain default values. quiet = int(gp.get_var_value(quiet, 0)) test_mode = int(gp.get_var_value(test_mode, 0)) if not quiet: gp.pissuing(cmd_buf, test_mode) gp.lpissuing(cmd_buf, test_mode) if test_mode: return "", "", 0 global sshlib max_exec_cmd_attempts = 2 # Look for existing SSH connection. # Prepare a search connection dictionary. search_connection_args = open_connection_args.copy() # Remove keys that don't work well for searches. search_connection_args.pop("timeout", None) connection = find_connection(search_connection_args) if connection: gp.lprint_timen("Found the following existing connection:") gp.lprintn(sprint_connection(connection)) if connection.alias == "": index_or_alias = connection.index else: index_or_alias = connection.alias gp.lprint_timen("Switching to existing connection: \"" + str(index_or_alias) + "\".") sshlib.switch_connection(index_or_alias) else: gp.lprint_timen("Connecting to " + open_connection_args['host'] + ".") cix = sshlib.open_connection(**open_connection_args) try: login_ssh(login_args) except Exception as login_exception: except_type, except_value, except_traceback = sys.exc_info() rc = 1 stderr = str(except_value) stdout = "" max_exec_cmd_attempts = 0 for exec_cmd_attempt_num in range(1, max_exec_cmd_attempts + 1): gp.lprint_var(exec_cmd_attempt_num) try: if fork: sshlib.start_command(cmd_buf) else: if open_connection_args['alias'] == "device_connection": stdout = sshlib.write(cmd_buf) stderr = "" rc = 0 else: stdout, stderr, rc = \ sshlib.execute_command(cmd_buf, return_stdout=True, return_stderr=True, return_rc=True) except Exception as execute_exception: except_type, except_value, except_traceback = sys.exc_info() gp.lprint_var(except_type) gp.lprint_varx("except_value", str(except_value)) if except_type is exceptions.AssertionError and\ re.match(r"Connection not open", str(except_value)): login_ssh(login_args) # Now we must continue to next loop iteration to retry the # execute_command. continue if (except_type is paramiko.ssh_exception.SSHException and re.match(r"SSH session not active", str(except_value))) or\ (except_type is socket.error and re.match(r"\[Errno 104\] Connection reset by peer", str(except_value))): # Close and re-open a connection. # Note: close_connection() doesn't appear to get rid of the # connection. It merely closes it. Since there is a concern # about over-consumption of resources, we use # close_all_connections() which also gets rid of all # connections. gp.lprint_timen("Closing all connections.") sshlib.close_all_connections() gp.lprint_timen("Connecting to " + open_connection_args['host'] + ".") cix = sshlib.open_connection(**open_connection_args) login_ssh(login_args) continue # We do not handle any other RuntimeErrors so we will raise the # exception again. sshlib.close_all_connections() raise(execute_exception) # If we get to this point, the command was executed. break if fork: return if rc != 0 and print_err: gp.print_var(rc, 1) if not print_out: gp.print_var(stderr) gp.print_var(stdout) if print_out: gp.printn(stderr + stdout) if not ignore_err: message = gp.sprint_error("The prior SSH" + " command returned a non-zero return" + " code:\n" + gp.sprint_var(rc, 1) + stderr + "\n") BuiltIn().should_be_equal(rc, 0, message) if open_connection_args['alias'] == "device_connection": return stdout return stdout, stderr, rc
def svalid_range(var_value, valid_range=[], var_name=""): r""" Return an empty string if var_value is within the range. Otherwise, return an error string. Description of arguments: var_value The value being validated. This value must be an integer. valid_range A list comprised of one or two elements which are the lower and upper ends of a range. These values must be integers except where noted. Valid specifications may be of the following forms: [lower, upper], [lower] or [None, upper]. var_name The name of the variable whose value is passed in var_value. This parameter is normally unnecessary as this function can figure out the var_name. This is provided for Robot callers. In this scenario, we are unable to get the variable name ourselves. """ error_message = "" # Validate this function's parms: # First, ensure that the value is an integer. error_message = svalid_integer(var_value, var_name) if not error_message == "": return error_message var_value = int(var_value) len_valid_range = len(valid_range) if len_valid_range == 0 or len_valid_range > 2: error_message += "Programmer error - For the valid_range parameter," +\ " you must provide a list consisting of one or two" +\ " elements.\n" +\ gp.sprint_var(valid_range) return error_message if len_valid_range == 1 or valid_range[0] is not None: # Make sure lower valid_range value is an integer. error_message = svalid_integer(valid_range[0], "valid_range[0]") if not error_message == "": error_message = "Programmer error:\n" + error_message return error_message if valid_range[0] is not None: valid_range[0] = int(valid_range[0]) if len_valid_range == 2: # Make sure upper valid_range value is an integer. error_message = svalid_integer(valid_range[1], "valid_range[1]") if not error_message == "": error_message = "Programmer error:\n" + error_message return error_message valid_range[1] = int(valid_range[1]) if valid_range[0] is not None and valid_range[0] > valid_range[1]: error_message = "Programmer error - In the following range, the" +\ " lower limit is greater than the upper" +\ " limit:\n" + gp.sprint_varx("valid_range", valid_range) return error_message if len_valid_range == 1: if var_value < valid_range[0]: error_message += "The following variable is not within the" +\ " expected range:\n" +\ gp.sprint_varx(get_var_name(var_name), var_value) +\ gp.sprint_varx("valid_range", str(valid_range[0]) + "..") return error_message return error_message if valid_range[0] is None: if var_value > valid_range[1]: error_message += "The following variable is not within the" +\ " expected range:\n" +\ gp.sprint_varx(get_var_name(var_name), var_value) +\ gp.sprint_varx("valid_range", ".." + str(valid_range[1])) return error_message if var_value < valid_range[0] or var_value > valid_range[1]: error_message += "The following variable is not within the expected" +\ " range:\n" +\ gp.sprint_varx(get_var_name(var_name), var_value) +\ gp.sprint_varx("valid_range", str(valid_range[0]) + ".." + str(valid_range[1])) return error_message return error_message
def gen_get_options(parser, stock_list=[]): r""" Parse the command line arguments using the parser object passed and return True/False (i.e. pass/fail). However, if gv.exit_on_error is set, simply exit the program on failure. Also set the following built in values: __builtin__.quiet This value is used by the qprint functions. __builtin__.test_mode This value is used by command processing functions. __builtin__.debug This value is used by the dprint functions. __builtin__.arg_obj This value is used by print_program_header, etc. __builtin__.parser This value is used by print_program_header, etc. Description of arguments: parser A parser object. See argparse module documentation for details. stock_list The caller can use this parameter to request certain stock parameters offered by this function. For example, this function will define a "quiet" option upon request. This includes stop help text and parm checking. The stock_list is a list of tuples each of which consists of an arg_name and a default value. Example: stock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)] """ # This is a list of stock parms that we support. master_stock_list = ["quiet", "test_mode", "debug", "loglevel"] # Process stock_list. for ix in range(0, len(stock_list)): if len(stock_list[ix]) < 1: error_message = "Programmer error - stock_list[" + str(ix) +\ "] is supposed to be a tuple containing at" +\ " least one element which is the name of" +\ " the desired stock parameter:\n" +\ gp.sprint_var(stock_list) return gv.process_error_message(error_message) if isinstance(stock_list[ix], tuple): arg_name = stock_list[ix][0] default = stock_list[ix][1] else: arg_name = stock_list[ix] default = None if arg_name not in master_stock_list: error_message = "Programmer error - arg_name \"" + arg_name +\ "\" not found found in stock list:\n" +\ gp.sprint_var(master_stock_list) return gv.process_error_message(error_message) if arg_name == "quiet": if default is None: default = 0 parser.add_argument( '--quiet', default=default, type=int, choices=[1, 0], help='If this parameter is set to "1", %(prog)s' + ' will print only essential information, i.e. it will' + ' not echo parameters, echo commands, print the total' + ' run time, etc.' + default_string) elif arg_name == "test_mode": if default is None: default = 0 parser.add_argument( '--test_mode', default=default, type=int, choices=[1, 0], help='This means that %(prog)s should go through all the' + ' motions but not actually do anything substantial.' + ' This is mainly to be used by the developer of' + ' %(prog)s.' + default_string) elif arg_name == "debug": if default is None: default = 0 parser.add_argument( '--debug', default=default, type=int, choices=[1, 0], help='If this parameter is set to "1", %(prog)s will print' + ' additional debug information. This is mainly to be' + ' used by the developer of %(prog)s.' + default_string) elif arg_name == "loglevel": if default is None: default = "info" parser.add_argument( '--loglevel', default=default, type=str, choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'debug', 'info', 'warning', 'error', 'critical'], help='If this parameter is set to "1", %(prog)s will print' + ' additional debug information. This is mainly to be' + ' used by the developer of %(prog)s.' + default_string) arg_obj = parser.parse_args() __builtin__.quiet = 0 __builtin__.test_mode = 0 __builtin__.debug = 0 __builtin__.loglevel = 'WARNING' for ix in range(0, len(stock_list)): if isinstance(stock_list[ix], tuple): arg_name = stock_list[ix][0] default = stock_list[ix][1] else: arg_name = stock_list[ix] default = None if arg_name == "quiet": __builtin__.quiet = arg_obj.quiet elif arg_name == "test_mode": __builtin__.test_mode = arg_obj.test_mode elif arg_name == "debug": __builtin__.debug = arg_obj.debug elif arg_name == "loglevel": __builtin__.loglevel = arg_obj.loglevel __builtin__.arg_obj = arg_obj __builtin__.parser = parser # For each command line parameter, create a corresponding global variable # and assign it the appropriate value. For example, if the command line # contained "--last_name='Smith', we'll create a global variable named # "last_name" with the value "Smith". module = sys.modules['__main__'] for key in arg_obj.__dict__: setattr(module, key, getattr(__builtin__.arg_obj, key)) return True
def cmd_fnc(cmd_buf, quiet=None, test_mode=None, debug=0, print_output=1, show_err=1, return_stderr=0, ignore_err=1): r""" Run the given command in a shell and return the shell return code and the output. Description of arguments: cmd_buf The command string to be run in a shell. quiet Indicates whether this function should run the print_issuing() function which prints "Issuing: <cmd string>" to stdout. test_mode If test_mode is set, this function will not actually run the command. If print_output is set, it will print "(test_mode) Issuing: <cmd string>" to stdout. debug If debug is set, this function will print extra debug info. print_output If this is set, this function will print the stdout/stderr generated by the shell command. show_err If show_err is set, this function will print a standardized error report if the shell command returns non-zero. return_stderr If return_stderr is set, this function will process the stdout and stderr streams from the shell command separately. It will also return stderr in addition to the return code and the stdout. """ # Determine default values. quiet = int(gm.global_default(quiet, 0)) test_mode = int(gm.global_default(test_mode, 0)) if debug: gp.print_vars(cmd_buf, quiet, test_mode, debug) err_msg = gv.valid_value(cmd_buf) if err_msg != "": raise ValueError(err_msg) if not quiet: gp.pissuing(cmd_buf, test_mode) if test_mode: if return_stderr: return 0, "", "" else: return 0, "" if return_stderr: err_buf = "" stderr = subprocess.PIPE else: stderr = subprocess.STDOUT sub_proc = subprocess.Popen(cmd_buf, bufsize=1, shell=True, executable='/bin/bash', stdout=subprocess.PIPE, stderr=stderr) out_buf = "" if return_stderr: for line in sub_proc.stderr: try: err_buf += line except TypeError: line = line.decode("utf-8") err_buf += line if not print_output: continue gp.gp_print(line) for line in sub_proc.stdout: try: out_buf += line except TypeError: line = line.decode("utf-8") out_buf += line if not print_output: continue gp.gp_print(line) if print_output and not robot_env: sys.stdout.flush() sub_proc.communicate() shell_rc = sub_proc.returncode if shell_rc != 0: err_msg = "The prior shell command failed.\n" err_msg += gp.sprint_var(shell_rc, gp.hexa()) if not print_output: err_msg += "out_buf:\n" + out_buf if show_err: gp.print_error_report(err_msg) if not ignore_err: if robot_env: BuiltIn().fail(err_msg) else: raise ValueError(err_msg) if return_stderr: return shell_rc, out_buf, err_buf else: return shell_rc, out_buf
def validate_parms(): r""" Validate all program parameters. """ process_pgm_parms() gp.qprintn() global openbmc_model gv.valid_value(openbmc_host) gv.valid_value(openbmc_username) gv.valid_value(openbmc_password) gv.valid_value(rest_username) gv.valid_value(rest_password) gv.valid_value(ipmi_username) gv.valid_value(ipmi_password) if os_host != "": gv.valid_value(os_username) gv.valid_value(os_password) if pdu_host != "": gv.valid_value(pdu_username) gv.valid_value(pdu_password) gv.valid_integer(pdu_slot_no) if openbmc_serial_host != "": gv.valid_integer(openbmc_serial_port) if openbmc_model == "": status, ret_values =\ grk.run_key_u("Get BMC System Model") openbmc_model = ret_values BuiltIn().set_global_variable("${openbmc_model}", openbmc_model) gv.valid_value(openbmc_model) gv.valid_integer(max_num_tests) gv.valid_integer(boot_pass) gv.valid_integer(boot_fail) plug_in_packages_list = grpi.rvalidate_plug_ins(plug_in_dir_paths) BuiltIn().set_global_variable("${plug_in_packages_list}", plug_in_packages_list) gv.valid_value(stack_mode, valid_values=['normal', 'skip']) if len(boot_list) == 0 and len(boot_stack) == 0 and not ffdc_only: error_message = "You must provide either a value for either the" +\ " boot_list or the boot_stack parm.\n" BuiltIn().fail(gp.sprint_error(error_message)) valid_boot_list(boot_list, valid_boot_types) valid_boot_list(boot_stack, valid_boot_types) selected_PDU_boots = list( set(boot_list + boot_stack) & set(boot_lists['PDU_reboot'])) if len(selected_PDU_boots) > 0 and pdu_host == "": error_message = "You have selected the following boots which" +\ " require a PDU host but no value for pdu_host:\n" error_message += gp.sprint_var(selected_PDU_boots) error_message += gp.sprint_var(pdu_host, fmt=gp.blank()) BuiltIn().fail(gp.sprint_error(error_message)) return
def cmd_fnc(cmd_buf, quiet=None, test_mode=None, debug=0, print_output=1, show_err=1): r""" Run the given command in a shell and return the shell return code. Description of arguments: cmd_buf The command string to be run in a shell. quiet Indicates whether this function should run the pissuing() function prints an "Issuing: <cmd string>" to stdout. test_mode If test_mode is set, this function will not actually run the command. debug If debug is set, this function will print extra debug info. print_output If this is set, this function will print the stdout/stderr generated by the shell command. show_err If show_err is set, this function will print a standardized error report if the shell command returns non-zero. """ quiet = int(gm.global_default(quiet, 0)) test_mode = int(gm.global_default(test_mode, 0)) if debug: gp.print_vars(cmd_buf, quiet, test_mode, debug) err_msg = gv.svalid_value(cmd_buf) if err_msg != "": raise ValueError(err_msg) if not quiet: gp.pissuing(cmd_buf, test_mode) if test_mode: return 0, "" sub_proc = subprocess.Popen(cmd_buf, bufsize=1, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) out_buf = "" for line in sub_proc.stdout: out_buf += line if not print_output: continue if robot_env: grp.rprint(line) else: sys.stdout.write(line) if print_output and not robot_env: sys.stdout.flush() sub_proc.communicate() shell_rc = sub_proc.returncode if shell_rc != 0 and show_err: if robot_env: grp.rprint_error_report("The prior command failed.\n" + gp.sprint_var(shell_rc, 1)) else: gp.print_error_report("The prior command failed.\n" + gp.sprint_var(shell_rc, 1)) return shell_rc, out_buf
def get_os_state(os_host="", os_username="", os_password="", req_states=default_os_req_states, os_up=True, quiet=None): r""" Get component states for the operating system such as ping, login, etc, put them into a dictionary and return them to the caller. Note that all substate values are strings. Description of argument(s): os_host The DNS name or IP address of the operating system. This defaults to global ${OS_HOST}. os_username The username to be used to login to the OS. This defaults to global ${OS_USERNAME}. os_password The password to be used to login to the OS. This defaults to global ${OS_PASSWORD}. req_states This is a list of states whose values are being requested by the caller. os_up If the caller knows that the os can't possibly be up, it can improve performance by passing os_up=False. This function will then simply return default values for all requested os sub states. quiet Indicates whether status details (e.g. curl commands) should be written to the console. Defaults to either global value of ${QUIET} or to 1. """ quiet = int(gp.get_var_value(quiet, 0)) # Set parm defaults where necessary and validate all parms. if os_host == "": os_host = BuiltIn().get_variable_value("${OS_HOST}") error_message = gv.valid_value(os_host, invalid_values=[None, ""]) if error_message != "": BuiltIn().fail(gp.sprint_error(error_message)) if os_username == "": os_username = BuiltIn().get_variable_value("${OS_USERNAME}") error_message = gv.valid_value(os_username, invalid_values=[None, ""]) if error_message != "": BuiltIn().fail(gp.sprint_error(error_message)) if os_password == "": os_password = BuiltIn().get_variable_value("${OS_PASSWORD}") error_message = gv.valid_value(os_password, invalid_values=[None, ""]) if error_message != "": BuiltIn().fail(gp.sprint_error(error_message)) invalid_req_states = [sub_state for sub_state in req_states if sub_state not in valid_os_req_states] if len(invalid_req_states) > 0: error_message = "The following req_states are not supported:\n" +\ gp.sprint_var(invalid_req_states) BuiltIn().fail(gp.sprint_error(error_message)) # Initialize all substate values supported by this function. os_ping = 0 os_login = 0 os_run_cmd = 0 if os_up: if 'os_ping' in req_states: # See if the OS pings. rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + os_host, print_output=0, show_err=0, ignore_err=1) if rc == 0: os_ping = 1 # Programming note: All attributes which do not require an ssh login # should have been processed by this point. master_req_login = ['os_login', 'os_run_cmd'] req_login = [sub_state for sub_state in req_states if sub_state in master_req_login] must_login = (len(req_login) > 0) if must_login: output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet, ignore_err=1, time_out=20) if rc == 0: os_login = 1 os_run_cmd = 1 else: gp.dprint_vars(output, stderr) gp.dprint_vars(rc, 1) os_state = DotDict() for sub_state in req_states: cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")" exec(cmd_buf) return os_state
def valid_list(var_value, valid_values=[], invalid_values=[], required_values=[], fail_on_empty=False, var_name=None): r""" The variable value is valid if it is a list where each entry can be found in the valid_values list or if none of its values can be found in the invalid_values list or if all of the values in the required_values list can be found in var_value. The caller may only specify one of these 3 arguments: valid_values, invalid_values, required_values. Description of argument(s): var_value The value being validated. valid_values A list of valid values. Each element in the var_value list must be equal to one of these values to be considered valid. invalid_values A list of invalid values. If any element in var_value is equal to any of the values in this argument, var_value is considered invalid. required_values Every value in required_values must be found in var_value. Otherwise, var_value is considered invalid. fail_on_empty Indicates that an empty list for the variable value should be considered an error. """ error_message = "" # Validate this function's arguments. if not (bool(len(valid_values)) ^ bool(len(invalid_values)) ^ bool(len(required_values))): error_message += "Programmer error - You must provide only one of the" error_message += " following: valid_values, invalid_values," error_message += " required_values.\n" error_message += gp.sprint_var(invalid_values, gp.show_type()) error_message += gp.sprint_var(valid_values, gp.show_type()) error_message += gp.sprint_var(required_values, gp.show_type()) return process_error_message(error_message) if type(var_value) is not list: var_name = get_var_name(var_name) error_message = valid_type(var_value, list, var_name=var_name) if error_message: return process_error_message(error_message) if fail_on_empty and len(var_value) == 0: var_name = get_var_name(var_name) error_message += "Invalid empty list:\n" error_message += gp.sprint_varx(var_name, var_value, gp.show_type()) return process_error_message(error_message) if len(required_values): found_error = 0 display_required_values = list(required_values) for ix in range(0, len(required_values)): if required_values[ix] not in var_value: found_error = 1 display_required_values[ix] = \ str(display_required_values[ix]) + "*" if found_error: var_name = get_var_name(var_name) error_message += "The following list is invalid:\n" error_message += gp.sprint_varx(var_name, var_value, gp.blank() | gp.show_type()) error_message += "\n" error_message += "Because some of the values in the " error_message += "required_values list are not present (see" error_message += " entries marked with \"*\"):\n" error_message += "\n" error_message += gp.sprint_varx('required_values', display_required_values, gp.blank() | gp.show_type()) error_message += "\n" return process_error_message(error_message) if len(invalid_values): found_error = 0 display_var_value = list(var_value) for ix in range(0, len(var_value)): if var_value[ix] in invalid_values: found_error = 1 display_var_value[ix] = str(var_value[ix]) + "*" if found_error: var_name = get_var_name(var_name) error_message += "The following list is invalid (see entries" error_message += " marked with \"*\"):\n" error_message += gp.sprint_varx(var_name, display_var_value, gp.blank() | gp.show_type()) error_message += "\n" error_message += gp.sprint_var(invalid_values, gp.show_type()) return process_error_message(error_message) found_error = 0 display_var_value = list(var_value) for ix in range(0, len(var_value)): if var_value[ix] not in valid_values: found_error = 1 display_var_value[ix] = str(var_value[ix]) + "*" if found_error: var_name = get_var_name(var_name) error_message += "The following list is invalid (see entries marked" error_message += " with \"*\"):\n" error_message += gp.sprint_varx(var_name, display_var_value, gp.blank() | gp.show_type()) error_message += "\n" error_message += gp.sprint_var(valid_values, gp.show_type()) return process_error_message(error_message) return process_error_message(error_message)
def wait_for_comm_cycle(start_boot_seconds, quiet=None): r""" Wait for communications to the BMC to stop working and then resume working. This function is useful when you have initiated some kind of reboot. Description of arguments: start_boot_seconds The time that the boot test started. The format is the epoch time in seconds, i.e. the number of seconds since 1970-01-01 00:00:00 UTC. This value should be obtained from the BMC so that it is not dependent on any kind of synchronization between this machine and the target BMC This will allow this program to work correctly even in a simulated environment. This value should be obtained by the caller prior to initiating a reboot. It can be obtained as follows: state = st.get_state(req_states=['epoch_seconds']) """ quiet = int(gp.get_var_value(quiet, 0)) # Validate parms. error_message = gv.svalid_integer(start_boot_seconds, var_name="start_boot_seconds") if error_message != "": BuiltIn().fail(gp.sprint_error(error_message)) match_state = anchor_state(DotDict([('packet_loss', '100')])) # Wait for 100% packet loss trying to ping machine. wait_state(match_state, wait_time="8 mins", interval="0 seconds") match_state['packet_loss'] = '^0$' # Wait for 0% packet loss trying to ping machine. wait_state(match_state, wait_time="8 mins", interval="0 seconds") # Get the uptime and epoch seconds for comparisons. We want to be sure # that the uptime is less than the elapsed boot time. Further proof that # a reboot has indeed occurred (vs random network instability giving a # false positive. We also use wait_state because the BMC may take a short # while to be ready to process SSH requests. match_state = DotDict([('uptime', '^[0-9\\.]+$'), ('epoch_seconds', '^[0-9]+$')]) state = wait_state(match_state, wait_time="2 mins", interval="1 second") elapsed_boot_time = int(state['epoch_seconds']) - start_boot_seconds gp.qprint_var(elapsed_boot_time) if state['uptime'] == "": error_message = "Unable to obtain uptime from the BMC. BMC is not" +\ " communicating." BuiltIn().fail(gp.sprint_error(error_message)) if int(float(state['uptime'])) < elapsed_boot_time: uptime = state['uptime'] gp.qprint_var(uptime) gp.qprint_timen("The uptime is less than the elapsed boot time," + " as expected.") else: error_message = "The uptime is greater than the elapsed boot time," +\ " which is unexpected:\n" +\ gp.sprint_var(start_boot_seconds) +\ gp.sprint_var(state) BuiltIn().fail(gp.sprint_error(error_message)) gp.qprint_timen("Verifying that REST API interface is working.") match_state = DotDict([('rest', '^1$')]) state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")
def valid_dict(var_value, required_keys=[], valid_values={}, invalid_values={}, var_name=None): r""" The dictionary variable value is valid if it contains all required keys and each entry passes the valid_value() call. Examples: person_record = {'last_name': 'Jones', 'first_name': 'John'} valid_values = {'last_name': ['Doe', 'Jones', 'Johnson'], 'first_name': ['John', 'Mary']} invalid_values = {'last_name': ['Manson', 'Hitler', 'Presley'], 'first_name': ['Mickey', 'Goofy']} valid_dict(person_record, valid_values=valid_values) valid_dict(person_record, invalid_values=invalid_values) Description of argument(s): var_value The value being validated. required_keys A list of keys which must be found in the dictionary for it to be considered valid. valid_values A dictionary whose entries correspond to the entries in var_value. Each value in valid_values is itself a valid_values list for the corresponding value in var_value. For any var_value[key] to be considered valid, its value must be found in valid_values[key]. invalid_values A dictionary whose entries correspond to the entries in var_value. Each value in invalid_values is itself an invalid_values list for the corresponding value in var_value. For any var_value[key] to be considered valid, its value must NOT be found in invalid_values[key]. """ error_message = "" missing_keys = list(set(required_keys) - set(var_value.keys())) if len(missing_keys) > 0: var_name = get_var_name(var_name) error_message += "The following dictionary is invalid because it is" error_message += " missing required keys:\n" error_message += gp.sprint_varx(var_name, var_value, gp.blank() | gp.show_type()) error_message += "\n" error_message += gp.sprint_var(missing_keys, gp.show_type()) return process_error_message(error_message) var_name = get_var_name(var_name) if len(valid_values): keys = valid_values.keys() error_message = valid_dict(var_value, required_keys=keys, var_name=var_name) if error_message: return process_error_message(error_message) for key, value in valid_values.items(): key_name = " [" + key + "]" sub_error_message = valid_value(var_value[key], valid_values=value, var_name=key_name) if sub_error_message: error_message += "The following dictionary is invalid because one of its entries is invalid:\n" error_message += gp.sprint_varx(var_name, var_value, gp.blank() | gp.show_type()) error_message += "\n" error_message += sub_error_message return process_error_message(error_message) for key, value in invalid_values.items(): if key not in var_value: continue key_name = " [" + key + "]" sub_error_message = valid_value(var_value[key], invalid_values=value, var_name=key_name) if sub_error_message: error_message += "The following dictionary is invalid because one of its entries is invalid:\n" error_message += gp.sprint_varx(var_name, var_value, gp.blank() | gp.show_type()) error_message += "\n" error_message += sub_error_message return process_error_message(error_message) return process_error_message(error_message)
def get_state(openbmc_host="", openbmc_username="", openbmc_password="", os_host="", os_username="", os_password="", req_states=default_req_states, quiet=None): r""" Get component states such as chassis state, bmc state, etc, put them into a dictionary and return them to the caller. Note that all substate values are strings. Note: If elapsed_boot_time is included in req_states, it is the caller's duty to call set_start_boot_seconds() in order to set global start_boot_seconds. elapsed_boot_time is the current time minus start_boot_seconds. Description of argument(s): openbmc_host The DNS name or IP address of the BMC. This defaults to global ${OPENBMC_HOST}. openbmc_username The username to be used to login to the BMC. This defaults to global ${OPENBMC_USERNAME}. openbmc_password The password to be used to login to the BMC. This defaults to global ${OPENBMC_PASSWORD}. os_host The DNS name or IP address of the operating system. This defaults to global ${OS_HOST}. os_username The username to be used to login to the OS. This defaults to global ${OS_USERNAME}. os_password The password to be used to login to the OS. This defaults to global ${OS_PASSWORD}. req_states This is a list of states whose values are being requested by the caller. quiet Indicates whether status details (e.g. curl commands) should be written to the console. Defaults to either global value of ${QUIET} or to 1. """ quiet = int(gp.get_var_value(quiet, 0)) # Set parm defaults where necessary and validate all parms. if openbmc_host == "": openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}") error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""]) if error_message != "": BuiltIn().fail(gp.sprint_error(error_message)) if openbmc_username == "": openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}") error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""]) if error_message != "": BuiltIn().fail(gp.sprint_error(error_message)) if openbmc_password == "": openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}") error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""]) if error_message != "": BuiltIn().fail(gp.sprint_error(error_message)) # NOTE: OS parms are optional. if os_host == "": os_host = BuiltIn().get_variable_value("${OS_HOST}") if os_host is None: os_host = "" if os_username is "": os_username = BuiltIn().get_variable_value("${OS_USERNAME}") if os_username is None: os_username = "" if os_password is "": os_password = BuiltIn().get_variable_value("${OS_PASSWORD}") if os_password is None: os_password = "" invalid_req_states = [sub_state for sub_state in req_states if sub_state not in valid_req_states] if len(invalid_req_states) > 0: error_message = "The following req_states are not supported:\n" +\ gp.sprint_var(invalid_req_states) BuiltIn().fail(gp.sprint_error(error_message)) # Initialize all substate values supported by this function. ping = 0 packet_loss = '' uptime = '' epoch_seconds = '' elapsed_boot_time = '' rest = '' chassis = '' requested_chassis = '' bmc = '' requested_bmc = '' boot_progress = '' operating_system = '' host = '' requested_host = '' attempts_left = '' # Get the component states. if 'ping' in req_states: # See if the OS pings. rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + openbmc_host, print_output=0, show_err=0, ignore_err=1) if rc == 0: ping = 1 if 'packet_loss' in req_states: # See if the OS pings. cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\ " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'" rc, out_buf = gc.shell_cmd(cmd_buf, print_output=0, show_err=0, ignore_err=1) if rc == 0: packet_loss = out_buf.rstrip("\n") if 'uptime' in req_states: # Sometimes reading uptime results in a blank value. Call with # wait_until_keyword_succeeds to ensure a non-blank value is obtained. remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\ " && [ ! -z \"${uptime}\" ] && echo ${uptime}" cmd_buf = ["BMC Execute Command", re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1', 'test_mode=0'] gp.qprint_issuing(cmd_buf, 0) gp.qprint_issuing(remote_cmd_buf, 0) try: stdout, stderr, rc =\ BuiltIn().wait_until_keyword_succeeds("10 sec", "0 sec", *cmd_buf) if rc == 0 and stderr == "": uptime = stdout except AssertionError as my_assertion_error: pass if 'epoch_seconds' in req_states or 'elapsed_boot_time' in req_states: date_cmd_buf = "date -u +%s" if USE_BMC_EPOCH_TIME: cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}'] if not quiet: gp.print_issuing(cmd_buf) status, ret_values = \ BuiltIn().run_keyword_and_ignore_error(*cmd_buf) if status == "PASS": stdout, stderr, rc = ret_values if rc == 0 and stderr == "": epoch_seconds = stdout.rstrip("\n") else: shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf, quiet=quiet, print_output=0) if shell_rc == 0: epoch_seconds = out_buf.rstrip("\n") if 'elapsed_boot_time' in req_states: global start_boot_seconds elapsed_boot_time = int(epoch_seconds) - start_boot_seconds master_req_rest = ['rest', 'host', 'requested_host', 'operating_system', 'attempts_left', 'boot_progress', 'chassis', 'requested_chassis' 'bmc' 'requested_bmc'] req_rest = [sub_state for sub_state in req_states if sub_state in master_req_rest] need_rest = (len(req_rest) > 0) state = DotDict() if need_rest: cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate", "quiet=${" + str(quiet) + "}"] gp.dprint_issuing(cmd_buf) status, ret_values = \ BuiltIn().run_keyword_and_ignore_error(*cmd_buf) if status == "PASS": state['rest'] = '1' else: state['rest'] = '0' if int(state['rest']): for url_path in ret_values: for attr_name in ret_values[url_path]: # Create a state key value based on the attr_name. try: ret_values[url_path][attr_name] = \ re.sub(r'.*\.', "", ret_values[url_path][attr_name]) except TypeError: pass # Do some key name manipulations. new_attr_name = re.sub(r'^Current|(State|Transition)$', "", attr_name) new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name) new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1', new_attr_name) new_attr_name = new_attr_name.lower().lstrip("_") new_attr_name = re.sub(r'power', r'chassis', new_attr_name) if new_attr_name in req_states: state[new_attr_name] = ret_values[url_path][attr_name] for sub_state in req_states: if sub_state in state: continue if sub_state.startswith("os_"): # We pass "os_" requests on to get_os_state. continue cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")" exec(cmd_buf) if os_host == "": # The caller has not specified an os_host so as far as we're concerned, # it doesn't exist. return state os_req_states = [sub_state for sub_state in req_states if sub_state.startswith('os_')] if len(os_req_states) > 0: # The caller has specified an os_host and they have requested # information on os substates. # Based on the information gathered on bmc, we'll try to make a # determination of whether the os is even up. We'll pass the result # of that assessment to get_os_state to enhance performance. os_up_match = DotDict() for sub_state in master_os_up_match: if sub_state in req_states: os_up_match[sub_state] = master_os_up_match[sub_state] os_up = compare_states(state, os_up_match) os_state = get_os_state(os_host=os_host, os_username=os_username, os_password=os_password, req_states=os_req_states, os_up=os_up, quiet=quiet) # Append os_state dictionary to ours. state.update(os_state) return state
def svalid_value(var_value, invalid_values=[], valid_values=[], var_name=""): r""" Return an empty string if var_value is a valid value. Otherwise, return an error string. Description of arguments: var_value The value being validated. invalid_values A list of invalid values. If var_value is equal to any of these, it is invalid. Note that if you specify anything for invalid_values (below), the valid_values list is not even processed. valid_values A list of invalid values. var_value must be equal to one of these values to be considered valid. var_name The name of the variable whose value is passed in var_value. This parameter is normally unnecessary as this function can figure out the var_name. This is provided for Robot callers. In this scenario, we are unable to get the variable name ourselves. """ success_message = "" error_message = "" stack_frame_ix = 3 len_valid_values = len(valid_values) len_invalid_values = len(invalid_values) if len_valid_values > 0 and len_invalid_values > 0: error_message += "Programmer error - You must provide either an" +\ " invalid_values list or a valid_values" +\ " list but NOT both.\n" +\ gp.sprint_var(invalid_values) +\ gp.sprint_var(valid_values) return error_message show_blanks = 1 if len_valid_values > 0: # Processing the valid_values list. if var_value in valid_values: return success_message if var_name == "": var_name = gp.get_arg_name(0, 1, stack_frame_ix) error_message += "The following variable has an invalid" +\ " value:\n" +\ gp.sprint_varx(var_name, var_value, show_blanks) +\ "\nIt must be one of the following values:\n" +\ gp.sprint_varx("valid_values", valid_values, show_blanks) return error_message if len_invalid_values == 0: # Assign default value. invalid_values = [""] # Assertion: We have an invalid_values list. Processing it now. if var_value not in invalid_values: return success_message if var_name == "": var_name = gp.get_arg_name(0, 1, stack_frame_ix) error_message += "The following variable has an invalid value:\n" +\ gp.sprint_varx(var_name, var_value, show_blanks) +\ "\nIt must NOT be one of the following values:\n" +\ gp.sprint_varx("invalid_values", invalid_values, show_blanks) return error_message