Пример #1
0
def list_tab(user):
    '''
    Return the contents of the specified user's incrontab

    CLI Example:

    .. code-block:: bash

        salt '*' incron.list_tab root
    '''
    if user == 'system':
        data = raw_system_incron()
    else:
        data = raw_incron(user)
        log.debug("user data {0}".format(data))
    ret = {'crons': [], 'pre': []}
    flag = False
    for line in data.splitlines():
        if len(line.split()) > 3:
            # Appears to be a standard incron line
            comps = line.split()
            path = comps[0]
            mask = comps[1]
            cmd = ' '.join(comps[2:])

            dat = {'path': path, 'mask': mask, 'cmd': cmd}
            ret['crons'].append(dat)
        else:
            ret['pre'].append(line)
    return ret
Пример #2
0
def list_tab(user):
    """
    Return the contents of the specified user's incrontab

    CLI Example:

    .. code-block:: bash

        salt '*' incron.list_tab root
    """
    if user == "system":
        data = raw_system_incron()
    else:
        data = raw_incron(user)
        log.debug("user data %s", data)
    ret = {"crons": [], "pre": []}
    flag = False
    for line in data.splitlines():
        if len(line.split()) > 3:
            # Appears to be a standard incron line
            comps = line.split()
            path = comps[0]
            mask = comps[1]
            cmd = " ".join(comps[2:])

            dat = {"path": path, "mask": mask, "cmd": cmd}
            ret["crons"].append(dat)
        else:
            ret["pre"].append(line)
    return ret
Пример #3
0
def list_tab(user):
    '''
    Return the contents of the specified user's incrontab

    CLI Example:

    .. code-block:: bash

        salt '*' incron.list_tab root
    '''
    if user == 'system':
        data = raw_system_incron()
    else:
        data = raw_incron(user)
        log.debug('incron user data %s', data)
    ret = {'crons': [], 'pre': []}
    flag = False
    comment = None
    tag = '# Line managed by Salt, do not edit'
    for line in data.splitlines():
        if line.endswith(tag):
            if len(line.split()) > 3:
                # Appears to be a standard incron line
                comps = line.split()
                path = comps[0]
                mask = comps[1]
                (cmd, comment) = ' '.join(comps[2:]).split(' # ')

                dat = {
                    'path': path,
                    'mask': mask,
                    'cmd': cmd,
                    'comment': comment
                }
                ret['crons'].append(dat)
                comment = None
        else:
            ret['pre'].append(line)
    return ret
Пример #4
0
def _format_host(host, data, indent_level=1):
    """
    Main highstate formatter. can be called recursively if a nested highstate
    contains other highstates (ie in an orchestration)
    """
    host = salt.utils.data.decode(host)

    colors = salt.utils.color.get_colors(__opts__.get("color"),
                                         __opts__.get("color_theme"))
    tabular = __opts__.get("state_tabular", False)
    rcounts = {}
    rdurations = []
    hcolor = colors["GREEN"]
    hstrs = []
    nchanges = 0
    strip_colors = __opts__.get("strip_colors", True)

    if isinstance(data, int):
        nchanges = 1
        hstrs.append("{0}    {1}{2[ENDC]}".format(hcolor, data, colors))
        hcolor = colors["CYAN"]  # Print the minion name in cyan
    elif isinstance(data, str):
        # Data in this format is from saltmod.function,
        # so it is always a 'change'
        nchanges = 1
        for data in data.splitlines():
            hstrs.append("{0}    {1}{2[ENDC]}".format(hcolor, data, colors))
        hcolor = colors["CYAN"]  # Print the minion name in cyan
    elif isinstance(data, list):
        # Errors have been detected, list them in RED!
        hcolor = colors["LIGHT_RED"]
        hstrs.append("    {0}Data failed to compile:{1[ENDC]}".format(
            hcolor, colors))
        for err in data:
            if strip_colors:
                err = salt.output.strip_esc_sequence(
                    salt.utils.data.decode(err))
            hstrs.append("{0}----------\n    {1}{2[ENDC]}".format(
                hcolor, err, colors))
    elif isinstance(data, dict):
        # Verify that the needed data is present
        data_tmp = {}
        for tname, info in data.items():
            if (isinstance(info, dict) and tname != "changes" and info
                    and "__run_num__" not in info):
                err = ("The State execution failed to record the order "
                       "in which all states were executed. The state "
                       "return missing data is:")
                hstrs.insert(0, pprint.pformat(info))
                hstrs.insert(0, err)
            if isinstance(info, dict) and "result" in info:
                data_tmp[tname] = info
        data = data_tmp
        # Everything rendered as it should display the output
        for tname in sorted(data, key=lambda k: data[k].get("__run_num__", 0)):
            ret = data[tname]
            # Increment result counts
            rcounts.setdefault(ret["result"], 0)

            # unpack state compression counts
            compressed_count = 1
            if (__opts__.get("state_compress_ids", False)
                    and "_|-state_compressed_" in tname):
                _, _id, _, _ = tname.split("_|-")
                count_match = re.search(r"\((\d+)\)$", _id)
                if count_match:
                    compressed_count = int(count_match.group(1))

            rcounts[ret["result"]] += compressed_count
            rduration = ret.get("duration", 0)
            try:
                rdurations.append(float(rduration))
            except ValueError:
                rduration, _, _ = rduration.partition(" ms")
                try:
                    rdurations.append(float(rduration))
                except ValueError:
                    log.error("Cannot parse a float from duration %s",
                              ret.get("duration", 0))

            tcolor = colors["GREEN"]
            if ret.get("name") in [
                    "state.orch", "state.orchestrate", "state.sls"
            ]:
                nested = output(ret["changes"], indent_level=indent_level + 1)
                ctext = re.sub("^",
                               " " * 14 * indent_level,
                               "\n" + nested,
                               flags=re.MULTILINE)
                schanged = True
                nchanges += 1
            else:
                schanged, ctext = _format_changes(ret["changes"])
                # if compressed, the changes are keyed by name
                if schanged and compressed_count > 1:
                    nchanges += len(ret["changes"].get("compressed changes",
                                                       {})) or 1
                else:
                    nchanges += 1 if schanged else 0

            # Skip this state if it was successful & diff output was requested
            if (__opts__.get("state_output_diff", False) and ret["result"]
                    and not schanged):
                continue

            # Skip this state if state_verbose is False, the result is True and
            # there were no changes made
            if (not __opts__.get("state_verbose", False) and ret["result"]
                    and not schanged):
                continue

            if schanged:
                tcolor = colors["CYAN"]
            if ret["result"] is False:
                hcolor = colors["RED"]
                tcolor = colors["RED"]
            if ret["result"] is None:
                hcolor = colors["LIGHT_YELLOW"]
                tcolor = colors["LIGHT_YELLOW"]

            state_output = __opts__.get("state_output", "full").lower()
            comps = tname.split("_|-")

            if state_output.endswith("_id"):
                # Swap in the ID for the name. Refs #35137
                comps[2] = comps[1]

            if state_output.startswith("filter"):
                # By default, full data is shown for all types. However, return
                # data may be excluded by setting state_output_exclude to a
                # comma-separated list of True, False or None, or including the
                # same list with the exclude option on the command line. For
                # now, this option must include a comma. For example:
                #     exclude=True,
                # The same functionality is also available for making return
                # data terse, instead of excluding it.
                cliargs = __opts__.get("arg", [])
                clikwargs = {}
                for item in cliargs:
                    if isinstance(item, dict) and "__kwarg__" in item:
                        clikwargs = item.copy()

                exclude = clikwargs.get(
                    "exclude", __opts__.get("state_output_exclude", []))
                if isinstance(exclude, str):
                    exclude = str(exclude).split(",")

                terse = clikwargs.get("terse",
                                      __opts__.get("state_output_terse", []))
                if isinstance(terse, str):
                    terse = str(terse).split(",")

                if str(ret["result"]) in terse:
                    msg = _format_terse(tcolor, comps, ret, colors, tabular)
                    hstrs.append(msg)
                    continue
                if str(ret["result"]) in exclude:
                    continue

            elif any((
                    state_output.startswith("terse"),
                    state_output.startswith("mixed")
                    and ret["result"] is not False,  # only non-error'd
                    state_output.startswith("changes") and ret["result"]
                    and not schanged,  # non-error'd non-changed
            )):
                # Print this chunk in a terse way and continue in the loop
                msg = _format_terse(tcolor, comps, ret, colors, tabular)
                hstrs.append(msg)
                continue

            state_lines = [
                "{tcolor}----------{colors[ENDC]}",
                "    {tcolor}      ID: {comps[1]}{colors[ENDC]}",
                "    {tcolor}Function: {comps[0]}.{comps[3]}{colors[ENDC]}",
                "    {tcolor}  Result: {ret[result]!s}{colors[ENDC]}",
                "    {tcolor} Comment: {comment}{colors[ENDC]}",
            ]
            if __opts__.get("state_output_profile") and "start_time" in ret:
                state_lines.extend([
                    "    {tcolor} Started: {ret[start_time]!s}{colors[ENDC]}",
                    "    {tcolor}Duration: {ret[duration]!s}{colors[ENDC]}",
                ])
            # This isn't the prettiest way of doing this, but it's readable.
            if comps[1] != comps[2]:
                state_lines.insert(
                    3, "    {tcolor}    Name: {comps[2]}{colors[ENDC]}")
            # be sure that ret['comment'] is utf-8 friendly
            try:
                if not isinstance(ret["comment"], str):
                    ret["comment"] = str(ret["comment"])
            except UnicodeDecodeError:
                # If we got here, we're on Python 2 and ret['comment'] somehow
                # contained a str type with unicode content.
                ret["comment"] = salt.utils.stringutils.to_unicode(
                    ret["comment"])
            try:
                comment = salt.utils.data.decode(ret["comment"])
                comment = comment.strip().replace("\n", "\n" + " " * 14)
            except AttributeError:  # Assume comment is a list
                try:
                    comment = ret["comment"].join(" ").replace(
                        "\n", "\n" + " " * 13)
                except AttributeError:
                    # Comment isn't a list either, just convert to string
                    comment = str(ret["comment"])
                    comment = comment.strip().replace("\n", "\n" + " " * 14)
            # If there is a data attribute, append it to the comment
            if "data" in ret:
                if isinstance(ret["data"], list):
                    for item in ret["data"]:
                        comment = "{} {}".format(comment, item)
                elif isinstance(ret["data"], dict):
                    for key, value in ret["data"].items():
                        comment = "{}\n\t\t{}: {}".format(comment, key, value)
                else:
                    comment = "{} {}".format(comment, ret["data"])
            for detail in ["start_time", "duration"]:
                ret.setdefault(detail, "")
            if ret["duration"] != "":
                ret["duration"] = "{} ms".format(ret["duration"])
            svars = {
                "tcolor": tcolor,
                "comps": comps,
                "ret": ret,
                "comment": salt.utils.data.decode(comment),
                # This nukes any trailing \n and indents the others.
                "colors": colors,
            }
            hstrs.extend([sline.format(**svars) for sline in state_lines])
            changes = "     Changes:   " + ctext
            hstrs.append("{0}{1}{2[ENDC]}".format(tcolor, changes, colors))

            if "warnings" in ret:
                rcounts.setdefault("warnings", 0)
                rcounts["warnings"] += 1
                wrapper = textwrap.TextWrapper(width=80,
                                               initial_indent=" " * 14,
                                               subsequent_indent=" " * 14)
                hstrs.append(
                    "   {colors[LIGHT_RED]} Warnings: {0}{colors[ENDC]}".
                    format(wrapper.fill("\n".join(ret["warnings"])).lstrip(),
                           colors=colors))

        # Append result counts to end of output
        colorfmt = "{0}{1}{2[ENDC]}"
        rlabel = {
            True: "Succeeded",
            False: "Failed",
            None: "Not Run",
            "warnings": "Warnings",
        }
        count_max_len = max([len(str(x)) for x in rcounts.values()] or [0])
        label_max_len = max([len(x) for x in rlabel.values()] or [0])
        line_max_len = label_max_len + count_max_len + 2  # +2 for ': '
        hstrs.append(
            colorfmt.format(
                colors["CYAN"],
                "\nSummary for {}\n{}".format(host, "-" * line_max_len),
                colors,
            ))

        def _counts(label, count):
            return "{0}: {1:>{2}}".format(label, count,
                                          line_max_len - (len(label) + 2))

        # Successful states
        changestats = []
        if None in rcounts and rcounts.get(None, 0) > 0:
            # test=True states
            changestats.append(
                colorfmt.format(
                    colors["LIGHT_YELLOW"],
                    "unchanged={}".format(rcounts.get(None, 0)),
                    colors,
                ))
        if nchanges > 0:
            changestats.append(
                colorfmt.format(colors["GREEN"], "changed={}".format(nchanges),
                                colors))
        if changestats:
            changestats = " ({})".format(", ".join(changestats))
        else:
            changestats = ""
        hstrs.append(
            colorfmt.format(
                colors["GREEN"],
                _counts(rlabel[True],
                        rcounts.get(True, 0) + rcounts.get(None, 0)),
                colors,
            ) + changestats)

        # Failed states
        num_failed = rcounts.get(False, 0)
        hstrs.append(
            colorfmt.format(
                colors["RED"] if num_failed else colors["CYAN"],
                _counts(rlabel[False], num_failed),
                colors,
            ))

        if __opts__.get("state_output_pct", False):
            # Add success percentages to the summary output
            try:
                success_pct = round(
                    ((rcounts.get(True, 0) + rcounts.get(None, 0)) /
                     (sum(rcounts.values()) - rcounts.get("warnings", 0))) *
                    100,
                    2,
                )

                hstrs.append(
                    colorfmt.format(
                        colors["GREEN"],
                        _counts("Success %", success_pct),
                        colors,
                    ))
            except ZeroDivisionError:
                pass

            # Add failure percentages to the summary output
            try:
                failed_pct = round(
                    (num_failed /
                     (sum(rcounts.values()) - rcounts.get("warnings", 0))) *
                    100,
                    2,
                )

                hstrs.append(
                    colorfmt.format(
                        colors["RED"] if num_failed else colors["CYAN"],
                        _counts("Failure %", failed_pct),
                        colors,
                    ))
            except ZeroDivisionError:
                pass

        num_warnings = rcounts.get("warnings", 0)
        if num_warnings:
            hstrs.append(
                colorfmt.format(
                    colors["LIGHT_RED"],
                    _counts(rlabel["warnings"], num_warnings),
                    colors,
                ))
        totals = "{0}\nTotal states run: {1:>{2}}".format(
            "-" * line_max_len,
            sum(rcounts.values()) - rcounts.get("warnings", 0),
            line_max_len - 7,
        )
        hstrs.append(colorfmt.format(colors["CYAN"], totals, colors))

        if __opts__.get("state_output_profile"):
            sum_duration = sum(rdurations)
            duration_unit = "ms"
            # convert to seconds if duration is 1000ms or more
            if sum_duration > 999:
                sum_duration /= 1000
                duration_unit = "s"
            total_duration = "Total run time: {} {}".format(
                "{:.3f}".format(sum_duration).rjust(line_max_len - 5),
                duration_unit)
            hstrs.append(
                colorfmt.format(colors["CYAN"], total_duration, colors))

    if strip_colors:
        host = salt.output.strip_esc_sequence(host)
    hstrs.insert(0, "{0}{1}:{2[ENDC]}".format(hcolor, host, colors))
    return "\n".join(hstrs), nchanges > 0
Пример #5
0
def list_tab(user):
    """
    Return the contents of the specified user's crontab

    CLI Example:

    .. code-block:: bash

        salt '*' cron.list_tab root
    """
    data = raw_cron(user)
    ret = {"pre": [], "crons": [], "special": [], "env": []}
    flag = False
    comment = None
    identifier = None
    for line in data.splitlines():
        if line == "# Lines below here are managed by Salt, do not edit":
            flag = True
            continue
        if flag:
            commented_cron_job = False
            if line.startswith("#DISABLED#"):
                # It's a commented cron job
                line = line[10:]
                commented_cron_job = True
            if line.startswith("@"):
                # Its a "special" line
                dat = {}
                comps = line.split()
                if len(comps) < 2:
                    # Invalid line
                    continue
                dat["spec"] = comps[0]
                dat["cmd"] = " ".join(comps[1:])
                dat["identifier"] = identifier
                dat["comment"] = comment
                dat["commented"] = False
                if commented_cron_job:
                    dat["commented"] = True
                ret["special"].append(dat)
                identifier = None
                comment = None
                commented_cron_job = False
            elif line.startswith("#"):
                # It's a comment! Catch it!
                comment_line = line.lstrip("# ")

                # load the identifier if any
                if SALT_CRON_IDENTIFIER in comment_line:
                    parts = comment_line.split(SALT_CRON_IDENTIFIER)
                    comment_line = parts[0].rstrip()
                    # skip leading :
                    if len(parts[1]) > 1:
                        identifier = parts[1][1:]

                if comment is None:
                    comment = comment_line
                else:
                    comment += "\n" + comment_line
            elif line.find("=") > 0 and (
                " " not in line or line.index("=") < line.index(" ")
            ):
                # Appears to be a ENV setup line
                comps = line.split("=", 1)
                dat = {}
                dat["name"] = comps[0]
                dat["value"] = comps[1]
                ret["env"].append(dat)
            elif len(line.split(" ")) > 5:
                # Appears to be a standard cron line
                comps = line.split(" ")
                dat = {
                    "minute": comps[0],
                    "hour": comps[1],
                    "daymonth": comps[2],
                    "month": comps[3],
                    "dayweek": comps[4],
                    "identifier": identifier,
                    "cmd": " ".join(comps[5:]),
                    "comment": comment,
                    "commented": False,
                }
                if commented_cron_job:
                    dat["commented"] = True
                ret["crons"].append(dat)
                identifier = None
                comment = None
                commented_cron_job = False
        else:
            ret["pre"].append(line)
    return ret
Пример #6
0
def list_tab(user):
    '''
    Return the contents of the specified user's crontab

    CLI Example:

    .. code-block:: bash

        salt '*' cron.list_tab root
    '''
    data = raw_cron(user)
    ret = {'pre': [], 'crons': [], 'special': [], 'env': []}
    flag = False
    comment = None
    identifier = None
    for line in data.splitlines():
        if line == '# Lines below here are managed by Salt, do not edit':
            flag = True
            continue
        if flag:
            commented_cron_job = False
            if line.startswith('#DISABLED#'):
                # It's a commented cron job
                line = line[10:]
                commented_cron_job = True
            if line.startswith('@'):
                # Its a "special" line
                dat = {}
                comps = line.split()
                if len(comps) < 2:
                    # Invalid line
                    continue
                dat['spec'] = comps[0]
                dat['cmd'] = ' '.join(comps[1:])
                dat['identifier'] = identifier
                dat['comment'] = comment
                dat['commented'] = False
                if commented_cron_job:
                    dat['commented'] = True
                ret['special'].append(dat)
                identifier = None
                comment = None
                commented_cron_job = False
            elif line.startswith('#'):
                # It's a comment! Catch it!
                comment_line = line.lstrip('# ')

                # load the identifier if any
                if SALT_CRON_IDENTIFIER in comment_line:
                    parts = comment_line.split(SALT_CRON_IDENTIFIER)
                    comment_line = parts[0].rstrip()
                    # skip leading :
                    if len(parts[1]) > 1:
                        identifier = parts[1][1:]

                if comment is None:
                    comment = comment_line
                else:
                    comment += '\n' + comment_line
            elif line.find('=') > 0 and (' ' not in line
                                         or line.index('=') < line.index(' ')):
                # Appears to be a ENV setup line
                comps = line.split('=', 1)
                dat = {}
                dat['name'] = comps[0]
                dat['value'] = comps[1]
                ret['env'].append(dat)
            elif len(line.split(' ')) > 5:
                # Appears to be a standard cron line
                comps = line.split(' ')
                dat = {
                    'minute': comps[0],
                    'hour': comps[1],
                    'daymonth': comps[2],
                    'month': comps[3],
                    'dayweek': comps[4],
                    'identifier': identifier,
                    'cmd': ' '.join(comps[5:]),
                    'comment': comment,
                    'commented': False
                }
                if commented_cron_job:
                    dat['commented'] = True
                ret['crons'].append(dat)
                identifier = None
                comment = None
                commented_cron_job = False
        else:
            ret['pre'].append(line)
    return ret