diff --git a/dashboards/annotations.py b/dashboards/annotations.py index bf2d63d2a5f3dfeefa51bd025aaf092ae383861a..1f8ec030f0aeb736a3f924d529d67d4b72a6ceb8 100644 --- a/dashboards/annotations.py +++ b/dashboards/annotations.py @@ -3,8 +3,6 @@ from dataclasses import dataclass @dataclass class Annotation: - # type: str = "influx" - # uid: str datasource: str iconColor: str name: str diff --git a/dashboards/dashboard_base.py b/dashboards/dashboard_base.py index 063c5d3320c5fd470f394698f161e6e8c35349fa..847e64bf7dd6db3a5ddafdfe971138ce290a4268 100644 --- a/dashboards/dashboard_base.py +++ b/dashboards/dashboard_base.py @@ -1,23 +1,19 @@ from dataclasses import asdict, dataclass from typing import List, Union -from grafanalib.core import (Annotations, Dashboard, DashboardList, GridPos, - Panel, Repeat, RowPanel, Stat, Template, - Templating, Threshold, Time, TimeSeries, Text) +from grafanalib.core import ( + Annotations, + Dashboard, + DashboardList, GridPos, + Panel, Repeat, RowPanel, Template, + Templating, Time, TimeSeries +) from grafanalib.influxdb import InfluxDBTarget from dashboards.annotations import Annotation from dashboards.influx_queries import Query -def get_text_panel(content: str, *, mode='markdown', **kwargs) -> Text: - return Text( - content=content, - mode=mode, - **kwargs - ) - - def get_influx_target(target_query: str, **kwargs) -> InfluxDBTarget: return InfluxDBTarget( query=target_query, @@ -29,25 +25,29 @@ def get_annotation(*args, **kwargs): return Annotations([Annotation(*args, **kwargs).to_json_data()]) -def get_commit_annotation(datasource: str, color: str, name: str, measurment_name: str): - query = ("SELECT first(\"commit\") FROM " +def get_commit_annotation(datasource: str, color: str, name: str, measurment_name: str, *, commit_key: str = "commit"): + query = (f"SELECT first(\"{commit_key}\") FROM " f"( SELECT * FROM \"{measurment_name}\" " - "WHERE host =~ /^$host$/ AND $timeFilter) GROUP BY \"commit\"") + f"WHERE host =~ /^$host$/ AND $timeFilter) GROUP BY \"${commit_key}\"") return get_annotation(datasource, color, name, query) -def get_dashboard_variable_query(name: str, template_query: str, - dataSource: str, *, - includeAll=True, - multi=True, - **kwargs) -> Template: - return Template(name, - template_query, - dataSource=dataSource, - type="query", - includeAll=includeAll, - multi=multi, - **kwargs) +def get_grid_pos(h: int, w: int, x: int, y: int): + return GridPos(h, w, x, y) + + +def pack_in_row(title: str, panels: Union[Panel, List[Panel]], + **kwargs) -> RowPanel: + + if isinstance(panels, Panel): + panels = [panels] + + return RowPanel( + title=title, + collapsed=True, + panels=panels, + **kwargs, + ) def get_dashboard_list(title: str, tags: List[str], **kwargs) -> DashboardList: @@ -129,88 +129,3 @@ def build_dashboard(options: DashboardOptions, ) return dashboard.auto_panel_ids() - - -def get_grid_pos(h: int, w: int, x: int, y: int): - return GridPos(h, w, x, y) - - -def pack_in_row(title: str, panels: Union[Panel, List[Panel]], - **kwargs) -> RowPanel: - - if isinstance(panels, Panel): - panels = [panels] - - return RowPanel( - title=title, - collapsed=True, - panels=panels, - **kwargs, - ) - - -def get_stat_panel(title: str, - dataSource: str, - stat_query: Query, - repeat: Template = None, - alias: str = "", - *, - maxPerRow=0, - **kwargs): - new_kwargs = { - 'alignment': 'center', - 'colorMode': 'value', - 'graphMode': 'area', - 'reduceCalc': 'last', - 'orientation': 'auto', - 'transparent': True, - } - new_kwargs.update(kwargs) - if repeat: - rep_args = ['h', repeat.name] - if maxPerRow: - rep_args.append(maxPerRow) - new_kwargs.setdefault('repeat', Repeat(*rep_args)) - return Stat( - title=title, - dataSource=dataSource, - targets=[get_influx_target(str(stat_query), alias=alias)], - thresholdType='percentage', - thresholds=[Threshold('green', 0, 0.0), Threshold('yellow', 1, 50.0), Threshold('red', 2, 80.0)], - ** new_kwargs, - ) - - -def get_color_regex_override(regex: str, color: str): - return { - "matcher": { - "id": "byRegexp", - "options": regex - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": color, - "mode": "fixed" - } - } - ] - } - - -def get_line_style_regex_override(regex: str, style: str): - return { - "matcher": { - "id": "byRegexp", - "options": regex - }, - "properties": [ - { - "id": "custom.lineStyle", - "value": { - "fill": style - } - } - ] - } diff --git a/dashboards/dashboard_fe2ti.py b/dashboards/dashboard_fe2ti.py index 577ca307be2aca21cb2c23a273bbbd0a0baf9168..19dbbab5dec9b6ffd574deb75628acdafc944c98 100644 --- a/dashboards/dashboard_fe2ti.py +++ b/dashboards/dashboard_fe2ti.py @@ -1,19 +1,16 @@ -from dashboards.dashboard_base import (DashboardOptions, build_dashboard, +from dashboards.dashboard_base import (DashboardOptions, + build_dashboard, get_commit_annotation, - get_dashboard_variable_query, pack_in_row, - TimeSeries, - get_influx_target, - Repeat, - get_color_regex_override, - get_line_style_regex_override, - get_text_panel) - -from dashboards.influx_queries import (Query, - join_variable_and, - show_tag_values) + Repeat,) + +from dashboards.overrides import get_line_style_regex_override, get_color_regex_override + +from dashboards.panels import PanelInfos, get_time_series_panel, get_text_panel +from dashboards.variables import get_dashboard_variable, Filter + +from dashboards.influx_queries import join_variable_and from dashboards.legends import Units -from collections import namedtuple description_markdown = r""" @@ -32,47 +29,26 @@ description_markdown = r""" - Information about the different Hosts on the NHR@FAU [Testcluster](https://hpc.fau.de/systems-services/documentation-instructions/clusters/test-cluster/) """ -Filter = namedtuple("Filter", ("name", "multi", "default_value"), defaults=('', True, '')) -PanelInfos = namedtuple("PanelInfos", ("name", "unit")) - - -def get_dashboard_variable(filter: Filter, measurment_name: str, data_source: str): - query = show_tag_values(measurment_name, filter.name) - kwargs = {} - if filter.multi: - kwargs.update({'includeAll': True, 'multi': True, }) - if filter.default_value: - kwargs.update({'default': filter.default_value}) - return get_dashboard_variable_query(filter.name, query, data_source, **kwargs) - - -def get_time_series_panel(field: PanelInfos, - data_source: str, - measurment_name: str, - *, - where=None, - group_by=None, - overrides=None): - query = Query(select_=field.name, - from_=measurment_name, - where_=where, - group_by=group_by) - - return TimeSeries( - title=field.name, - dataSource=data_source, - targets=[get_influx_target(str(query))], - unit=field.unit, - pointSize=9, - overrides=overrides, - ) + +PARDISO_COLOR = 'rgb(86,166, 75)' +UMFPACK_COLOR = 'rgb(242, 204, 12)' +ILU_COLOR = 'rgb(50, 116, 217)' +INTEL_LINESTYLE = "solid" +GCC_LINESTYLE = "dashed" def dashboard_fe2ti(): data_source = "fe2ti" measurment_name = "fe2ti" row_repeat = "micro_problem_size" - other_filters = [ + options = DashboardOptions( + title="FE2ti Benchmarks", + description="Benchmarks for fe2ti", + tags=['benchmark', 'fe2ti'], + timezone="browser", + ) + + filters = [ Filter("micro_problem_size", True, '8'), Filter("host", False, 'skylakesp2'), Filter("branch"), @@ -84,10 +60,6 @@ def dashboard_fe2ti(): Filter("ksp_tol") ] - other_vars = [get_dashboard_variable(filter, measurment_name, data_source) for filter in other_filters] - row_repeat_var = other_vars[0] - - where = join_variable_and([f.name for f in other_filters]) fields = [PanelInfos("Time to solution", Units.seconds), PanelInfos("DP [MFLOP/s] STAT Sum", Units.mflop_sec), PanelInfos("Operational intensity STAT Sum", Units.flop_per_byte), @@ -102,20 +74,19 @@ def dashboard_fe2ti(): PanelInfos('Power [W] STAT Sum', Units.watt), PanelInfos('Clock [MHz] STAT Avg', Units.megahertz), ] - options = DashboardOptions( - title="FE2ti Benchmarks", - description="Benchmarks for fe2ti", - tags=['benchmark', 'fe2ti'], - timezone="browser", - ) + filter_vars = [get_dashboard_variable(filter, measurment_name, data_source) for filter in filters] + + row_repeat_var = [fv for fv in filter_vars if fv.name == row_repeat][0] + + where = join_variable_and([f.name for f in filters]) annotations = get_commit_annotation(data_source, "red", "commits", measurment_name) overrides = [*[ get_color_regex_override(f".*[Ss]olver.*: {solver}.*", color) - for solver, color in [("pardiso", "#56A64B"), ("umfpack", "#F2CC0C"), ("ilu", "#3274D9")] + for solver, color in [("pardiso", PARDISO_COLOR), ("umfpack", UMFPACK_COLOR), ("ilu", ILU_COLOR)] ], - get_line_style_regex_override(".*intel.*", "solid"), - get_line_style_regex_override(".*gcc.*", "dashed") + get_line_style_regex_override(".*intel.*", INTEL_LINESTYLE), + get_line_style_regex_override(".*gcc.*", GCC_LINESTYLE) ] description_panel = pack_in_row("Legend", get_text_panel(description_markdown, title="FE2TI benchmarks")) @@ -125,7 +96,7 @@ def dashboard_fe2ti(): data_source, measurment_name, where=where, - group_by=[f.name for f in other_filters], + group_by=[f.name for f in filters], overrides=overrides, ) for field in fields] @@ -137,5 +108,5 @@ def dashboard_fe2ti(): ) return build_dashboard(options, rows=[description_panel, row], - templating=[*other_vars], + templating=[*filter_vars], annotations=annotations) diff --git a/dashboards/dashboard_list.py b/dashboards/dashboard_list.py index 7aa89ba09f8e98f3ed20a799fcf75d72aa6c70fd..f2340ae6006940d3995c19edc905df1313931e8a 100644 --- a/dashboards/dashboard_list.py +++ b/dashboards/dashboard_list.py @@ -1,15 +1,16 @@ from typing import List +from dashboards.variables import get_dashboard_variable_query from dashboards.dashboard_base import (DashboardOptions, build_dashboard, build_row_repeat_dashboard, get_commit_annotation, get_dashboard_list, - get_dashboard_variable_query, - get_grid_pos, get_stat_panel, + get_grid_pos, pack_in_row) from dashboards.influx_queries import (Query, get_variable_condition, join_conditions, join_variable_and, show_tag_values) +from dashboards.panels import get_stat_panel from dashboards.legends import AxisLabel, Units diff --git a/dashboards/influx_queries.py b/dashboards/influx_queries.py index 4dff22b1bbfe28f9d182957c5b83d6928420c59d..a0277d9503691e5f181b4e46c26d9ebd558df116 100644 --- a/dashboards/influx_queries.py +++ b/dashboards/influx_queries.py @@ -9,15 +9,36 @@ class Query: from_: str where_: str group_by: List[str] = field(default_factory=list) + select_string: bool = True + from_string: bool = True - def __str__(self): + def _get_select(self): + if self.select_string: + return f'SELECT \"{self.select_}\" ' + else: + return f'SELECT {self.select_} ' + + def _get_from(self): + if self.from_string: + return f'FROM \"{self.from_}\" ' + else: + return f'FROM {self.from_} ' + + def _get_where(self): + return f'WHERE ({self.where_}) AND $timeFilter ' - ret = f'SELECT \"{self.select_}\" ' - ret += f'FROM \"{self.from_}\" ' - ret += f'WHERE ({self.where_}) AND $timeFilter ' + def _get_group_by(self): if self.group_by: group_by = ', '.join(f'"{tag}"' for tag in self.group_by) - ret += f'GROUP BY {group_by}' + return f'GROUP BY {group_by}' + else: + return '' + + def __str__(self): + ret = self._get_select() + ret += self._get_from() + ret += self._get_where() + ret += self._get_group_by() return ret diff --git a/dashboards/overrides.py b/dashboards/overrides.py new file mode 100644 index 0000000000000000000000000000000000000000..d5d9755712940b4f699671ac550701e708882a68 --- /dev/null +++ b/dashboards/overrides.py @@ -0,0 +1,33 @@ +def get_color_regex_override(regex: str, color: str): + return { + "matcher": { + "id": "byRegexp", + "options": regex + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": color, + "mode": "fixed" + } + } + ] + } + + +def get_line_style_regex_override(regex: str, style: str): + return { + "matcher": { + "id": "byRegexp", + "options": regex + }, + "properties": [ + { + "id": "custom.lineStyle", + "value": { + "fill": style + } + } + ] + } diff --git a/dashboards/panels.py b/dashboards/panels.py new file mode 100644 index 0000000000000000000000000000000000000000..0f6f69eddf9196fff1db7382fd829f0c99cad025 --- /dev/null +++ b/dashboards/panels.py @@ -0,0 +1,72 @@ +from collections import namedtuple +from dashboards.influx_queries import Query +from grafanalib.core import TimeSeries, Text, Stat, Template, Repeat, Threshold +from dashboards.dashboard_base import get_influx_target + +PanelInfos = namedtuple("PanelInfos", ("name", "unit")) + + +def get_time_series_panel(field: PanelInfos, + data_source: str, + measurment_name: str, + *, + where=None, + group_by=None, + overrides=None): + is_regex = False + if measurment_name[0] == '/' and measurment_name[-1] == '/': + is_regex = True + query = Query(select_=field.name, + from_=measurment_name, + where_=where, + group_by=group_by, + from_string=not is_regex) + + return TimeSeries( + title=field.name, + dataSource=data_source, + targets=[get_influx_target(str(query))], + unit=field.unit, + pointSize=9, + overrides=overrides, + ) + + +def get_text_panel(content: str, *, mode='markdown', **kwargs) -> Text: + return Text( + content=content, + mode=mode, + **kwargs + ) + + +def get_stat_panel(title: str, + dataSource: str, + stat_query: Query, + repeat: Template = None, + alias: str = "", + *, + maxPerRow=0, + **kwargs): + new_kwargs = { + 'alignment': 'center', + 'colorMode': 'value', + 'graphMode': 'area', + 'reduceCalc': 'last', + 'orientation': 'auto', + 'transparent': True, + } + new_kwargs.update(kwargs) + if repeat: + rep_args = ['h', repeat.name] + if maxPerRow: + rep_args.append(maxPerRow) + new_kwargs.setdefault('repeat', Repeat(*rep_args)) + return Stat( + title=title, + dataSource=dataSource, + targets=[get_influx_target(str(stat_query), alias=alias)], + thresholdType='percentage', + thresholds=[Threshold('green', 0, 0.0), Threshold('yellow', 1, 50.0), Threshold('red', 2, 80.0)], + ** new_kwargs, + ) diff --git a/dashboards/upload.py b/dashboards/upload.py index 5b1dc9973349d4ff9308452a2d131f44509641a8..ccb9fd301fd27715e6136a70fe50e54b0dc5b73c 100644 --- a/dashboards/upload.py +++ b/dashboards/upload.py @@ -64,4 +64,4 @@ def load_config_from_env(env_path: str = ".env") -> Tuple[str, str]: def upload_dashboard(dashboard: Dashboard, folder: int) -> None: grafana_server, grafana_api_key = load_config_from_env() dashboard_json = get_dashboard_json(dashboard, overwrite=True, folder=folder) - upload_to_grafana(dashboard_json, grafana_server, grafana_api_key) + upload_to_grafana(dashboard_json, grafana_server, grafana_api_key, verify=False) diff --git a/dashboards/variables.py b/dashboards/variables.py new file mode 100644 index 0000000000000000000000000000000000000000..8a791ac62bbb3273dc5246957740182f3ab6792f --- /dev/null +++ b/dashboards/variables.py @@ -0,0 +1,38 @@ +from grafanalib.core import Template +from collections import namedtuple +from dashboards.influx_queries import show_tag_values + +Filter = namedtuple("Filter", ("name", "multi", "default_value"), defaults=('', True, '')) + + +def get_measurement_filter(name, data_source, + *, filter_pattern: str = None, + **kwargs) -> Template: + query = "SHOW MEASUREMENTS" + if filter_pattern: + query += f" WITH MEASUREMENT =~ /{filter_pattern}/" + return get_dashboard_variable_query(name, query, data_source, **kwargs) + + +def get_dashboard_variable(filter: Filter, measurment_name: str, data_source: str): + query = show_tag_values(measurment_name, filter.name) + kwargs = {} + if filter.multi: + kwargs.update({'includeAll': True, 'multi': True, }) + if filter.default_value: + kwargs.update({'default': filter.default_value}) + return get_dashboard_variable_query(filter.name, query, data_source, **kwargs) + + +def get_dashboard_variable_query(name: str, template_query: str, + dataSource: str, *, + includeAll=True, + multi=True, + **kwargs) -> Template: + return Template(name, + template_query, + dataSource=dataSource, + type="query", + includeAll=includeAll, + multi=multi, + **kwargs) diff --git a/tests/test_dashboard_creation.py b/tests/test_dashboard_creation.py index fd75b2e4b51df21d8ebfd2dd1637e82250b941bd..14ba2e6856fbdf8c30eb0ec18226fc01c9c703cb 100644 --- a/tests/test_dashboard_creation.py +++ b/tests/test_dashboard_creation.py @@ -1,11 +1,12 @@ from grafanalib.core import (Dashboard, Repeat, RowPanel, Templating, Time, TimeSeries) +from dashboards.variables import get_dashboard_variable_query from dashboards.dashboard_base import (get_commit_annotation, - get_dashboard_variable_query, get_influx_target) from dashboards.dashboard_list import dashboard_uniformGridGPU from dashboards.dashboard_fe2ti import dashboard_fe2ti +from dashboards.dashboard_pystencils import dashboard_pystencils_cpu from dashboards.influx_queries import Query, show_tag_values dataSource = 'InfluxDB-1' @@ -82,5 +83,9 @@ def test_build_dashboard(): assert dashboard_uniformGridGPU() == dashboard -def Test_dashboard_fe2ti(): +def test_dashboard_fe2ti(): dashboard_fe2ti() + + +def test_dashboard_pystencils_cpu(): + dashboard_pystencils_cpu()