Module: check_mk
Branch: master
Commit: 0afeed9eb46a2e310454e9dc554eb8b35d6881e0
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=0afeed9eb46a2e…
Author: Andreas Boesl <ab(a)mathias-kettner.de>
Date: Mon May 7 14:10:35 2018 +0200
5804 FIX BI aggregations: increased render performance
The livestatus data required for rendering the BI tree is no longer fetched consecutive from one site after another.
The new mechanism queries all sites in parallel, hereby increasing the livestatus query performance.
Change-Id: I022516125d28d58da93c7d47648c2e8d5db67fa3
---
.werks/5804 | 12 ++++++++++++
web/htdocs/bi.py | 50 ++++++++++++++++++++++++--------------------------
2 files changed, 36 insertions(+), 26 deletions(-)
diff --git a/.werks/5804 b/.werks/5804
new file mode 100644
index 0000000..a2a51cd
--- /dev/null
+++ b/.werks/5804
@@ -0,0 +1,12 @@
+Title: BI aggregations: increased render performance
+Level: 1
+Component: bi
+Class: fix
+Compatible: compat
+Edition: cre
+State: unknown
+Version: 1.6.0i1
+Date: 1525694777
+
+The livestatus data required for rendering the BI tree is no longer fetched consecutive from one site after another.
+The new mechanism queries all sites in parallel, hereby increasing the livestatus query performance.
diff --git a/web/htdocs/bi.py b/web/htdocs/bi.py
index ca5cfb8..9c93275 100644
--- a/web/htdocs/bi.py
+++ b/web/htdocs/bi.py
@@ -2483,33 +2483,31 @@ def execute_rule_node(node, status_info, aggregation_options):
# a known lists of lists (list of site/host pairs)
def get_status_info(required_hosts):
# Query each site only for hosts that that site provides
- site_hosts = {}
+ req_hosts = set()
+ req_sites = set()
+
for site, host in required_hosts:
- hosts = site_hosts.get(site)
- if hosts == None:
- site_hosts[site] = [host]
- else:
- hosts.append(host)
-
- tuples = []
- for site, hosts in site_hosts.iteritems():
- filter = ""
- for host in hosts:
- filter += "Filter: name = %s\n" % host
- if len(hosts) > 1:
- filter += "Or: %d\n" % len(hosts)
- sites.live().set_auth_domain('bi')
- sites.live().set_only_sites([site])
- data = sites.live().query(
- "GET hosts\n"
- "Columns: name state hard_state plugin_output scheduled_downtime_depth "
- "acknowledged in_service_period services_with_fullstate\n"
- + filter)
- sites.live().set_auth_domain('read')
- sites.live().set_only_sites(None)
- tuples += [((site, e[0]), e[1:]) for e in data]
-
- return dict(tuples)
+ req_hosts.add(host)
+ req_sites.add(site)
+
+ host_filter = ""
+ for host in req_hosts:
+ host_filter += "Filter: name = %s\n" % host
+ if len(req_hosts) > 1:
+ host_filter += "Or: %d\n" % len(req_hosts)
+ sites.live().set_auth_domain('bi')
+ sites.live().set_only_sites(list(req_sites))
+ sites.live().set_prepend_site(True)
+ data = sites.live().query(
+ "GET hosts\n"
+ "Columns: name state hard_state plugin_output scheduled_downtime_depth "
+ "acknowledged in_service_period services_with_fullstate\n"
+ + host_filter)
+ sites.live().set_auth_domain('read')
+ sites.live().set_only_sites(None)
+ sites.live().set_prepend_site(False)
+ return dict([((e[0], e[1]), e[2:]) for e in data])
+
# This variant of the function is configured not with a list of
# hosts but with a livestatus filter header and a list of columns
Module: check_mk
Branch: master
Commit: 23f60f6d2a660d73bf1f2cc12343ef2f5d17ccf0
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=23f60f6d2a660d…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Wed May 2 07:45:22 2018 +0200
Fix missing display hints for non list "*" placeholder
* Add display hints for ".level1.*.level3." paths
Display hints for paths like "software.applications.docker.container.networks.*."
were not working at all. Added a simple implementation that was one of such "*"
placeholders in a path.
* "%s" placeholders have been added to display hint titles
Just like %d for *-matching on lists, the %s is now working for
path part matching.
* Cleaned up previous replaement of ":*." list item syntax a bit
* Moved display hint matching to a central place.
Change-Id: Idb3352c0461222e8cf292c1347977cfa65d14dad
---
web/plugins/views/inventory.py | 130 ++++++++++++++++++++++++++++++-----------
1 file changed, 95 insertions(+), 35 deletions(-)
diff --git a/web/plugins/views/inventory.py b/web/plugins/views/inventory.py
index b7dd386..3d00f97 100644
--- a/web/plugins/views/inventory.py
+++ b/web/plugins/views/inventory.py
@@ -79,7 +79,7 @@ def _paint_host_inventory_tree_value(struct_tree, parsed_path, tree_renderer, in
tree_renderer.show_numeration(child, path=invpath)
else:
tree_renderer.show_attribute(child.get_child_data().get(attributes_key),
- inv_display_hint(invpath))
+ _inv_display_hint(invpath))
code = html.drain()
return "invtree", code
@@ -407,13 +407,71 @@ def inv_paint_timestamp_as_age_days(timestamp):
# '----------------------------------------------------------------------'
-# Convert .foo.bar:18.test to .foo.bar:*.test
-def inv_display_hint(invpath):
- r = regex(".[0-9]+")
- invpath = r.sub(":*", invpath)
- hint = inventory_displayhints.get(invpath, {})
+def _inv_display_hint(invpath):
+ """Generic access function to display hints
+ Don't use other methods to access the hints!"""
+ hint_id = _find_display_hint_id(invpath)
+ hint = inventory_displayhints.get(hint_id, {})
+ return _convert_display_hint(hint)
- # Convert paint type to paint function, for the convenciance of the called
+
+def _find_display_hint_id(invpath):
+ """Looks up the display hint for the given inventory path.
+
+ It returns either the ID of the display hint matching the given invpath
+ or None in case no entry was found.
+
+ In case no exact match is possible try to match display hints that use
+ some kind of *-syntax. There are two types of cases here:
+
+ :* -> Entries in lists (* resolves to list index numbers)
+ .* -> Path entries (* resolves to a path element)
+
+ The current logic has some limitations related to the ways stars can
+ be used.
+ """
+ invpath = invpath.rstrip(".:")
+
+ # Convert index of lists to *-syntax
+ # e.g. ".foo.bar:18.test" to ".foo.bar:*.test"
+ r = regex("([^\.]):[0-9]+")
+ invpath = r.sub("\\1:*", invpath)
+
+ candidates = [
+ invpath,
+ ]
+
+ # Produce a list of invpath candidates with a "*" going from back to front.
+ #
+ # This algorithm only allows one ".*" in a invpath. It finds the match with
+ # the longest path prefix before the ".*".
+ #
+ # TODO: Implement a generic mechanism that allows as many stars as possible
+ invpath_parts = invpath.split(".")
+ star_index = len(invpath_parts) - 1
+ while star_index >= 0:
+ parts = invpath_parts[:star_index] + ["*"] + invpath_parts[star_index+1:]
+ invpath_with_star = "%s" % ".".join(parts)
+ candidates.append(invpath_with_star)
+ star_index -= 1
+
+ for candidate in candidates:
+ # TODO: Better cleanup trailing ":" and "." from display hints at all. They are useless
+ # for finding the right entry.
+ if candidate in inventory_displayhints:
+ return candidate
+
+ if candidate+"." in inventory_displayhints:
+ return candidate+"."
+
+ if candidate+":" in inventory_displayhints:
+ return candidate+":"
+
+ return None
+
+
+def _convert_display_hint(hint):
+ """Convert paint type to paint function, for the convenciance of the called"""
if "paint" in hint:
paint_function_name = "inv_paint_" + hint["paint"]
hint["paint_function"] = globals()[paint_function_name]
@@ -422,7 +480,7 @@ def inv_display_hint(invpath):
def inv_titleinfo(invpath, node):
- hint = inv_display_hint(invpath)
+ hint = _inv_display_hint(invpath)
icon = hint.get("icon")
if "title" in hint:
title = hint["title"]
@@ -1486,32 +1544,15 @@ class NodeRenderer(object):
for _, node in container.get_edge_nodes():
node_abs_path = node.get_absolute_path()
- #FIXME clean this up
- _titleinfo = []
- for part in node_abs_path:
- try:
- part = int(part)
- except ValueError:
- pass
- else:
- last = _titleinfo.pop(-1)
- part = "%s:%d" % (last, part)
- finally:
- _titleinfo.append(part)
-
- titleinfo_path = ".%s." % ".".join(_titleinfo)
- icon, title = inv_titleinfo(titleinfo_path, node)
+ raw_invpath = self._get_raw_path(".".join(map(str, node_abs_path)))
+ invpath = ".%s." % raw_invpath
- if "%d" in title: # Replace with list index
- list_index = int(titleinfo_path.split(":")[-1].rstrip(".")) + 1
- title = title % list_index
+ icon, title = inv_titleinfo(invpath, node)
- # Some displayhints may end with ":", eg. ".software.packages:"
- if "%s:" % titleinfo_path[:-1] in inventory_displayhints:
- icon, title = inv_titleinfo("%s:" % titleinfo_path[:-1], node)
+ # Replace placeholders in title with the real values for this path
+ if "%d" in title or "%s" in title:
+ title = self._replace_placeholders(title, invpath)
- raw_invpath = self._get_raw_path(".".join(map(str, node_abs_path)))
- invpath = ".%s." % raw_invpath
header = self._get_header(title, ".".join(map(str, node_abs_path)), "#666")
fetch_url = html.makeuri_contextless([("host", self._hostname),
("path", invpath),
@@ -1526,13 +1567,32 @@ class NodeRenderer(object):
child.show(self, path=raw_invpath)
html.end_foldable_container()
+
+ def _replace_placeholders(self, raw_title, invpath):
+ hint_id = _find_display_hint_id(invpath)
+ invpath_parts = invpath.strip(".").split(".")
+
+ # Use the position of the stars in the path to build a list of texts
+ # that should be used for replacing the tile placeholders
+ replace_vars = []
+ hint_parts = hint_id.strip(".").split(".")
+ for index, hint_part in enumerate(hint_parts):
+ if hint_part == "*":
+ replace_vars.append(invpath_parts[index])
+
+ # Now replace the variables in the title. Handle the case where we have
+ # more stars than macros in the title.
+ num_macros = raw_title.count("%d") + raw_title.count("%s")
+ return raw_title % tuple(replace_vars[:num_macros])
+
+
# ---numeration-----------------------------------------------------------
def show_numeration(self, numeration, path=None):
#FIXME these kind of paths are required for hints.
# Clean this up one day.
invpath = ".%s:" % self._get_raw_path(path)
- hint = inv_display_hint(invpath)
+ hint = _inv_display_hint(invpath)
keyorder = hint.get("keyorder", []) # well known keys
data = numeration.get_child_data()
@@ -1541,7 +1601,7 @@ class NodeRenderer(object):
for key in keyorder:
sub_invpath = "%s0.%s" % (invpath, key)
icon, title = inv_titleinfo(sub_invpath, None)
- sub_hint = inv_display_hint(sub_invpath)
+ sub_hint = _inv_display_hint(sub_invpath)
short_title = sub_hint.get("short", title)
titles.append((short_title, key))
@@ -1588,7 +1648,7 @@ class NodeRenderer(object):
for title, key in titles:
value = entry.get(key)
sub_invpath = "%s%d.%s" % (invpath, index, key)
- hint = inv_display_hint(sub_invpath)
+ hint = _inv_display_hint(sub_invpath)
if "paint_function" in hint:
#FIXME At the moment we need it to get tdclass
# Clean this up one day.
@@ -1616,7 +1676,7 @@ class NodeRenderer(object):
for key, value in attributes.get_child_data().iteritems():
sub_invpath = "%s.%s" % (invpath, key)
icon, title = inv_titleinfo(sub_invpath, key)
- hint = inv_display_hint(sub_invpath)
+ hint = _inv_display_hint(sub_invpath)
html.open_tr()
html.open_th(title=sub_invpath)