Skip to content

Commit

Permalink
agent_based api: move code: render
Browse files Browse the repository at this point in the history
Change-Id: I0168e7485fa7837390b638a141dd184da5eb9c34
  • Loading branch information
mo-ki committed Nov 6, 2023
1 parent 8fee0fb commit efad3e1
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 57 deletions.
2 changes: 1 addition & 1 deletion cmk/base/check_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@
from cmk.checkengine.submitters import ServiceDetails, ServiceState

import cmk.base.config as _config
from cmk.base.api.agent_based import render as _render

# pylint: disable=unused-import
from cmk.base.api.agent_based.register.utils_legacy import (
LegacyCheckDefinition as LegacyCheckDefinition,
)

from cmk.agent_based.v1 import render as _render
from cmk.agent_based.v1_backend.plugin_contexts import (
host_name as host_name, # pylint: disable=unused-import
)
Expand Down
13 changes: 4 additions & 9 deletions cmk/base/plugins/agent_based/agent_based_api/v1/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@
# Copyright (C) 2019 Checkmk 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.
"""
The "render" namespace adds functions to render values in a human readable way.

All of the render functions take a single numerical value as an argument, and return
a string.
"""
from cmk.base.api.agent_based.render import ( # pylint: disable=redefined-builtin
from cmk.agent_based.v1.render import ( # pylint: disable=redefined-builtin
bytes,
date,
datetime,
Expand All @@ -23,15 +18,15 @@
)

__all__ = [
"bytes",
"date",
"datetime",
"timespan",
"disksize",
"bytes",
"filesize",
"frequency",
"iobandwidth",
"networkbandwidth",
"nicspeed",
"iobandwidth",
"percent",
"timespan",
]
2 changes: 2 additions & 0 deletions packages/cmk-agent-based/cmk/agent_based/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# 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.

from . import render
from ._checking_classes import (
CheckResult,
DiscoveryResult,
Expand All @@ -27,6 +28,7 @@
"ServiceLabel",
"State",
"regex",
"render",
"CheckResult",
"DiscoveryResult",
]
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
# Copyright (C) 2019 Checkmk 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.
"""Render functions for check development
"""
The "render" namespace adds functions to render values in a human readable way.
These are meant to be exposed in the API
All of the render functions take a single numerical value as an argument, and return
a string.
"""
import math
import time
from collections.abc import Iterable
import math as _math
import time as _time
from collections.abc import Iterable as _Iterable

_DATE_FORMAT = "%b %d %Y"

Expand Down Expand Up @@ -48,7 +50,7 @@ def date(epoch: float | None) -> str:
"""
if epoch is None:
return "never"
return time.strftime(_DATE_FORMAT, time.localtime(float(epoch)))
return _time.strftime(_DATE_FORMAT, _time.localtime(float(epoch)))


def datetime(epoch: float | None) -> str:
Expand All @@ -65,10 +67,10 @@ def datetime(epoch: float | None) -> str:
"""
if epoch is None:
return "never"
return time.strftime("%s %%H:%%M:%%S" % _DATE_FORMAT, time.localtime(float(epoch)))
return _time.strftime(f"{_DATE_FORMAT} %H:%M:%S", _time.localtime(float(epoch)))


def _gen_timespan_chunks(seconds: float, nchunks: int) -> Iterable[str]:
def _gen_timespan_chunks(seconds: float, nchunks: int) -> _Iterable[str]:
if seconds < 0:
raise ValueError("Cannot render negative timespan")

Expand All @@ -79,7 +81,7 @@ def _gen_timespan_chunks(seconds: float, nchunks: int) -> Iterable[str]:

for unit, scale in _TIME_UNITS[start : start + nchunks]:
last_chunk = unit.endswith("seconds")
value = (round if last_chunk else int)(seconds / scale) # type: ignore[operator]
value = round(seconds / scale) if last_chunk else int(seconds / scale)
yield f"{value:.0f} {unit if value != 1 else unit[:-1]}"
if last_chunk:
break
Expand All @@ -105,7 +107,7 @@ def timespan(seconds: float) -> str:
"""
ts = " ".join(_gen_timespan_chunks(float(seconds), nchunks=2))
if ts == "0 %s" % _TIME_UNITS[-1][0]:
if ts == f"0 {_TIME_UNITS[-1][0]}":
ts = "0 seconds"
return ts

Expand All @@ -119,21 +121,21 @@ def _digits_left(value: float) -> int:
"""
try:
return max(int(math.log10(abs(value)) + 1), 1)
return max(int(_math.log10(abs(value)) + 1), 1)
except ValueError:
return 1


def _auto_scale(value: float, use_si_units: bool, add_bytes_prefix: bool = True) -> tuple[str, str]:
if use_si_units:
base = 1000
base = 1000.0
size_prefixes = _SIZE_PREFIXES_SI
else:
base = 1024
base = 1024.0
size_prefixes = _SIZE_PREFIXES_IEC

try:
log_value = int(math.log(abs(value), base))
log_value = int(_math.log(abs(value), base))
except ValueError:
log_value = 0

Expand All @@ -142,7 +144,7 @@ def _auto_scale(value: float, use_si_units: bool, add_bytes_prefix: bool = True)
if add_bytes_prefix:
unit = (unit + ("B" if use_si_units else "iB")).lstrip("i")
scaled_value = float(value) / base**exponent
fmt = "%%.%df" % max(3 - _digits_left(scaled_value), 0)
fmt = f"%.{max(3 - _digits_left(scaled_value), 0)}f"
return fmt % scaled_value, unit


Expand All @@ -153,7 +155,8 @@ def frequency(hertz: float) -> str:
>>> frequency(1e10 / 3.)
'3.33 GHz'
"""
return "%s %sHz" % _auto_scale(float(hertz), use_si_units=True, add_bytes_prefix=False)
value_str, unit = _auto_scale(float(hertz), use_si_units=True, add_bytes_prefix=False)
return f"{value_str} {unit}Hz"


def disksize(bytes_: float) -> str:
Expand All @@ -164,7 +167,7 @@ def disksize(bytes_: float) -> str:
'1.02 kB'
"""
value_str, unit = _auto_scale(float(bytes_), use_si_units=True)
return "{} {}".format(value_str if unit != "B" else value_str.split(".")[0], unit)
return f"{value_str if unit != 'B' else value_str.split('.')[0]} {unit}"


def bytes(bytes_: float) -> str: # pylint: disable=redefined-builtin
Expand All @@ -175,7 +178,7 @@ def bytes(bytes_: float) -> str: # pylint: disable=redefined-builtin
'1.00 MiB'
"""
value_str, unit = _auto_scale(float(bytes_), use_si_units=False)
return "{} {}".format(value_str if unit != "B" else value_str.split(".")[0], unit)
return f"{value_str if unit != 'B' else value_str.split('.')[0]} {unit}"


def filesize(bytes_: float) -> str:
Expand All @@ -185,16 +188,17 @@ def filesize(bytes_: float) -> str:
>>> filesize(12345678)
'12,345,678 B'
"""
val_str = "%.0f" % float(bytes_)
val_str = f"{float(bytes_):.0f}"
offset = len(val_str) % 3

groups = [val_str[0:offset]] + [val_str[i : i + 3] for i in range(offset, len(val_str), 3)]
return "%s B" % ",".join(groups).strip(",")
return f"{','.join(groups).strip(',')} B"


def networkbandwidth(octets_per_sec: float) -> str:
"""Render network bandwidth using an appropriate SI prefix"""
return "%s %sit/s" % _auto_scale(float(octets_per_sec) * 8, use_si_units=True)
value_str, unit = _auto_scale(float(octets_per_sec) * 8, use_si_units=True)
return f"{value_str} {unit}it/s"


def nicspeed(octets_per_sec: float) -> str:
Expand All @@ -219,7 +223,8 @@ def iobandwidth(bytes_: float) -> str:
'128 B/s'
"""
return "%s %s/s" % _auto_scale(float(bytes_), use_si_units=True)
value_str, unit = _auto_scale(float(bytes_), use_si_units=True)
return f"{value_str} {unit}/s"


def percent(percentage: float) -> str:
Expand All @@ -246,3 +251,18 @@ def percent(percentage: float) -> str:

# this includes negative values!
return f"{value:.2f}%"


__all__ = [
"bytes",
"date",
"datetime",
"disksize",
"filesize",
"frequency",
"iobandwidth",
"networkbandwidth",
"nicspeed",
"percent",
"timespan",
]
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
# 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 time

import pytest

from tests.testlib import set_timezone

import cmk.base.api.agent_based.render as render
from cmk.agent_based.v1 import render


@pytest.mark.parametrize(
Expand All @@ -19,9 +18,9 @@
(1587908220.0, "Apr 26 2020"),
],
)
def test_date(epoch: float | None, output: str) -> None:
with set_timezone("UTC"):
assert output == render.date(epoch=epoch)
def test_date(epoch: float | None, output: str, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(time, "localtime", time.gmtime)
assert output == render.date(epoch=epoch)


@pytest.mark.parametrize(
Expand All @@ -32,9 +31,9 @@ def test_date(epoch: float | None, output: str) -> None:
(1587908220.0, "Apr 26 2020 13:37:00"),
],
)
def test_datetime(epoch: float | None, output: str) -> None:
with set_timezone("UTC"):
assert output == render.datetime(epoch=epoch)
def test_datetime(epoch: float | None, output: str, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(time, "localtime", time.gmtime)
assert output == render.datetime(epoch=epoch)


@pytest.mark.parametrize(
Expand Down Expand Up @@ -72,7 +71,7 @@ def test_timespan_negative() -> None:
],
)
def test__digits_left(value: float, output: int) -> None:
assert output == render._digits_left(value)
assert output == render._digits_left(value) # pylint: disable=protected-access


@pytest.mark.parametrize(
Expand All @@ -90,7 +89,7 @@ def test__digits_left(value: float, output: int) -> None:
],
)
def test__auto_scale(value: float, use_si_units: bool, output: tuple[str, str]) -> None:
assert output == render._auto_scale(value, use_si_units)
assert output == render._auto_scale(value, use_si_units) # pylint: disable=protected-access


@pytest.mark.parametrize(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

import pytest

from cmk.base.api.agent_based import render, utils
from cmk.base.api.agent_based import utils

from cmk.agent_based.v1 import Metric, Result, State
from cmk.agent_based.v1 import Metric, render, Result, State


@pytest.mark.parametrize(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@
| light heartedly! |
+---------------------------------------------------------+
"""
from types import ModuleType

from cmk.base.plugins.agent_based.agent_based_api import v1

from cmk.agent_based import v1 as v1_new_location


def _names(space):
return sorted(n for n in dir(space) if not n.startswith("_"))
def _names(space: ModuleType) -> set[str]:
return {n for n in dir(space) if not n.startswith("_")}


def test_v1() -> None:
if _names(v1) != [
expected = {
"Attributes",
"CheckResult",
"GetRateError",
Expand Down Expand Up @@ -61,13 +65,14 @@ def test_v1() -> None:
"render",
"startswith",
"type_defs",
]:
}
if _names(v1) != expected: # TODO or _names(v1_new_location) != expected:
# do not output actual names. Changing this is meant to hurt!
raise AssertionError(__doc__)


def test_v1_render() -> None:
if _names(v1.render) != [
expected = {
"bytes",
"date",
"datetime",
Expand All @@ -79,33 +84,37 @@ def test_v1_render() -> None:
"nicspeed",
"percent",
"timespan",
]:
raise AssertionError(__doc__)
}
assert _names(v1.render) == expected
assert _names(v1_new_location.render) == expected


def test_v1_type_defs() -> None:
if _names(v1.type_defs) != [
expected = {
"CheckResult",
"DiscoveryResult",
"HostLabelGenerator",
"InventoryResult",
"StringByteTable",
"StringTable",
]:
}
if _names(v1.type_defs) != expected: # TODO or _names(v1_new_location.type_defs) != expected:
raise AssertionError(__doc__)


def test_v1_register() -> None:
if _names(v1.register) != [
expected = {
"RuleSetType",
"agent_section",
"check_plugin",
"inventory_plugin",
"snmp_section",
]:
}
if _names(v1.register) != expected: # TODO or _names(v1_new_location.register) != expected:
raise AssertionError(__doc__)


def test_v1_clusterize() -> None:
if _names(v1.clusterize) != ["make_node_notice_results"]:
expected = {"make_node_notice_results"}
if _names(v1.clusterize) != expected: # TODO or _names(v1_new_location.clusterize) != expected:
raise AssertionError(__doc__)

0 comments on commit efad3e1

Please sign in to comment.