Module: check_mk
Branch: master
Commit: ab6113372ffa64ad4968374353db52c94c3d7d9d
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=ab6113372ffa64…
Author: Óscar Nájera <on(a)mathias-kettner.de>
Date: Fri Apr 5 15:46:08 2019 +0200
RRD data query has configurable limit on data length
RRDtool has a configurable default limit of 400 points per
query. Livestatus did not configure that value until now, but we may need
to do queries over large periods of time and we don't want down sampling.
This change offers backwards compatibility, leaving this parameter as
optional.
The off by one in the query to result arises on the new way result bounds
are given. Now the result bounds contain the query bounds instead of
providing a shift of bounds between query and response.
CMK-1935
Change-Id: I2bcd22396887fe04f7617cb6e1248225662482c1
---
cmk/utils/prediction.py | 8 +++-----
tests/integration/cmk_base/test_services_prediction.py | 10 ++++++++++
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/cmk/utils/prediction.py b/cmk/utils/prediction.py
index 8aab80d..c1236af 100644
--- a/cmk/utils/prediction.py
+++ b/cmk/utils/prediction.py
@@ -111,15 +111,13 @@ class TimeSeries(object):
return self.start == other.start and self.end == other.end and self.step == other.step and self.values == other.values
-def get_rrd_data(hostname, service_description, varname, cf, fromtime, untiltime):
+def get_rrd_data(hostname, service_description, varname, cf, fromtime, untiltime, max_entries=400):
"""Fetch RRD historic metrics data of a specific service, within the specified time range
returns a TimeSeries object holding interval and data information
Query to livestatus always returns if database is found, thus:
- Values can be None when there is no data for a given timestamp
- - Livestatus/rrdtool returns a maximum of 400 data-points per query,
- even if better resolution is available within the time range.
- Reply from livestatus/rrdtool is always enough to describe the
queried interval. That means, the returned bounds are always outside
the queried interval.
@@ -140,11 +138,11 @@ def get_rrd_data(hostname, service_description, varname, cf, fromtime, untiltime
rpn = "%s.%s" % (varname, cf.lower()) # "MAX" -> "max"
lql = "GET services\n" \
- "Columns: rrddata:m1:%s:%s:%s:%s\n" \
+ "Columns: rrddata:m1:%s:%s:%s:%s:%s\n" \
"OutputFormat: python\n" \
"Filter: host_name = %s\n" \
"Filter: description = %s\n" % tuple(map(livestatus.lqencode,
- map(str, (rpn, fromtime, untiltime, step,
+ map(str, (rpn, fromtime, untiltime, step, max_entries,
hostname, service_description))))
try:
diff --git a/tests/integration/cmk_base/test_services_prediction.py b/tests/integration/cmk_base/test_services_prediction.py
index fa6e331..a5a279b 100644
--- a/tests/integration/cmk_base/test_services_prediction.py
+++ b/tests/integration/cmk_base/test_services_prediction.py
@@ -88,6 +88,16 @@ def test_get_rrd_data(cfg_setup, utcdate, timezone, period, result):
assert (timeseries.step, len(timeseries.values)) == result
+(a)pytest.mark.parametrize("max_entries, result", [(400, (180, 401)), (20, (3600, 21)),
+ (50, (1800, 41)), (1000, (120, 600)),
+ (1200, (60, 1200))])
+def test_get_rrd_data_point_max(cfg_setup, max_entries, result):
+ from_time, until_time = 1543430040, 1543502040
+ timeseries = cmk.utils.prediction.get_rrd_data('test-prediction', 'CPU load', 'load15', 'MAX',
+ from_time, until_time, max_entries)
+ assert (timeseries.step, len(timeseries.values)) == result
+
+
@pytest.mark.parametrize('utcdate, timezone, params, reference', [
("2018-09-01 07:00", "Europe/Berlin", {
'period': 'wday',
Module: check_mk
Branch: master
Commit: 62917296f47162ac5391c61e00d9b4db2f4db147
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=62917296f47162…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Sat Apr 6 23:35:58 2019 +0200
Reduce visual clutter in WATO folder host list
In a lot of situations several of the standard columns are not filled at
all, e.g. the IPv6 address column is useless in non IPv6 networks.
Instead of showing the empty columns they are now completely hidden from
the user.
Change-Id: I7b95becace7062340178b83019cdfbf943fbb95d
---
cmk/gui/table.py | 29 ++++++++++++++++++++++++-----
cmk/gui/wato/pages/folders.py | 5 +++--
2 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/cmk/gui/table.py b/cmk/gui/table.py
index 66846b6..4dbb95a 100644
--- a/cmk/gui/table.py
+++ b/cmk/gui/table.py
@@ -91,6 +91,7 @@ class Table(object):
self.options = {
"collect_headers": False, # also: True, "finished"
"omit_if_empty": kwargs.get("omit_if_empty", False),
+ "omit_empty_columns": kwargs.get("omit_empty_columns", False),
"omit_headers": kwargs.get("omit_headers", False),
"searchable": kwargs.get("searchable", True),
"sortable": kwargs.get("sortable", True),
@@ -277,12 +278,18 @@ class Table(object):
table_id = self.id
num_cols = len(self.headers)
+ empty_columns = self._get_empty_columns(rows, num_cols)
+ num_cols -= len([v for v in empty_columns if v])
html.open_table(class_=["data", "oddeven", self.css])
# If we have no group headers then paint the headers now
if self.rows and self.rows[0][2] != "header":
- self._render_headers(actions_enabled, actions_visible)
+ self._render_headers(
+ actions_enabled,
+ actions_visible,
+ empty_columns,
+ )
if actions_enabled and actions_visible:
html.open_tr(class_=["data", "even0", "actions"])
@@ -311,7 +318,6 @@ class Table(object):
html.close_tr()
for nr, (row_spec, css, state, _fixed, attrs) in enumerate(rows):
-
if not css and "class_" in attrs:
css = attrs.pop("class_")
if not css and "class" in attrs:
@@ -329,14 +335,17 @@ class Table(object):
html.close_td()
html.close_tr()
- self._render_headers(actions_enabled, actions_visible)
+ self._render_headers(actions_enabled, actions_visible, empty_columns)
continue
oddeven_name = "even" if (nr - 1) % 2 == 0 else "odd"
html.open_tr(
class_=["data", "%s%d" % (oddeven_name, state), css if css else None], **attrs)
- for cell_content, css_classes, colspan in row_spec:
+ for col_index, (cell_content, css_classes, colspan) in enumerate(row_spec):
+ if self.options["omit_empty_columns"] and empty_columns[col_index]:
+ continue
+
html.open_td(
class_=css_classes if css_classes else None,
colspan=colspan if colspan else None)
@@ -351,6 +360,13 @@ class Table(object):
html.close_table()
+ def _get_empty_columns(self, rows, num_cols):
+ empty_columns = [True] * num_cols
+ for row_spec, _css, _state, _fixed, _attrs in rows:
+ for col_index, (cell_content, _css_classes, _colspan) in enumerate(row_spec):
+ empty_columns[col_index] &= not cell_content
+ return empty_columns
+
def _write_csv(self, csv_separator):
rows = self.rows
@@ -377,7 +393,7 @@ class Table(object):
]))
html.write("\n")
- def _render_headers(self, actions_enabled, actions_visible):
+ def _render_headers(self, actions_enabled, actions_visible, empty_columns):
if self.options["omit_headers"]:
return
@@ -386,6 +402,9 @@ class Table(object):
html.open_tr()
first_col = True
for nr, (header, css, help_txt, sortable) in enumerate(self.headers):
+ if self.options["omit_empty_columns"] and empty_columns[nr]:
+ continue
+
text = header
if help_txt:
diff --git a/cmk/gui/wato/pages/folders.py b/cmk/gui/wato/pages/folders.py
index 8e58c1a..d095e39 100644
--- a/cmk/gui/wato/pages/folders.py
+++ b/cmk/gui/wato/pages/folders.py
@@ -406,7 +406,8 @@ class ModeFolder(WatoMode):
# Show table of hosts in this folder
html.begin_form("hosts", method="POST")
- with table_element("hosts", title=_("Hosts"), searchable=False) as table:
+ with table_element(
+ "hosts", title=_("Hosts"), searchable=False, omit_empty_columns=True) as table:
# Remember if that host has a target folder (i.e. was imported with
# a folder information but not yet moved to that folder). If at least
@@ -554,7 +555,7 @@ class ModeFolder(WatoMode):
icon = "autherr"
title = html.strip_tags(reason)
- table.cell(_('Auth'), html.render_icon(icon, title), sortable=False)
+ table.cell(_('Auth'), html.render_icon(icon, title), css="buttons", sortable=False)
# Permissions and Contact groups - through complete recursion and inhertance
permitted_groups, host_contact_groups, _use_for_services = host.groups()