Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
def filter_struct(structure, filter_dict, regex=False, invert=False):
    r"""
    Filter the structure by removing any entries that do NOT contain the keys/values specified in filter_dict
    and return the result.

    The selection process is directed only at the first-level entries of the structure.

    Example:

    Given a dictionary named "properties" that has the following structure:

    properties:
      [/redfish/v1/Systems/system/Processors]:
        [Members]:
          [0]:
            [@odata.id]:                              /redfish/v1/Systems/system/Processors/cpu0
          [1]:
            [@odata.id]:                              /redfish/v1/Systems/system/Processors/cpu1
      [/redfish/v1/Systems/system/Processors/cpu0]:
        [Status]:
          [State]:                                    Enabled
          [Health]:                                   OK
      [/redfish/v1/Systems/system/Processors/cpu1]:
        [Status]:
          [State]:                                    Enabled
          [Health]:                                   Bad

    The following call:

    properties = filter_struct(properties, "[('Health', 'OK')]")

    Would return a new properties dictionary that looks like this:

    properties:
      [/redfish/v1/Systems/system/Processors/cpu0]:
        [Status]:
          [State]:                                    Enabled
          [Health]:                                   OK

    Note that the first item in the original properties directory had no key anywhere in the structure named
    "Health".  Therefore, that item failed to make the cut.  The next item did have a key named "Health"
    whose value was "OK" so it was included in the new structure.  The third item had a key named "Health"
    but its value was not "OK" so it also failed to make the cut.

    Description of argument(s):
    structure                       Any nested combination of lists or dictionaries.  See the prolog of
                                    get_nested() for details.
    filter_dict                     For each key/value pair in filter_dict, each entry in structure must
                                    contain the same key/value pair at some level.  A filter_dict value of
                                    None is treated as a special case.  Taking the example shown above,
                                    [('State', None)] would mean that the result should only contain records
                                    that have no State key at all.
    regex                           Indicates whether the values in the filter_dict should be interpreted as
                                    regular expressions.
    invert                          Invert the results.  Instead of including only matching entries in the
                                    results, include only NON-matching entries in the results.
    """

    # Convert filter_dict from a string containing a python object definition to an actual python object (if
    # warranted).
    filter_dict = fa.source_to_object(filter_dict)

    # Determine whether structure is a list or a dictionary and process accordingly.  The result returned
    # will be of the same type as the structure.
    if type(structure) is list:
        result = []
        for element in structure:
            if match_struct(element, filter_dict, regex) != invert:
                result.append(element)
    else:
        try:
            result = collections.OrderedDict()
        except AttributeError:
            result = DotDict()
        for struct_key, struct_value in structure.items():
            if match_struct(struct_value, filter_dict, regex) != invert:
                result[struct_key] = struct_value

    return result
Ejemplo n.º 3
0
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,
              valid_rcs=[0],
              ignore_err=None,
              return_stderr=0,
              fork=0,
              error_regexes=None):
    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 valid_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.
    valid_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) and return the Popen
                                    object created by the subprocess.popen() function.  See the kill_cmd
                                    function for details on how to process the popen object.
    error_regexes                   A list of regular expressions to be used to identify errors in the
                                    command output.  If there is a match for any of these regular
                                    expressions, the command will be considered a failure and the shell_rc
                                    will be set to -1.  For example, if error_regexes = ['ERROR:'] and the
                                    command output contains 'ERROR:  Unrecognized option', it will be counted
                                    as an error even if the command returned 0.  This is useful when running
                                    commands that do not always return non-zero on error.
    """

    err_msg = gv.valid_value(command_string)
    if err_msg:
        raise ValueError(err_msg)

    # 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)
    ignore_err = int(gm.dft(ignore_err, gp.get_stack_var('ignore_err', 1)))

    gp.qprint_issuing(command_string, test_mode)
    if test_mode:
        return (0, "", "") if return_stderr else (0, "")

    # Convert a string python dictionary definition to a dictionary.
    valid_rcs = fa.source_to_object(valid_rcs)
    # Convert each list entry to a signed value.
    valid_rcs = [gm.to_signed(x) for x in valid_rcs]

    stderr = subprocess.PIPE if return_stderr else subprocess.STDOUT

    # Write all output to func_out_history_buf 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_out_history_buf will
    # contain the complete history from the current invocation of this function.
    global command_timed_out
    command_timed_out = False
    func_out_history_buf = ""
    for attempt_num in range(1, max_attempts + 1):
        sub_proc = subprocess.Popen(command_string,
                                    preexec_fn=os.setsid,
                                    bufsize=1,
                                    shell=True,
                                    universal_newlines=True,
                                    executable='/bin/bash',
                                    stdout=subprocess.PIPE,
                                    stderr=stderr)
        if fork:
            return sub_proc

        if time_out:
            command_timed_out = False
            # Designate a SIGALRM handling function and set alarm.
            signal.signal(signal.SIGALRM, shell_cmd_timed_out)
            signal.alarm(time_out)
        try:
            stdout_buf, stderr_buf = sub_proc.communicate()
        except IOError:
            command_timed_out = True
        # Restore the original SIGALRM handler and clear the alarm.
        signal.signal(signal.SIGALRM, original_sigalrm_handler)
        signal.alarm(0)

        # Output from this loop iteration is written to func_out_buf for later processing.  This can include
        # stdout, stderr and our own error messages.
        func_out_buf = ""
        if print_output:
            if return_stderr:
                func_out_buf += stderr_buf
            func_out_buf += stdout_buf
        shell_rc = sub_proc.returncode
        if shell_rc in valid_rcs:
            # Check output for text indicating there is an error.
            if error_regexes and re.match('|'.join(error_regexes), stdout_buf):
                shell_rc = -1
            else:
                break
        err_msg = "The prior shell command failed.\n"
        err_msg += gp.sprint_var(attempt_num)
        err_msg += gp.sprint_vars(command_string, command_timed_out, time_out)
        err_msg += gp.sprint_varx("child_pid", sub_proc.pid)
        err_msg += gp.sprint_vars(shell_rc, valid_rcs, fmt=gp.hexa())
        if error_regexes:
            err_msg += gp.sprint_vars(error_regexes)
        if not print_output:
            if return_stderr:
                err_msg += "stderr_buf:\n" + stderr_buf
            err_msg += "stdout_buf:\n" + stdout_buf
        if show_err:
            func_out_buf += gp.sprint_error_report(err_msg)
        if attempt_num < max_attempts:
            cmd_buf = "time.sleep(" + str(retry_sleep_time) + ")"
            if show_err:
                func_out_buf += gp.sprint_issuing(cmd_buf)
            exec(cmd_buf)
        func_out_history_buf += func_out_buf

    if shell_rc in valid_rcs:
        gp.gp_print(func_out_buf)
    else:
        if show_err:
            gp.gp_print(func_out_history_buf, stream='stderr')
        else:
            # There is no error information to show so just print output from last loop iteration.
            gp.gp_print(func_out_buf)
        if not ignore_err:
            # If the caller has already asked to show error info, avoid repeating that in the failure message.
            err_msg = "The prior shell command failed.\n" if show_err \
                else err_msg
            if robot_env:
                BuiltIn().fail(err_msg)
            else:
                raise ValueError(err_msg)

    return (shell_rc, stdout_buf, stderr_buf) if return_stderr \
        else (shell_rc, stdout_buf)