Exemple #1
0
def check_levels(
    value: float,
    *,
    levels_upper: Optional[Tuple[float, float]] = None,
    levels_lower: Optional[Tuple[float, float]] = None,
    metric_name: Optional[str] = None,
    render_func: Optional[Callable[[float], str]] = None,
    label: Optional[str] = None,
    boundaries: Optional[Tuple[Optional[float], Optional[float]]] = None,
    notice_only: bool = False,
) -> Generator[Union[Result, Metric], None, None]:
    """Generic function for checking a value against levels.

    Args:

        value:        The currently measured value
        levels_upper: A pair of upper thresholds. If value is larger than these, the
                      service goes to **WARN** or **CRIT**, respecively.
        levels_lower: A pair of lower thresholds. If value is smaller than these, the
                      service goes to **WARN** or **CRIT**, respecively.
        metric_name:  The name of the datasource in the RRD that corresponds to this value
                      or None in order not to generate a metric.
        render_func:  A single argument function to convert the value from float into a
                      human readable string.
        label:        The label to prepend to the output.
        boundaries:   Minimum and maximum to add to the metric.
        notice_only:  Only show up in service output if not OK (otherwise in details).
                      See `notice` keyword of `Result` class.

    Example:

        >>> result, metric = check_levels(
        ...     23.0,
        ...     levels_upper=(12., 42.),
        ...     metric_name="temperature",
        ...     label="Fridge",
        ...     render_func=lambda v: "%.1f°" % v,
        ... )
        >>> print(result.summary)
        Fridge: 23.0° (warn/crit at 12.0°/42.0°)
        >>> print(metric)
        Metric('temperature', 23.0, levels=(12.0, 42.0))

    """
    if render_func is None:
        render_func = lambda f: "%.2f" % f

    info_text = str(render_func(value))  # forgive wrong output type
    if label:
        info_text = "%s: %s" % (label, info_text)

    value_state, levels_text = _do_check_levels(value, levels_upper,
                                                levels_lower, render_func)

    if notice_only:
        yield Result(state=value_state, notice=info_text + levels_text)
    else:
        yield Result(state=value_state, summary=info_text + levels_text)
    if metric_name:
        yield Metric(metric_name,
                     value,
                     levels=levels_upper,
                     boundaries=boundaries)
Exemple #2
0
        (2, (3, 6), (1, 0), int, (State.OK, "")),
        (1, (3, 6), (1, 0), int, (State.OK, "")),
        (0, (3, 6), (1, 0), int, (State.WARN, " (warn/crit below 1/0)")),
        (-1, (3, 6), (1, 0), int, (State.CRIT, " (warn/crit below 1/0)")),
    ])
def test_boundaries(value, levels_upper, levels_lower, render_func, result):
    assert utils._do_check_levels(value, levels_upper, levels_lower,
                                  render_func) == result


@pytest.mark.parametrize("value, kwargs, result", [
    (5, {
        "metric_name": "battery",
        "render_func": render.percent,
    }, [
        Result(state=State.OK, summary="5.00%"),
        Metric("battery", 5.0),
    ]),
    (6, {
        "metric_name": "disk",
        "levels_upper": (4, 8),
        "render_func": lambda x: "%.2f years" % x,
        "label": "Disk Age",
    }, [
        Result(
            state=State.WARN,
            summary="Disk Age: 6.00 years (warn/crit at 4.00 years/8.00 years)"
        ),
        Metric("disk", 6.0, levels=(4., 8.)),
    ]),
    (5e-7, {
Exemple #3
0
 def _add_node_name(result: Result, node_name: str) -> Result:
     return Result(
         state=result.state,
         summary=result.summary,
         details='\n'.join(f"[{node_name}]: {line}" for line in result.details.splitlines()),
     )
Exemple #4
0
def aggregate_node_details(
    node_name: str,
    node_check_returns: Iterable[ABCCheckGenerated],
) -> Optional[Result]:
    """Aggregate the results of a node check into a single Result

    The results of the check on the node are aggregated into a single
    Result instance, showing all node results in its details.
    The state is the worst of all individual states (as checkmk would
    compute it for the service on the node).

    If no results for the nodes are available, None is returned.

    Example:
        To yield the summary results of every node of a cluster from within a
        cluster_check_function use

            for node_name, node_section in sections.values():
                summary_result = aggregate_node_details(
                    node_name,
                    check_my_plugin(item, node_section),
                )
                if summary_result is not None:
                    yield summary_result

        Note that this will send no text to the services summary, only to the
        details page.

    Args:
        node_name (str): The name of the node
        node_check_returns (Sequence[Union[IgnoreResults, Result, Metric]]): The return values or
        generator of the nodes check_function call

    Returns:
        Optional[Result]: Aggregated node result. None if the node check returned nothing.

    """

    # drop Metrics, we may be evaluating a generator
    try:
        returns_wo_metrics = [
            r for r in node_check_returns if not isinstance(r, Metric)
        ]
    except IgnoreResultsError:
        return None

    results = [r for r in returns_wo_metrics if isinstance(r, Result)]
    if not results or len(results) != len(
            returns_wo_metrics):  # latter: encountered IgnoreResults
        return None

    details_with_markers = [
        "%s%s" % (r.details.strip(), state_markers[int(r.state)])
        for r in results
    ]

    details_lines: List[str] = sum(
        (d.split('\n') for d in details_with_markers), [])

    return Result(
        state=state.worst(*(r.state for r in results)),
        details="\n".join("[%s]: %s" % (node_name, d) for d in details_lines),
    )
Exemple #5
0
def test_result_invalid(state_, summary, notice, details):
    with pytest.raises((TypeError, ValueError)):
        _ = Result(state=state_,
                   summary=summary,
                   notice=notice,
                   details=details)
Exemple #6
0
        ) for node in secondary_nodes for r in self._node_results.results[node])

    @staticmethod
    def _secondary_nodes_state(
        secondary_nodes: Sequence[str],
        levels: Tuple[float, float],
    ) -> State:
        count = len(secondary_nodes)
        return State.CRIT if count >= levels[1] else State(count >= levels[0])

    def metrics(self, node_name: Optional[str]) -> CheckResult:
        used_node = node_name or self._pivoting
        if not (metrics := self._node_results.metrics.get(used_node, ())):
            return
        yield Result(
            state=State.OK,
            notice=f"[{used_node}] Metrics: {', '.join(m.name for m in metrics)}",
        )
        yield from metrics


class NodeCheckExecutor:
    def __init__(self, *, service_id: ServiceID, persist_value_store_changes: bool) -> None:
        self._service_id = service_id
        self._persist_value_store_changes = persist_value_store_changes

    def __call__(
        self,
        check_function: Callable[..., CheckResult],
        cluster_kwargs: _Kwargs,
    ) -> NodeResults:
        """Dispatch the check function results for all nodes"""
Exemple #7
0
                        lambda tp: _check_timeperiod(tp, active_timeperiods))

    determined_check_params = cmk.base.checking.legacy_determine_check_params(
        rules)
    assert expected_result == determined_check_params, (
        "Determine params: Expected '%s' but got '%s'" %
        (expected_result, determined_check_params))


def _check_timeperiod(timeperiod, active_timeperiods):
    return timeperiod in active_timeperiods


@pytest.mark.parametrize("subresults, aggregated_results", [
    ([], cmk.base.checking.ITEM_NOT_FOUND),
    ([
        Result(state=state.OK, notice="details"),
    ], (0, "Everything looks OK - 1 detail available\ndetails", [])),
    ([
        Result(state=state.OK, summary="summary1", details="detailed info1"),
        Result(state=state.WARN, summary="summary2", details="detailed info2"),
    ], (1, "summary1, summary2(!)\ndetailed info1\ndetailed info2(!)", [])),
    ([
        Result(state=state.OK, summary="summary"),
        Metric(name="name", value=42),
    ], (0, "summary\nsummary", [("name", 42.0, None, None, None, None)])),
])
def test_aggregate_result(subresults, aggregated_results):
    assert cmk.base.checking._aggregate_results(
        subresults) == aggregated_results
Exemple #8
0
 def unfit_for_clustering(*args, **kwargs):
     yield Result(
         state=state.UNKNOWN,
         summary=("This service is not ready to handle clustered data. "
                  "Please change your configuration."),
     )
def test_node_returns_ok_and_warn():
    node_results = _check_function_node((_OK_RESULT, _WARN_RESULT))
    assert list(make_node_notice_results("test_node", node_results)) == [
        Result(state=State.OK, notice="[test_node]: I am fine"),
        Result(state=State.WARN, notice="[test_node]: Watch out"),
    ]
    ],
)
def test_parsing(string_table: StringTable, section: Section):
    assert parse(string_table) == section


@pytest.mark.parametrize(
    "section ,results",
    [
        pytest.param(
            [
                SplunkMessage("foo", "info", "host",
                              "2019-05-16T08:32:33+02:00", "msg")
            ],
            [
                Result(state=State.OK,
                       summary="2019-05-16T08:32:33+02:00 - host - msg")
            ],
            id="single message",
        ),
        pytest.param(
            [
                SplunkMessage("foo", "info", "host",
                              "2019-05-16T08:32:33+02:00", "msg"),
                SplunkMessage("bar", "info", "host",
                              "2019-05-16T08:32:33+02:00", "msg2"),
            ],
            [
                Result(state=State.OK,
                       summary="2019-05-16T08:32:33+02:00 - host - msg"),
                Result(state=State.OK,
                       summary="2019-05-16T08:32:33+02:00 - host - msg2"),
Exemple #11
0
def test_node_returns_details_only():
    node_results = _check_function_node((Result(state=State.OK,
                                                notice="This is detailed"), ))
    assert list(make_node_notice_results("test_node", node_results)) == [
        Result(state=State.OK, notice="[test_node]: This is detailed"),
    ]
Exemple #12
0
def test_node_returns_metric():
    node_results = _check_function_node((_OK_RESULT, Metric("panic", 42)))
    assert list(make_node_notice_results("test_node", node_results)) == [
        Result(state=State.OK, notice="[test_node]: I am fine"),
    ]
Exemple #13
0
def test_result(state_, summary, notice, details, expected_triple):
    result = Result(state=state_,
                    summary=summary,
                    notice=notice,
                    details=details)
    assert (result.state, result.summary, result.details) == expected_triple
Exemple #14
0
                      num_threads=1234,
                      max_threads=2468)
    params: Dict[str, Any] = {}
    result = set(check_cpu_threads(params, section))
    assert result == {
        Metric('thread_usage', 50.0),
        Metric('threads', 1234.0),
        Result(state=State.OK, summary='1234'),
        Result(state=State.OK, summary='Usage: 50.00%')
    }


@pytest.mark.parametrize('info, check_result', [
    ([[u'0.88', u'0.83', u'0.87', u'2/2148', u'21050', u'8']], {
        Metric('threads', 2148.0, levels=(2000.0, 4000.0)),
        Result(state=State.WARN, summary='2148 (warn/crit at 2000/4000)'),
    }),
    ([[u'0.88', u'0.83', u'0.87', u'2/1748', u'21050', u'8'], [u'124069']], {
        Metric('threads', 1748.0, levels=(2000.0, 4000.0)),
        Result(state=State.OK, summary='1748'),
        Metric('thread_usage', 1.408893438328672),
        Result(state=State.OK, summary='Usage: 1.41%')
    }),
])
def test_cpu_threads_regression(info, check_result):
    section = parse_cpu(info)
    assert section is not None
    params = {'levels': (2000, 4000)}
    assert list(discover_cpu_threads(section)) == [Service()]
    assert set(check_cpu_threads(params, section)) == check_result
    assert list(discovery_systemd_units_services_summary(section)) == discovered_services


@pytest.mark.parametrize(
    "item, params, section, check_results",
    [
        (
            "virtualbox",
            {
                "else": 2,
                "states": {"active": 0, "failed": 2, "inactive": 0},
                "states_default": 2,
            },
            SECTION,
            [
                Result(state=State.OK, summary="Status: active"),
                Result(state=State.OK, summary="LSB: VirtualBox Linux kernel module"),
            ],
        ),
        (
            "foo",
            {
                "else": 2,
                "states": {"active": 0, "failed": 2, "inactive": 0},
                "states_default": 2,
            },
            SECTION,
            [
                Result(state=State.CRIT, summary="Status: failed"),
                Result(
                    state=State.OK,
Exemple #16
0
def make_node_notice_results(
    node_name: str,
    node_check_results: CheckResult,
    *,
    force_ok: bool = False,
) -> Iterable[Result]:
    """Prepare results of a node for output in a cluster check function

    This function consumes everything from a check function (that is, a :class:`Result`,
    a :class:`Metric` or an :class:`IgnoreResults`) and returns an iterable of :class:`Result`s.

    The text is prepended with `'[node]: '`, and the text type is changed from `summary` to `notice`
    (see :class:`Result` for more details).

    Usage example:
        >>> def cluster_check_myplugin(item, section):
        ...     '''A cluster check function that just passes along all nodes' results'''
        ...     for node_name, node_section in sections.values():
        ...         yield from make_node_notice_results(
        ...             node_name,
        ...             check_myplugin(item, node_section),
        ...         )

        This will write text from every node to the services summary if the state is not OK,
        otherwise to the details text.

    Args:
        node_name:          The name of the node
        node_check_results: The return values or generator of the nodes check_function call
        force_ok:           If specified, the state of all results is chacnged to OK. In this case
                            the state marker corresponding to the original state is appended to
                            the text.

    Returns:
        The node results, with the text type `notice`.

    """

    # consume potential generator and drop Metrics
    try:
        returns_wo_metrics = [
            r for r in node_check_results if not isinstance(r, Metric)
        ]
    except IgnoreResultsError:
        return

    # check for encountered IgnoreResults (also tells mypy that it's all Results)
    results = [r for r in returns_wo_metrics if isinstance(r, Result)]
    if len(results) != len(returns_wo_metrics):
        return

    def _nodify(text: str, state: State) -> str:
        """Prepend node name and, if state is forced to OK, append state marker"""
        node_text = '\n'.join("[%s]: %s" % (node_name, line)
                              for line in text.split('\n'))
        if not force_ok:
            return node_text
        return "%s%s" % (node_text.rstrip(), state_markers[int(state)])

    for result in results:
        yield Result(
            state=State.OK if force_ok else result.state,
            notice=_nodify(result.summary, result.state),
            details=_nodify(result.details, result.state),
        )
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.
#import pytest  # type: ignore[import]

from cmk.base.api.agent_based.checking_classes import (
    IgnoreResults,
    IgnoreResultsError,
    Metric,
    Result,
    state,
)
from cmk.base.api.agent_based.clusterize import aggregate_node_details

_OK_RESULT = Result(state=state.OK, summary="I am fine")

_WARN_RESULT = Result(state=state.WARN, summary="Watch out")


def _check_function_node(test_results):
    for res in test_results:
        yield res


def test_node_returns_nothing():
    assert aggregate_node_details("test_node", _check_function_node(())) is None
    assert aggregate_node_details("test_node", ()) is None


def test_node_raises():
Exemple #18
0
def check_levels_predictive(
    value: float,
    *,
    levels: Dict[str, Any],
    metric_name: str,
    render_func: Optional[Callable[[float], str]] = None,
    label: Optional[str] = None,
    boundaries: Optional[Tuple[Optional[float], Optional[float]]] = None,
) -> Generator[Union[Result, Metric], None, None]:
    """Generic function for checking a value against levels.

    Args:

        value:        Currently measured value
        levels:       Predictive levels. These are used automatically.
                      Lower levels are imposed if the passed dictionary contains "lower"
                      as key, upper levels are imposed if it contains "upper" or
                      "levels_upper_min" as key.
                      If value is lower/higher than these, the service goes to **WARN**
                      or **CRIT**, respecively.
        metric_name:  Name of the datasource in the RRD that corresponds to this value
        render_func:  Single argument function to convert the value from float into a
                      human readable string.
                      readable fashion
        label:        Label to prepend to the output.
        boundaries:   Minimum and maximum to add to the metric.

    """
    if render_func is None:
        render_func = "%.2f".format

    # validate the metric name, before we can get the levels.
    Metric.validate_name(metric_name)

    try:
        ref_value, levels_tuple = cmk.base.prediction.get_levels(
            check_api_utils.host_name(),
            check_api_utils.service_description(),
            metric_name,
            levels,
            "MAX",
        )
        if ref_value:
            predictive_levels_msg = " (predicted reference: %s)" % render_func(
                ref_value)
        else:
            predictive_levels_msg = " (no reference for prediction yet)"

    except MKGeneralException as e:
        ref_value = None
        levels_tuple = (None, None, None, None)
        predictive_levels_msg = " (no reference for prediction: %s)" % e

    except Exception as e:
        if cmk.utils.debug.enabled():
            raise
        yield Result(state=State.UNKNOWN, summary="%s" % e)
        return

    levels_upper = (None if levels_tuple[0] is None or levels_tuple[1] is None
                    else (levels_tuple[0], levels_tuple[1]))

    levels_lower = (None if levels_tuple[2] is None or levels_tuple[3] is None
                    else (levels_tuple[2], levels_tuple[3]))

    value_state, levels_text = _do_check_levels(value, levels_upper,
                                                levels_lower, render_func)

    if label:
        info_text = "%s: %s%s" % (label, render_func(value),
                                  predictive_levels_msg)
    else:
        info_text = "%s%s" % (render_func(value), predictive_levels_msg)

    yield Result(state=value_state, summary=info_text + levels_text)
    yield Metric(metric_name,
                 value,
                 levels=levels_upper,
                 boundaries=boundaries)
    if ref_value:
        yield Metric("predict_%s" % metric_name, ref_value)
def test_node_returns_details_only():
    node_results = _check_function_node((Result(state=State.OK,
                                                notice="This is detailed"), ))
    state, text = aggregate_node_details("test_node", node_results)
    assert state is State.OK
    assert text == "[test_node]: This is detailed"