Module: check_mk
Branch: master
Commit: 53dcc0828bf2caa1062881bd47982fb5f8c259d3
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=53dcc0828bf2ca…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Fri Mar 3 13:10:42 2017 +0100
4423 FIX Availability of service filtered views was showing services multiple times
When a service is in multiple service groups and a view is filtered by a single service
group and the availability view is grouped by service groups, a single service was
shown multiple times. Once for each service group membership.
The service is now shown only for the filtered service group memberships.
Change-Id: I9ad374abe29d6f7cf1dca24201281ddb99be3e87
---
.werks/4423 | 15 +++++++++++
web/htdocs/availability.py | 56 ++++++++++++++++++++++++++++++++++++++-
web/htdocs/views.py | 6 ++++-
web/plugins/views/availability.py | 4 +--
4 files changed, 77 insertions(+), 4 deletions(-)
diff --git a/.werks/4423 b/.werks/4423
new file mode 100644
index 0000000..22d2bf6
--- /dev/null
+++ b/.werks/4423
@@ -0,0 +1,15 @@
+Title: Availability of service filtered views was showing services multiple times
+Level: 1
+Component: multisite
+Class: fix
+Compatible: compat
+Edition: cre
+State: unknown
+Version: 1.5.0i1
+Date: 1488542925
+
+When a service is in multiple service groups and a view is filtered by a single service
+group and the availability view is grouped by service groups, a single service was
+shown multiple times. Once for each service group membership.
+
+The service is now shown only for the filtered service group memberships.
diff --git a/web/htdocs/availability.py b/web/htdocs/availability.py
index 6b4dba0..54dbbdc 100644
--- a/web/htdocs/availability.py
+++ b/web/htdocs/availability.py
@@ -640,7 +640,7 @@ def get_outage_statistic_options(avoptions):
# of spans. Each span is a dictionary that describes one span of time where
# a specific host or service has one specific state.
# what is either "host" or "service" or "bi".
-def get_availability_rawdata(what, filterheaders, only_sites, av_object, include_output,
avoptions):
+def get_availability_rawdata(what, context, filterheaders, only_sites, av_object,
include_output, avoptions):
if what == "bi":
return get_bi_availability_rawdata(filterheaders, only_sites, av_object,
include_output, avoptions)
@@ -692,6 +692,10 @@ def get_availability_rawdata(what, filterheaders, only_sites,
av_object, include
columns = ["site"] + columns
spans = [ dict(zip(columns, span)) for span in data ]
+ # When a group filter is set, only care about these groups in the group fields
+ if avoptions["grouping"] not in [ None, "host" ]:
+ filter_groups_of_entries(context, avoptions, spans)
+
# Now we find out if the log row limit was exceeded or
# if the log's length is the limit by accident.
# If this limit was exceeded then we cut off the last element
@@ -702,6 +706,56 @@ def get_availability_rawdata(what, filterheaders, only_sites,
av_object, include
return spans_by_object(spans, logrow_limit_reached_entry)
+
+def filter_groups_of_entries(context, avoptions, spans):
+ group_by = avoptions["grouping"]
+
+ only_groups = set()
+ # TODO: This is a dirty hack. The logic of the filters needs to be moved to the
filters.
+ # They need to be able to filter the list of all groups.
+ # TODO: Negated filters are not handled here. :(
+ if group_by == "service_groups":
+ # Extract from context:
+ # 'servicegroups': {'servicegroups': 'cpu|disk',
'neg_servicegroups': 'off'},
+ # 'optservicegroup': {'optservice_group': '',
'neg_optservice_group': 'off'},
+ negated = context.get("servicegroups",
{}).get("neg_servicegroups") == "on"
+ if negated:
+ return
+
+ only_groups.update([ e for e in context.get("servicegroups",
{}).get("servicegroups", "").split("|") if e ])
+
+ negated = context.get("optservicegroup",
{}).get("neg_optservice_group") == "on"
+ if negated:
+ return
+
+ group_name = context.get("optservicegroup",
{}).get("optservice_group")
+ if group_name and not negated:
+ only_groups.add(group_name)
+
+ elif group_by == "host_groups":
+ html.log(repr(context))
+ negated = context.get("hostgroups", {}).get("neg_hostgroups")
== "on"
+ if negated:
+ return
+
+ only_groups.update([ e for e in context.get("hostgroups",
{}).get("hostgroups", "").split("|") if e ])
+
+ negated = context.get("opthostgroup",
{}).get("neg_opthost_group") == "on"
+ if negated:
+ return
+
+ group_name = context.get("opthostgroup",
{}).get("opthost_group")
+ if group_name and not negated:
+ only_groups.add(group_name)
+
+ else:
+ raise NotImplementedError()
+
+ for span in spans:
+ filtered_groups = list(set(span[group_by]).intersection(only_groups))
+ span[group_by] = filtered_groups
+
+
# Sort the raw spans into a tree of dicts, so that we
# have easy access to the timeline of each object
def spans_by_object(spans, logrow_limit_reached_entry):
diff --git a/web/htdocs/views.py b/web/htdocs/views.py
index 7d1de2d..384f3a7 100644
--- a/web/htdocs/views.py
+++ b/web/htdocs/views.py
@@ -1544,7 +1544,11 @@ def show_view(view, show_heading = False, show_buttons = True,
# hosts and service table, but "statehist". This is *not* true for BI
availability, though (see later)
if html.var("mode") == "availability" and (
"aggr" not in datasource["infos"] or
html.var("timeline_aggr")):
- return render_availability_page(view, datasource, filterheaders, only_sites,
limit)
+
+ context = visuals.get_context_from_uri_vars(datasource['infos'])
+ context.update(visuals.get_singlecontext_html_vars(view))
+
+ return render_availability_page(view, datasource, context, filterheaders,
only_sites, limit)
query = filterheaders + view.get("add_headers", "")
diff --git a/web/plugins/views/availability.py b/web/plugins/views/availability.py
index 4b7e974..4447439 100644
--- a/web/plugins/views/availability.py
+++ b/web/plugins/views/availability.py
@@ -138,7 +138,7 @@ def render_availability_options(what):
# Render the page showing availability table or timelines. It
# is (currently) called by views.py, when showing a view but
# availability mode is activated.
-def render_availability_page(view, datasource, filterheaders, only_sites, limit):
+def render_availability_page(view, datasource, context, filterheaders, only_sites,
limit):
if handle_edit_annotations():
return
@@ -191,7 +191,7 @@ def render_availability_page(view, datasource, filterheaders,
only_sites, limit)
# Now compute all data, we need this also for CSV export
if not html.has_user_errors():
av_rawdata, has_reached_logrow_limit = \
- availability.get_availability_rawdata(what, filterheaders, only_sites,
+ availability.get_availability_rawdata(what, context, filterheaders,
only_sites,
av_object, av_mode ==
"timeline", avoptions)
av_data = availability.compute_availability(what, av_rawdata, avoptions)