mirror of
https://github.com/yavook/kiwi-simple-metrics.git
synced 2024-11-24 08:23:01 +00:00
External Metric working
This commit is contained in:
parent
627edab9e9
commit
5128cfefc7
3 changed files with 110 additions and 38 deletions
|
@ -4,3 +4,5 @@ echo "Dummy"
|
|||
echo "95"
|
||||
echo "normal"
|
||||
awk "BEGIN{srand(); r=rand(); print r * 100}"
|
||||
|
||||
exit 0
|
|
@ -1,3 +1,5 @@
|
|||
import os
|
||||
import subprocess
|
||||
from typing import Iterator
|
||||
|
||||
from ..settings import SETTINGS
|
||||
|
@ -5,16 +7,112 @@ from ._report import Report, ReportData
|
|||
|
||||
|
||||
def _hwdata() -> Iterator[ReportData]:
|
||||
yield ReportData(
|
||||
name="Foo",
|
||||
value=69.42,
|
||||
threshold=80,
|
||||
inverted=False,
|
||||
format=SETTINGS.cpu.report,
|
||||
def parse_output(exe: os.PathLike) -> ReportData:
|
||||
try:
|
||||
# check exe is executable
|
||||
assert os.access(exe, os.X_OK)
|
||||
|
||||
# run exe
|
||||
# => TimeoutExpired: execution took too long
|
||||
execution = subprocess.run(
|
||||
args=[exe],
|
||||
stdout=subprocess.PIPE,
|
||||
timeout=SETTINGS.external.timeout,
|
||||
)
|
||||
|
||||
# look at the first four output lines
|
||||
# => UnicodeDecodeError: output is not decodable
|
||||
output = execution.stdout.decode().split("\n")[:4]
|
||||
|
||||
# extract and check name (fail if empty)
|
||||
assert (name := "".join(
|
||||
char
|
||||
for char in output[0]
|
||||
if char.isprintable()
|
||||
)[:100]) != ""
|
||||
|
||||
except (AssertionError,
|
||||
subprocess.TimeoutExpired,
|
||||
UnicodeDecodeError,
|
||||
IndexError):
|
||||
return ReportData.from_settings(
|
||||
name=os.path.basename(exe)[:100],
|
||||
value=100,
|
||||
settings=SETTINGS.external,
|
||||
)
|
||||
|
||||
try:
|
||||
# check exit status
|
||||
assert execution.returncode == 0
|
||||
|
||||
# check output length
|
||||
assert len(output) == 4
|
||||
|
||||
# extract threshold and value
|
||||
threshold = float(output[1])
|
||||
value = float(output[3])
|
||||
|
||||
# extract and check inversion
|
||||
assert (inverted := output[2].strip().lower()) in (
|
||||
"normal", "inverted",
|
||||
)
|
||||
|
||||
except (ValueError, AssertionError):
|
||||
return ReportData.from_settings(
|
||||
name=name,
|
||||
value=100,
|
||||
settings=SETTINGS.external,
|
||||
)
|
||||
|
||||
# success
|
||||
return ReportData(
|
||||
name=name,
|
||||
value=value,
|
||||
threshold=threshold,
|
||||
inverted=inverted == "inverted",
|
||||
format=SETTINGS.external.report,
|
||||
)
|
||||
|
||||
yield from (
|
||||
parse_output(exe)
|
||||
for exe in SETTINGS.external.executables
|
||||
)
|
||||
|
||||
|
||||
def external() -> Report | None:
|
||||
"""
|
||||
External Metric
|
||||
=====
|
||||
|
||||
This metric's values are defined external executables (e.g. shell scripts).
|
||||
Any executable with suitable output can be used as a value for this metric.
|
||||
|
||||
To comply, the executable's output must be UTF-8 decodable and start with
|
||||
four consecutive lines holding the following information:
|
||||
|
||||
1. value name
|
||||
2. percent threshold
|
||||
3. the string "normal" or "inverted", without quotes
|
||||
4. percent current value
|
||||
|
||||
The executable may produce additional output, which will be ignored.
|
||||
Percentages may be floating point numbers and must use a decimal point "."
|
||||
as a separator in that case.
|
||||
The output is evaluated once execution finishes with exit status 0.
|
||||
A report is generated for each executable. Its value name is stripped of
|
||||
non-printable characters and limited to a length of 100.
|
||||
|
||||
Non-compliance will be reported as failed values, i.e. normal values with a
|
||||
threshold of 0% and a value of 100%, in these cases:
|
||||
|
||||
- non-executable files and executables outputting non-UTF8:
|
||||
reported as the files' basename
|
||||
- executables with generally noncompliant outputs:
|
||||
reported as the first line of output
|
||||
- failure to parse any of the threshold, inversion or current value
|
||||
- otherwise compliant executables with non-zero exit status
|
||||
"""
|
||||
|
||||
return Report.aggregate(
|
||||
settings=SETTINGS.external,
|
||||
get_data=_hwdata,
|
||||
|
|
|
@ -82,39 +82,11 @@ class DiskMS(MetricSettings):
|
|||
|
||||
|
||||
class ExternalMS(MetricSettings):
|
||||
"""
|
||||
External Metric
|
||||
=====
|
||||
|
||||
This metric's values are defined external executables (e.g. bash scripts).
|
||||
Any executable with suitable output can be used as a value for this metric.
|
||||
|
||||
To comply, the executable's output must start with four consecutive lines,
|
||||
holding the following information:
|
||||
|
||||
1. value name (max. 100 characters)
|
||||
2. percent threshold
|
||||
3. the string "normal" or "inverted", without quotes
|
||||
4. percent current value
|
||||
|
||||
Percentages may be floating point numbers and must use a decimal point "."
|
||||
as a separator in that case.
|
||||
|
||||
Non-compliance will be reported as failed values as follows:
|
||||
|
||||
- non-executable files are reported as the files' basename
|
||||
- executables with generally noncompliant outputs are reported as
|
||||
the first line of their output truncated to 100 chars
|
||||
- failure to parse the threshold or inversion results in
|
||||
an upper threshold of 0%
|
||||
- failure to parse the current value results in
|
||||
an upper threshold of 0% and a value of 100%
|
||||
- compliant executables with non-zero exit status are still
|
||||
reported as a failed value
|
||||
"""
|
||||
|
||||
name: str = "External Metrics"
|
||||
name: str = "External Metric"
|
||||
threshold: float = 0
|
||||
|
||||
# path to executable files
|
||||
executables: list[FilePath] = Field(default_factory=list)
|
||||
|
||||
# wait at most this many seconds for each executable
|
||||
timeout: int = 60
|
||||
|
|
Loading…
Reference in a new issue