def render_bi_availability(title, aggr_rows): # type: (Text, Rows) -> None config.user.need_permission("general.see_availability") av_mode = html.request.get_ascii_input_mandatory("av_mode", "availability") avoptions = get_availability_options_from_url("bi") if av_mode == "timeline": title = _("Timeline of") + " " + title else: title = _("Availability of") + " " + title if html.output_format != "csv_export": html.body_start(title) html.top_heading(title) html.begin_context_buttons() html.toggle_button("avoptions", False, "painteroptions", _("Configure details of the report")) html.context_button(_("Status View"), html.makeuri([("mode", "status")]), "status") if config.reporting_available() and config.user.may( "general.reporting"): html.context_button(_("Export as PDF"), html.makeuri([], filename="report_instant.py"), "report") if config.user.may("general.csv_export"): html.context_button( _("Export as CSV"), html.makeuri([("output_format", "csv_export")]), "download_csv") if av_mode == "timeline": html.context_button(_("Availability"), html.makeuri([("av_mode", "availability")]), "availability") elif len(aggr_rows) == 1: aggr_name = aggr_rows[0]["aggr_name"] aggr_group = aggr_rows[0]["aggr_group"] timeline_url = html.makeuri([("av_mode", "timeline"), ("av_aggr_name", aggr_name), ("av_aggr_group", aggr_group)]) html.context_button(_("Timeline"), timeline_url, "timeline") html.end_context_buttons() avoptions = render_availability_options("bi") if not html.has_user_errors(): logrow_limit = avoptions["logrow_limit"] if logrow_limit == 0: livestatus_limit = None else: livestatus_limit = (len(aggr_rows) * logrow_limit) spans = [] # type: List[AVSpan] # iterate all aggregation rows timewarpcode = HTML() timewarp = html.request.get_integer_input("timewarp") has_reached_logrow_limit = False timeline_containers, fetched_rows = availability.get_timeline_containers( aggr_rows, avoptions, timewarp, livestatus_limit + 1 if livestatus_limit is not None else None) if livestatus_limit and fetched_rows > livestatus_limit: has_reached_logrow_limit = True for timeline_container in timeline_containers: tree = timeline_container.aggr_tree these_spans = timeline_container.timeline timewarp_tree_state = timeline_container.timewarp_state spans += these_spans # render selected time warp for the corresponding aggregation row (should be matched by only one) if timewarp and timewarp_tree_state: state, assumed_state, node, _subtrees = timewarp_tree_state eff_state = state if assumed_state is not None: eff_state = assumed_state row = { "aggr_tree": tree, "aggr_treestate": timewarp_tree_state, "aggr_state": state, # state disregarding assumptions "aggr_assumed_state": assumed_state, # is None, if there are no assumptions "aggr_effective_state": eff_state, # is assumed_state, if there are assumptions, else real state "aggr_name": node["title"], "aggr_output": eff_state["output"], "aggr_hosts": node["reqhosts"], "aggr_function": node["func"], "aggr_group": html.request.var("aggr_group"), } renderer = bi.FoldableTreeRendererTree( row, omit_root=False, expansion_level=config.user.bi_expansion_level, only_problems=False, lazy=False) tdclass, htmlcode = renderer.css_class(), renderer.render() with html.plugged(): # TODO: SOMETHING IS WRONG IN HERE (used to be the same situation in original code!) # FIXME: WHAT is wrong in here?? html.open_h3() # render icons for back and forth button_back_shown = False button_forth_shown = False if int(these_spans[0]["from"]) == timewarp: html.disabled_icon_button("back_off") button_back_shown = True previous_span = None for span in these_spans: if not button_back_shown and int( span["from"] ) == timewarp and previous_span is not None: html.icon_button( html.makeuri([("timewarp", str(int(previous_span["from"]))) ]), _("Jump one phase back"), "back") button_back_shown = True # Multiple followup spans can have the same "from" time # We only show one forth-arrow with an actual time difference elif not button_forth_shown and previous_span and int( previous_span["from"]) == timewarp and int( span["from"]) != timewarp: html.icon_button( html.makeuri([("timewarp", str(int(span["from"])))]), _("Jump one phase forth"), "forth") button_forth_shown = True previous_span = span if not button_forth_shown: html.disabled_icon_button("forth_off") html.write_text(" ") html.icon_button(html.makeuri([("timewarp", "")]), _("Close Timewarp"), "closetimewarp") html.write_text("%s %s" % (_("Timewarp to "), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timewarp)))) html.close_h3() html.open_table(class_=["data", "table", "timewarp"]) html.open_tr(class_=["data", "odd0"]) html.open_td(class_=tdclass) html.write_html(htmlcode) html.close_td() html.close_tr() html.close_table() timewarpcode += html.drain() # Note: 'spans_by_object' returns two arguments which are used by # all availability views but not by BI. There we have to take # only complete aggregations av_rawdata = availability.spans_by_object(spans, None)[0] av_data = availability.compute_availability("bi", av_rawdata, avoptions) # If we abolish the limit we have to fetch the data again # with changed logrow_limit = 0, which means no limit if has_reached_logrow_limit: text = _( "Your query matched more than %d log entries. " "<b>Note:</b> The shown data does not necessarily reflect the " "matched entries and the result might be incomplete. " ) % avoptions["logrow_limit"] text += html.render_a(_('Repeat query without limit.'), html.makeuri([("_unset_logrow_limit", "1")])) html.show_warning(text) if html.output_format == "csv_export" and config.user.may( "general.csv_export"): _output_csv("bi", av_mode, av_data, avoptions) return html.write(timewarpcode) do_render_availability("bi", av_rawdata, av_data, av_mode, None, avoptions) html.bottom_footer() html.body_end()
def edit_annotation(): site_id = html.request.var("anno_site") or "" hostname = html.request.get_str_input_mandatory("anno_host") service = html.request.var("anno_service") or None fromtime = html.request.get_float_input_mandatory("anno_from") untiltime = html.request.get_float_input_mandatory("anno_until") site_host_svc = (site_id, hostname, service) # Find existing annotation with this specification annotations = availability.load_annotations() annotation = availability.find_annotation(annotations, site_host_svc, fromtime, untiltime) if annotation: value = annotation.copy() else: value = { "from": fromtime, "until": untiltime, "text": "", } value["host"] = hostname value["service"] = service value["site"] = site_id if html.check_transaction(): try: vs = _vs_annotation() value = vs.from_html_vars("_editanno") vs.validate_value(value, "_editanno") site_host_svc = (value["site"], value["host"], value["service"]) del value["site"] del value["host"] value["date"] = time.time() value["author"] = config.user.id availability.update_annotations(site_host_svc, value, replace_existing=annotation) html.request.del_var("filled_in") return False except MKUserError as e: html.user_error(e) title = _("Edit annotation of ") + hostname if service: title += "/" + service html.body_start(title) html.top_heading(title) html.begin_context_buttons() html.context_button(_("Abort"), html.makeuri([("anno_host", "")]), "abort") html.end_context_buttons() html.begin_form("editanno", method="GET") _vs_annotation().render_input_as_form("_editanno", value) html.button("save", _("Save")) html.hidden_fields() html.end_form() html.bottom_footer() html.body_end() return True
def render_availability_page(view, filterheaders): # type: (View, FilterHeaders) -> None config.user.need_permission("general.see_availability") if handle_edit_annotations(): return # We make reports about hosts, services or BI aggregates if "service" in view.datasource.infos: what = "service" elif "aggr_name" in view.datasource.infos: what = "bi" else: what = "host" avoptions = get_availability_options_from_url(what) time_range, range_title = avoptions["range"] # We have two display modes: # - Show availability table (stats) "table" # - Show timeline "timeline" # --> controlled by URL variable "av_mode" av_mode = html.request.get_ascii_input_mandatory("av_mode", "table") if av_mode == "timeline": title = _("Availability Timeline") else: title = _("Availability") # This is combined with the object selection # - Show all objects # - Show one specific object # --> controlled by URL variables "av_site", "av_host" and "av_service" # --> controlled by "av_aggr" in case of BI aggregate title += " - " av_object = None # type: AVObjectSpec if html.request.var("av_host"): av_object = (html.request.get_str_input_mandatory("av_site"), html.request.get_str_input_mandatory("av_host"), html.request.get_unicode_input_mandatory("av_service")) title += av_object[1] if av_object[2]: title += " - " + av_object[2] elif html.request.var("av_aggr"): av_object = (None, None, html.request.get_unicode_input_mandatory("av_aggr")) title += av_object[2] else: title += view_title(view.spec) # Deletion must take place before computation, since it affects the outcome with html.plugged(): handle_delete_annotations() confirmation_html_code = html.drain() # Now compute all data, we need this also for CSV export if not html.has_user_errors(): include_long_output = av_mode == "timeline" \ and "timeline_long_output" in avoptions["labelling"] av_rawdata, has_reached_logrow_limit = availability.get_availability_rawdata( what, view.context, filterheaders, view.only_sites, av_object=av_object, include_output=av_mode == "timeline", include_long_output=include_long_output, avoptions=avoptions) av_data = availability.compute_availability(what, av_rawdata, avoptions) # Do CSV ouput if html.output_format == "csv_export" and config.user.may( "general.csv_export"): _output_csv(what, av_mode, av_data, avoptions) return title += " - " + range_title if display_options.enabled(display_options.H): html.body_start(title, force=True) if display_options.enabled(display_options.T): html.top_heading(title) html.write(confirmation_html_code) # Remove variables for editing annotations, otherwise they will make it into the uris html.request.del_vars("anno_") if html.request.var("filled_in") == "editanno": html.request.del_var("filled_in") if display_options.enabled(display_options.B): html.begin_context_buttons() html.toggle_button( "avoptions", html.has_user_errors(), "painteroptions", _("Configure details of the report"), ) html.context_button( _("Status View"), html.makeuri([("mode", "status")]), "status", ) if config.reporting_available() and config.user.may( "general.reporting"): html.context_button( _("Export as PDF"), html.makeuri([], filename="report_instant.py"), "report", ) if config.user.may("general.csv_export"): html.context_button( _("Export as CSV"), html.makeuri([("output_format", "csv_export")]), "download_csv", ) if av_mode == "timeline" or av_object: html.context_button( _("Availability"), html.makeuri([("av_mode", "availability"), ("av_host", ""), ("av_aggr", "")]), "availability", ) elif not av_object: html.context_button( _("Timeline"), html.makeuri([("av_mode", "timeline")]), "timeline", ) elif av_mode == "timeline" and what != "bi": history_url = availability.history_url_of(av_object, time_range) html.context_button( _("History"), history_url, "history", ) html.end_context_buttons() # Render the avoptions again to get the HTML code, because the HTML vars have changed # above (anno_ and editanno_ has been removed, which must not be part of the form avoptions = render_availability_options(what) if not html.has_user_errors(): # If we abolish the limit we have to fetch the data again # with changed logrow_limit = 0, which means no limit if has_reached_logrow_limit: text = _( "Your query matched more than %d log entries. " "<b>Note:</b> The number of shown rows does not necessarily reflect the " "matched entries and the result might be incomplete. " ) % avoptions["logrow_limit"] text += html.render_a( _('Repeat query without limit.'), html.makeuri([("_unset_logrow_limit", "1"), ("avo_logrow_limit", 0)])) html.show_warning(text) do_render_availability(what, av_rawdata, av_data, av_mode, av_object, avoptions) if display_options.enabled(display_options.Z): html.bottom_footer() if display_options.enabled(display_options.H): html.body_end()