def rvalid_range(var_name, range): r""" Validate that a robot integer is within the given range. This function is the robot wrapper for gen_robot_print.svalid_range. Description of arguments: var_name The name of the variable whose value is to be validated. 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]. The caller may also specify this value as a string which will then be converted to a list in the aforementioned format: lower..upper, lower.. or ..upper. Examples of robot calls and corresponding output: Robot code... Rvalid Range MY_PARM 5..9 Output... #(CDT) 2018/05/09 11:45:00.166344 - 0.004252 - **ERROR** The following # variable is not within the expected range: MY_PARM: 4 valid_range: 5..9 """ var_value = BuiltIn().get_variable_value("${" + var_name + "}") if var_value is None: var_value = "" error_message = "Variable \"" + var_name +\ "\" not found (i.e. it's undefined).\n" else: try: range = range.split("..") except AttributeError: pass if range[0] == "": range[0] = None range = [x for x in range if x] error_message = gv.svalid_range(var_value, range, var_name) if not error_message == "": error_message = grp.sprint_error_report(error_message) BuiltIn().fail(error_message)
def rvalid_range(var_name, range): r""" Validate that a robot integer is within the given range. This function is the robot wrapper for gen_robot_print.svalid_range. Description of arguments: var_name The name of the variable whose value is to be validated. 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]. The caller may also specify this value as a string which will then be converted to a list in the aforementioned format: lower..upper, lower.. or ..upper. Examples of robot calls and corresponding output: Robot code... Rvalid Range MY_PARM 5..9 Output... #(CDT) 2018/05/09 11:45:00.166344 - 0.004252 - **ERROR** The following # variable is not within the expected range: MY_PARM: 4 valid_range: 5..9 """ var_value = BuiltIn().get_variable_value("${" + var_name + "}") if var_value is None: var_value = "" error_message = "Variable \"" + var_name +\ "\" not found (i.e. it's undefined).\n" else: if isinstance(range, unicode): range = range.split("..") if range[0] == "": range[0] = None range = [x for x in range if x] error_message = gv.svalid_range(var_value, range, var_name) if not error_message == "": error_message = grp.sprint_error_report(error_message) BuiltIn().fail(error_message)
def rvalid_integer(var_name): r""" Validate a robot integer. This function is the robot wrapper for gen_robot_print.svalid_integer. Description of arguments: var_name The name of the variable whose value is to be validated. Examples of robot calls and corresponding output: Robot code... Rvalid Integer MY_PARM Output... #(CDT) 2016/11/02 10:44:43 - **ERROR** Variable "MY_PARM" not found (i.e. #it's undefined). or if it is defined but blank: Output... #(CDT) 2016/11/02 10:45:37 - **ERROR** Invalid integer value: MY_PARM: <blank> Robot code... ${MY_PARM}= Set Variable HELLO Rvalid Integer MY_PARM Output... #(CDT) 2016/11/02 10:46:18 - **ERROR** Invalid integer value: MY_PARM: HELLO """ # Note: get_variable_value() seems to have no trouble with local variables. var_value = BuiltIn().get_variable_value("${" + var_name + "}") if var_value is None: var_value = "" error_message = "Variable \"" + var_name +\ "\" not found (i.e. it's undefined).\n" else: error_message = gv.svalid_integer(var_value, var_name) if not error_message == "": error_message = grp.sprint_error_report(error_message) BuiltIn().fail(error_message)
def set_ffdc_defaults(ffdc_dir_path=None, ffdc_prefix=None): r""" Set a default value for ffdc_dir_path and ffdc_prefix if they don't already have values. Return both values. Description of arguments: ffdc_dir_path The dir path where FFDC data should be put. ffdc_prefix The prefix to be given to each FFDC file name generated. NOTE: If global variable ffdc_dir_path_style is set to ${1}, this function will create default values in a newer way. Otherwise, its behavior will remain unchanged. """ # Note: Several subordinate functions like 'Get Test Dir and Name' and # 'Header Message' expect global variable FFDC_TIME to be set. cmd_buf = ["Get Current Time Stamp"] grp.rdpissuing_keyword(cmd_buf) FFDC_TIME = BuiltIn().run_keyword(*cmd_buf) BuiltIn().set_global_variable("${FFDC_TIME}", FFDC_TIME) ffdc_dir_path_style = BuiltIn().get_variable_value( "${ffdc_dir_path_style}") if ffdc_dir_path is None: if ffdc_dir_path_style: try: ffdc_dir_path = os.environ['FFDC_DIR_PATH'] except KeyError: ffdc_dir_path = os.path.dirname( BuiltIn().get_variable_value("${LOG_FILE}")) + "/" else: FFDC_LOG_PATH = os.getcwd() + "/logs/" if FFDC_LOG_PATH is None: FFDC_LOG_PATH = "" if FFDC_LOG_PATH == "": FFDC_LOG_PATH = os.path.dirname( BuiltIn().get_variable_value("${LOG_FILE}")) + "/" error_message = gv.svalid_value(FFDC_LOG_PATH, var_name="FFDC_LOG_PATH") if error_message != "": error_message = grp.sprint_error_report(error_message) BuiltIn().fail(error_message) FFDC_LOG_PATH = os.path.normpath(FFDC_LOG_PATH) + os.sep cmd_buf = ["Get Test Dir and Name"] grp.rpissuing_keyword(cmd_buf) suitename, testname = BuiltIn().run_keyword(*cmd_buf) ffdc_dir_path = FFDC_LOG_PATH + suitename + "/" + testname + "/" # Add trailing slash. ffdc_dir_path = os.path.normpath(ffdc_dir_path) + os.sep if ffdc_prefix is None: FFDC_TIME = BuiltIn().get_variable_value("${FFDC_TIME}") if ffdc_prefix is None: if ffdc_dir_path_style: OPENBMC_HOST = BuiltIn().get_variable_value("${OPENBMC_HOST}") OPENBMC_NICKNAME = BuiltIn().get_variable_value( "${OPENBMC_NICKNAME}", default=OPENBMC_HOST) ffdc_prefix = OPENBMC_NICKNAME + "." + FFDC_TIME[2:8] + "." +\ FFDC_TIME[8:14] + "." else: ffdc_prefix = FFDC_TIME + "_" BuiltIn().set_global_variable("${FFDC_DIR_PATH}", ffdc_dir_path) BuiltIn().set_global_variable("${FFDC_PREFIX}", ffdc_prefix) return ffdc_dir_path, ffdc_prefix
def ffdc(ffdc_dir_path=None, ffdc_prefix=None, ffdc_function_list=""): r""" Gather First Failure Data Capture (FFDC). This includes: - Set global FFDC_TIME. - Create FFDC work space directory. - Write test info details. - Call BMC methods to write/collect FFDC data. Description of arguments: ffdc_dir_path The dir path where FFDC data should be put. ffdc_prefix The prefix to be given to each FFDC file name generated. ffdc_function_list A colon-delimited list of all the types of FFDC data you wish to have collected. A blank value means that all possible kinds of FFDC are to be collected. See FFDC_METHOD_CALL object in lib/openbmc_ffdc_list.py for possible choices. """ ffdc_file_list = [] # Check if Ping and SSH connection is alive OPENBMC_HOST = BuiltIn().get_variable_value("${OPENBMC_HOST}") state = st.get_state(req_states=['ping', 'uptime']) gp.qprint_var(state) if not int(state['ping']): gp.print_error("BMC is not ping-able. Terminating FFDC collection.\n") return ffdc_file_list if state['uptime'] == "": gp.print_error("BMC is not communicating. Terminating FFDC" + " collection.\n") return ffdc_file_list gp.qprint_timen("Collecting FFDC.") # Get default values for arguments. ffdc_dir_path, ffdc_prefix = set_ffdc_defaults(ffdc_dir_path, ffdc_prefix) gp.qprint_var(ffdc_dir_path) gp.qprint_var(ffdc_prefix) # LOG_PREFIX is used by subordinate functions. LOG_PREFIX = ffdc_dir_path + ffdc_prefix BuiltIn().set_global_variable("${LOG_PREFIX}", LOG_PREFIX) cmd_buf = ["Create Directory", ffdc_dir_path] grp.rqpissuing_keyword(cmd_buf) status, output = BuiltIn().run_keyword_and_ignore_error(*cmd_buf) if status != "PASS": error_message = grp.sprint_error_report("Create Directory failed" + " with the following" + " error:\n" + output) BuiltIn().fail(error_message) # FFDC_FILE_PATH is used by Header Message. FFDC_FILE_PATH = ffdc_dir_path + ffdc_prefix + "BMC_general.txt" BuiltIn().set_global_variable("${FFDC_FILE_PATH}", FFDC_FILE_PATH) status, ffdc_file_list = grk.run_key("Header Message") status, ffdc_file_sub_list = \ grk.run_key_u("Call FFDC Methods ffdc_function_list=" + ffdc_function_list) # Combine lists, remove duplicates and sort. ffdc_file_list = sorted(set(ffdc_file_list + ffdc_file_sub_list)) gp.qprint_timen("Finished collecting FFDC.") return ffdc_file_list
def rvalid_value(var_name, invalid_values=[], valid_values=[]): r""" Validate a robot value. This function is the robot wrapper for gen_robot_print.svalid_value. Description of arguments: var_name The name of the variable whose value is to be 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. Examples of robot calls and corresponding output: Robot code... rvalid_value MY_PARM Output... #(CDT) 2016/11/02 10:04:20 - **ERROR** Variable "MY_PARM" not found (i.e. #it's undefined). or if it is defined but blank: Output... #(CDT) 2016/11/02 10:14:24 - **ERROR** The following variable has an #invalid value: MY_PARM: It must NOT be one of the following values: invalid_values: invalid_values[0]: <blank> Robot code... ${invalid_values}= Create List one two three ${MY_PARM}= Set Variable one rvalid_value MY_PARM invalid_values=${invalid_values} Output... #(CDT) 2016/11/02 10:20:05 - **ERROR** The following variable has an #invalid value: MY_PARM: one It must NOT be one of the following values: invalid_values: invalid_values[0]: one invalid_values[1]: two invalid_values[2]: three """ # Note: get_variable_value() seems to have no trouble with local variables. var_value = BuiltIn().get_variable_value("${" + var_name + "}") if var_value is None: var_value = "" error_message = "Variable \"" + var_name +\ "\" not found (i.e. it's undefined).\n" else: error_message = gv.svalid_value(var_value, invalid_values, valid_values, var_name) if not error_message == "": error_message = grp.sprint_error_report(error_message) BuiltIn().fail(error_message)
def shell_cmd(command_string, quiet=None, print_output=1, 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))) ignore_err = int(gm.dft(ignore_err, gp.get_stack_var('ignore_err', 1))) err_msg = gv.svalid_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 = [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: err_buf += line if not print_output: continue func_stdout += line for line in sub_proc.stdout: 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 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, 1) err_msg += gp.sprint_var(allowed_shell_rcs, 1) if not print_output: if return_stderr: err_msg += "err_buf:\n" + err_buf err_msg += "out_buf:\n" + out_buf if show_err: if robot_env: func_stdout += grp.sprint_error_report(err_msg) else: 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 if robot_env: grp.rprint(func_stdout) else: sys.stdout.write(func_stdout) sys.stdout.flush() 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 set_ffdc_defaults(ffdc_dir_path=None, ffdc_prefix=None): r""" Set a default value for ffdc_dir_path and ffdc_prefix if they don't already have values. Return both values. Description of arguments: ffdc_dir_path The dir path where FFDC data should be put. ffdc_prefix The prefix to be given to each FFDC file name generated. NOTE: If global variable ffdc_dir_path_style is set to ${1}, this function will create default values in a newer way. Otherwise, its behavior will remain unchanged. """ # Note: Several subordinate functions like 'Get Test Dir and Name' and # 'Header Message' expect global variable FFDC_TIME to be set. cmd_buf = ["Get Current Time Stamp"] grp.rdpissuing_keyword(cmd_buf) FFDC_TIME = BuiltIn().run_keyword(*cmd_buf) BuiltIn().set_global_variable("${FFDC_TIME}", FFDC_TIME) ffdc_dir_path_style = BuiltIn().get_variable_value( "${ffdc_dir_path_style}") if ffdc_dir_path is None: if ffdc_dir_path_style: try: ffdc_dir_path = os.environ['FFDC_DIR_PATH'] except KeyError: ffdc_dir_path = os.path.dirname( BuiltIn().get_variable_value("${LOG_FILE}")) + "/" else: FFDC_LOG_PATH = os.getcwd() + "/logs/" if FFDC_LOG_PATH is None: FFDC_LOG_PATH = "" if FFDC_LOG_PATH == "": FFDC_LOG_PATH = os.path.dirname( BuiltIn().get_variable_value("${LOG_FILE}")) + "/" error_message = gv.svalid_value(FFDC_LOG_PATH, var_name="FFDC_LOG_PATH") if error_message != "": error_message = grp.sprint_error_report(error_message) BuiltIn().fail(error_message) FFDC_LOG_PATH = os.path.normpath(FFDC_LOG_PATH) + os.sep cmd_buf = ["Get Test Dir and Name"] grp.rpissuing_keyword(cmd_buf) suitename, testname = BuiltIn().run_keyword(*cmd_buf) ffdc_dir_path = FFDC_LOG_PATH + suitename + "/" + testname + "/" # Add trailing slash. ffdc_dir_path = os.path.normpath(ffdc_dir_path) + os.sep if ffdc_prefix is None: FFDC_TIME = BuiltIn().get_variable_value("${FFDC_TIME}") if ffdc_prefix is None: if ffdc_dir_path_style: OPENBMC_HOST = BuiltIn().get_variable_value("${OPENBMC_HOST}") OPENBMC_NICKNAME = BuiltIn().get_variable_value( "${OPENBMC_NICKNAME}", default=OPENBMC_HOST) ffdc_prefix = OPENBMC_NICKNAME + "." + FFDC_TIME[2:8] + "." +\ FFDC_TIME[8:14] + "." else: ffdc_prefix = FFDC_TIME + "_" BuiltIn().set_global_variable("${FFDC_DIR_PATH}", ffdc_dir_path) BuiltIn().set_global_variable("${FFDC_PREFIX}", ffdc_prefix) return ffdc_dir_path, ffdc_prefix
def ffdc(ffdc_dir_path=None, ffdc_prefix=None, ffdc_function_list=""): r""" Gather First Failure Data Capture (FFDC). This includes: - Set global FFDC_TIME. - Create FFDC work space directory. - Write test info details. - Call BMC methods to write/collect FFDC data. Description of arguments: ffdc_dir_path The dir path where FFDC data should be put. ffdc_prefix The prefix to be given to each FFDC file name generated. ffdc_function_list A colon-delimited list of all the types of FFDC data you wish to have collected. A blank value means that all possible kinds of FFDC are to be collected. See FFDC_METHOD_CALL object in lib/openbmc_ffdc_list.py for possible choices. """ ffdc_file_list = [] # Check if Ping and SSH connection is alive OPENBMC_HOST = BuiltIn().get_variable_value("${OPENBMC_HOST}") state = st.get_state(req_states=['ping', 'uptime']) gp.qprint_var(state) if not int(state['ping']): gp.print_error("BMC is not ping-able. Terminating FFDC collection.\n") return ffdc_file_list if state['uptime'] == "": gp.print_error("BMC is not communicating. Terminating FFDC" + " collection.\n") return ffdc_file_list gp.qprint_timen("Collecting FFDC.") # Get default values for arguments. ffdc_dir_path, ffdc_prefix = set_ffdc_defaults(ffdc_dir_path, ffdc_prefix) gp.qprint_var(ffdc_dir_path) gp.qprint_var(ffdc_prefix) # LOG_PREFIX is used by subordinate functions. LOG_PREFIX = ffdc_dir_path + ffdc_prefix BuiltIn().set_global_variable("${LOG_PREFIX}", LOG_PREFIX) cmd_buf = ["Create Directory", ffdc_dir_path] grp.rqpissuing_keyword(cmd_buf) status, output = BuiltIn().run_keyword_and_ignore_error(*cmd_buf) if status != "PASS": error_message = grp.sprint_error_report("Create Directory failed" + " with the following" + " error:\n" + output) BuiltIn().fail(error_message) # FFDC_FILE_PATH is used by Header Message. FFDC_FILE_PATH = ffdc_dir_path + ffdc_prefix + "BMC_general.txt" BuiltIn().set_global_variable("${FFDC_FILE_PATH}", FFDC_FILE_PATH) status, ffdc_file_list = grk.run_key("Header Message") status, ffdc_file_sub_list = \ grk.run_key_u("Call FFDC Methods ffdc_function_list=" + ffdc_function_list) # Combine lists, remove duplicates and sort. ffdc_file_list = sorted(set(ffdc_file_list + ffdc_file_sub_list)) gp.qprint_timen("Finished collecting FFDC.") return ffdc_file_list
def rvalid_value(var_name, invalid_values=[], valid_values=[]): r""" Validate a robot value. This function is the robot wrapper for gen_robot_print.svalid_value. Description of arguments: var_name The name of the variable whose value is to be 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. If either the invalid_values or the valid_values parms are not of type "list", they will be processed as python code in order to generate a list. This allows the robot programmer to essentially specify a list literal. For example, the robot code could contain the following: Rvalid Value var1 valid_values=['one', 'two'] Examples of robot calls and corresponding output: Robot code... rvalid_value MY_PARM Output... #(CDT) 2016/11/02 10:04:20 - **ERROR** Variable "MY_PARM" not found (i.e. #it's undefined). or if it is defined but blank: Output... #(CDT) 2016/11/02 10:14:24 - **ERROR** The following variable has an #invalid value: MY_PARM: It must NOT be one of the following values: invalid_values: invalid_values[0]: <blank> Robot code... ${invalid_values}= Create List one two three ${MY_PARM}= Set Variable one rvalid_value MY_PARM invalid_values=${invalid_values} Output... #(CDT) 2016/11/02 10:20:05 - **ERROR** The following variable has an #invalid value: MY_PARM: one It must NOT be one of the following values: invalid_values: invalid_values[0]: one invalid_values[1]: two invalid_values[2]: three """ # Note: get_variable_value() seems to have no trouble with local variables. var_value = BuiltIn().get_variable_value("${" + var_name + "}") if type(valid_values) is not list: # Evaluate python syntax to convert to a list. exec("valid_values = " + valid_values) if type(invalid_values) is not list: # Evaluate python syntax to convert to a list. exec("invalid_values = " + invalid_values) if var_value is None: var_value = "" error_message = "Variable \"" + var_name +\ "\" not found (i.e. it's undefined).\n" else: error_message = gv.svalid_value(var_value, invalid_values, valid_values, var_name) if not error_message == "": error_message = grp.sprint_error_report(error_message) BuiltIn().fail(error_message)