From: Pere Diaz Bou Date: Tue, 26 Oct 2021 12:42:17 +0000 (+0200) Subject: monitoring/grafana: Grafana query tester X-Git-Tag: v17.1.0~426^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F43669%2Fhead;p=ceph.git monitoring/grafana: Grafana query tester Signed-off-by: Pere Diaz Bou --- diff --git a/monitoring/grafana/dashboards/.pylintrc b/monitoring/grafana/dashboards/.pylintrc new file mode 120000 index 000000000000..aa04b020cb4c --- /dev/null +++ b/monitoring/grafana/dashboards/.pylintrc @@ -0,0 +1 @@ +../../../src/pybind/mgr/dashboard/.pylintrc \ No newline at end of file diff --git a/monitoring/grafana/dashboards/CMakeLists.txt b/monitoring/grafana/dashboards/CMakeLists.txt index b8f59fc04b84..5711957923c8 100644 --- a/monitoring/grafana/dashboards/CMakeLists.txt +++ b/monitoring/grafana/dashboards/CMakeLists.txt @@ -12,7 +12,9 @@ endif() if(WITH_GRAFANA) include(AddCephTest) - add_tox_test(grafana TOX_ENVS grafonnet-check) + add_tox_test(grafana-check TOX_ENVS grafonnet-check) + add_tox_test(grafana-query-test TOX_ENVS promql-query-test) + add_tox_test(grafana-lint TOX_ENVS lint) set(ver 0.1.0) set(name grafonnet-lib) include(ExternalProject) @@ -30,7 +32,7 @@ if(WITH_GRAFANA) ${name}) ExternalProject_Get_Property(${name} SOURCE_DIR) set_property( - TEST run-tox-grafana + TEST run-tox-grafana-check run-tox-grafana-query-test run-tox-grafana-lint APPEND PROPERTY ENVIRONMENT GRAFONNET_PATH=${SOURCE_DIR}/grafonnet) diff --git a/monitoring/grafana/dashboards/requirements-lint.txt b/monitoring/grafana/dashboards/requirements-lint.txt new file mode 100644 index 000000000000..f9a3c772f79d --- /dev/null +++ b/monitoring/grafana/dashboards/requirements-lint.txt @@ -0,0 +1,18 @@ +attrs==21.2.0 +behave==1.2.6 +py==1.10.0 +pyparsing==2.4.7 +PyYAML==6.0 +types-PyYAML==6.0.0 +typing-extensions==3.10.0.2 +termcolor==1.1.0 +types-termcolor==1.1.2 +dataclasses==0.6 +types-dataclasses==0.6.1 +six==1.16.0 +toml==0.10.2 +pylint==2.6.0 +isort==5.10.0 +mypy==0.910 +mypy-extensions==0.4.3 +prettytable==2.4.0 diff --git a/monitoring/grafana/dashboards/tests/__init__.py b/monitoring/grafana/dashboards/tests/__init__.py new file mode 100644 index 000000000000..204a5f0d721b --- /dev/null +++ b/monitoring/grafana/dashboards/tests/__init__.py @@ -0,0 +1,187 @@ +import re +import subprocess +import sys +import tempfile +from dataclasses import asdict, dataclass, field +from typing import Any, List + +import yaml + + +@dataclass +class InputSeries: + series: str = '' + values: str = '' + +@dataclass +class ExprSample: + labels: str = '' + value: float = -1 + +@dataclass +class PromqlExprTest: + expr: str = '' + eval_time: str = '1m' + exp_samples: List[ExprSample] = field(default_factory=list) + +@dataclass +class Test: + interval: str = '1m' + input_series: List[InputSeries] = field(default_factory=list) + promql_expr_test: List[PromqlExprTest] = field(default_factory=list) + + +@dataclass +class TestFile: + evaluation_interval: str = '1m' + tests: List[Test] = field(default_factory=list) + + +class PromqlTest: + """ + Base class to provide prometheus query test capabilities. After setting up + the query test with its input and expected output it's expected to run promtool. + + https://prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/#test-yml + + The workflow of testing would be something like: + + # add prometheus query to test + self.set_expression('bonding_slaves > 0') + + # add some prometheus input series + self.add_series('bonding_slaves{master="bond0"}', '2') + self.add_series('bonding_slaves{master="bond1"}', '3') + self.add_series('node_network_receive_bytes{instance="127.0.0.1", + device="eth1"}', "10 100 230 22") + + # expected output of the query + self.add_exp_samples('bonding_slaves{master="bond0"}', 2) + self.add_exp_samples('bonding_slaves{master="bond1"}', 3) + + # at last, always call promtool with: + self.assertTrue(self.run_promtool()) + # assertTrue means it expect promtool to succeed + """ + + def __init__(self): + self.test_output_file = tempfile.NamedTemporaryFile('w+') + + self.test_file = TestFile() + self.test = Test() + self.promql_expr_test = PromqlExprTest() + self.test.promql_expr_test.append(self.promql_expr_test) + self.test_file.tests.append(self.test) + + self.variables = {} + + def __del__(self): + self.test_output_file.close() + + + def set_evaluation_interval(self, interval: int, unit: str = 'm') -> None: + """ + Set the evaluation interval of the time series + + Args: + interval (int): number of units. + unit (str): unit type: 'ms', 's', 'm', etc... + """ + self.test_file.evaluation_interval = f'{interval}{unit}' + + def set_interval(self, interval: int, unit: str = 'm') -> None: + """ + Set the duration of the time series + + Args: + interval (int): number of units. + unit (str): unit type: 'ms', 's', 'm', etc... + """ + self.test.interval = f'{interval}{unit}' + + def set_expression(self, expr: str) -> None: + """ + Set the prometheus expression/query used to filter data. + + Args: + expr(str): expression/query. + """ + self.promql_expr_test.expr = expr + + def add_series(self, series: str, values: str) -> None: + """ + Add a series to the input. + + Args: + series(str): Prometheus series. + Notation: '{