def render_mobile_dataset(rows, view, group_cells, cells, num_columns, show_checkboxes): if not is_mobile(request, response): html.show_error(_("This view can only be used in mobile mode.")) return painter_options = PainterOptions.get_instance() painter_options.set("ts_format", "both") for row in rows: html.open_table(class_="dataset") for cell in cells: _tdclass, content = cell.render(row) if not content: continue # Omit empty cells html.open_tr(class_="header") html.th(cell.title()) html.close_tr() html.open_tr(class_="data") cell.paint(row) html.close_tr() html.close_table() html.javascript( '$("table.dataset > tbody > tr.data > td").addClass("ui-shadow").not(".state").addClass("nonstatus");\n' '$("table.dataset > tbody > tr.data a").attr("data-ajax", "false");\n')
def werk_table_row(caption, content, css=None): html.open_tr() html.th(caption) html.open_td(class_=css) html.write(content) html.close_td() html.close_tr()
def _show_header_line(self, cells, num_columns, show_checkboxes): html.open_tr() for n in range(1, num_columns + 1): if show_checkboxes: if n == 1: render_group_checkbox_th() else: html.th("") for cell in cells: cell.paint_as_header() if n < num_columns: html.td("", class_="gap") html.close_tr()
def page(self): html.open_table(class_=["data", "headerleft"]) html.open_tr() html.th(_("Title")) html.open_td() html.b(self._manpage["header"]["title"]) html.close_td() html.close_tr() html.open_tr() html.th(_("Name of plugin")) html.open_td() html.tt(self._check_type) html.close_td() html.close_tr() html.open_tr() html.th(_("Description")) html.td(self._manpage_text(self._manpage["header"]["description"])) html.close_tr() if self._manpage["type"] == "check_mk": html.open_tr() html.th(_("Service name")) html.td( HTML(self._manpage["service_description"].replace( "%s", "☐"))) html.close_tr() check_ruleset_name = self._manpage.get("check_ruleset_name") if check_ruleset_name is not None: self._show_ruleset("checkgroup_parameters:%s" % check_ruleset_name) cluster = self._manpage["header"].get("cluster") if cluster: html.open_tr() html.th(_("Cluster behaviour")) html.td(self._manpage_text(cluster)) html.close_tr() else: self._show_ruleset("active_checks:%s" % self._check_type[6:]) html.close_table()
def _show_ruleset(self, varname): if varname not in rulespec_registry: return rulespec = rulespec_registry[varname] url = makeuri_contextless(request, [("mode", "edit_ruleset"), ("varname", varname)]) html.open_tr() html.th(_("Parameter rule set")) html.open_td() html.icon_button(url, _("Edit parameter rule set for this check type"), "check_parameters") html.a(rulespec.title, url) html.close_td() html.close_tr() html.open_tr() html.th(_("Example for Parameters")) html.open_td() vs = rulespec.valuespec vs.render_input("dummy", vs.default_value()) html.close_td() html.close_tr()
def page(self): html.open_table(class_=["data", "headerleft"]) html.open_tr() html.th(_("Title")) html.open_td() html.b(self._manpage["header"]["title"]) html.close_td() html.close_tr() html.open_tr() html.th(_("Name of plugin")) html.open_td() html.tt(self._check_type) html.close_td() html.close_tr() html.open_tr() html.th(_("Description")) html.td(self._manpage_text(self._manpage["header"]["description"])) html.close_tr() if self._manpage["type"] == "check_mk": html.open_tr() html.th(_("Service name")) html.td( HTML(self._manpage["service_description"].replace( "%s", "☐"))) html.close_tr() if self._manpage.get("group"): group = self._manpage["group"] varname = "checkgroup_parameters:" + group self._show_ruleset(varname) else: varname = "active_checks:" + self._check_type[6:] self._show_ruleset(varname) html.close_table()
def show_job_row_headers(cls): html.open_tr() for header in cls.get_headers(): html.th(header) html.close_tr()
def show_job_details(cls, job_id, job_status): """Renders the complete job details in a single table with left headers""" html.open_table(class_=["data", "headerleft", "job_details"]) # Static info for left, right in [ (_("ID"), job_id), (_("Title"), job_status.get("title", "")), (_("Started"), cmk.utils.render.date_and_time(job_status["started"])), (_("Owner"), job_status.get("user", "")), ]: html.open_tr() html.th(left) html.td(right) html.close_tr() # Actions html.open_tr() html.th(_("Actions")) html.open_td() if job_status.get("may_stop"): html.icon_button( html.makeactionuri([(ActionHandler.stop_job_var, job_id)]), _("Stop this job"), "disable_test", ) if job_status.get("may_delete"): html.icon_button( html.makeactionuri([(ActionHandler.delete_job_var, job_id)]), _("Delete this job"), "delete", ) html.close_td() html.close_tr() # Job state html.open_tr() html.th(_("State")) html.td(job_status["state"], css=cls.get_css_for_jobstate(job_status["state"])) html.close_tr() if job_status["state"] == background_job.JobStatusStates.EXCEPTION: html.open_tr() html.th(_("Acknowledged by")) html.td(job_status.get("acknowledged_by", "")) html.close_tr() # Dynamic data loginfo = job_status.get("loginfo") runtime_info = ensure_str( cmk.utils.render.timespan(job_status.get("duration", 0))) if job_status["state"] == background_job.JobStatusStates.RUNNING \ and job_status.get("estimated_duration") is not None: runtime_info += u" (%s: %s)" % ( _("estimated duration"), ensure_str( cmk.utils.render.timespan( job_status["estimated_duration"]))) for left, right in [ (_("Runtime"), runtime_info), (_("PID"), job_status["pid"] or ""), (_("Result"), "<br>".join(loginfo["JobResult"])), ]: if right is None: continue html.open_tr() html.th(left) html.td(HTML(right)) html.close_tr() # Exceptions exceptions = loginfo["JobException"] if exceptions: html.open_tr() html.th(_("Exceptions")) html.open_td() if exceptions and "logfile_path" in job_status: exceptions.append( _("More information can be found in %s") % job_status["logfile_path"]) html.open_div(class_="log_output", id_="exception_log") html.pre("\n".join(exceptions)) html.close_div() html.close_td() html.close_tr() # Progress Update html.open_tr() html.th(_("Progress Info")) html.open_td() html.open_div(class_="log_output", style="height: 400px;", id_="progress_log") html.pre(HTML("\n").join(loginfo["JobProgressUpdate"])) html.close_div() html.close_td() html.close_tr() html.close_table() html.javascript( "var log = document.getElementById('progress_log'); log.scrollTop = log.scrollHeight;" )
def _show_rows(self): rows = self._get_rows() if bool([r for r in rows if r.stats is None]): html.center(_("No data from any site")) return html.open_table(class_=["content_center", "tacticaloverview"], cellspacing="2", cellpadding="0", border="0") show_stales = self.parameters()["show_stale"] and config.user.may( "general.see_stales_in_tactical_overview") has_stale_objects = bool([r for r in rows if r.what != "events" and r.stats[-1]]) for row in rows: if row.what == "events": amount, problems, unhandled_problems = row.stats stales = 0 # no events open and disabled in local site: don't show events if amount == 0 and not config.mkeventd_enabled: continue else: amount, problems, unhandled_problems, stales = row.stats context_vars = get_context_url_variables(row.context) html.open_tr() html.th(row.title) html.th(_("Problems")) html.th(_("Unhandled")) if show_stales and has_stale_objects: html.th(_("Stale")) html.close_tr() td_class = 'col4' if has_stale_objects else 'col3' html.open_tr() url = html.makeuri_contextless(row.views.total + context_vars, filename="view.py") html.open_td(class_=["total", td_class]) html.a("%s" % amount, href=url, target="main") html.close_td() for value, ty in [(problems, "handled"), (unhandled_problems, "unhandled")]: url = html.makeuri_contextless(getattr(row.views, ty) + context_vars, filename="view.py") html.open_td(class_=[td_class, "states prob" if value != 0 else None]) link(str(value), url) html.close_td() if show_stales and has_stale_objects: if row.views.stale: url = html.makeuri_contextless(row.views.stale + context_vars, filename="view.py") html.open_td(class_=[td_class, "states prob" if stales != 0 else None]) link(str(stales), url) html.close_td() else: html.td(html.render_span("0")) html.close_tr() html.close_table()
def show(self): pie_id = "dashlet_%d" % self._dashlet_id pie_diameter = 130 pie_left_aspect = 0.5 pie_right_aspect = 0.8 table = self._table() filter_headers, only_sites = visuals.get_filter_headers( table=self._livestatus_table(), infos=self.infos(), context=self.context) query = "GET %s\n" % self._livestatus_table() for entry in table: query += entry[3] query += self._filter() + filter_headers if only_sites: try: sites.live().set_only_sites(only_sites) result = sites.live().query_row(query) finally: sites.live().set_only_sites() else: try: result = sites.live().query_summed_stats(query) except MKLivestatusNotFoundError: result = [] pies = list(zip(table, result)) total = sum([x[1] for x in pies]) html.open_div(class_="stats") html.canvas('', class_="pie", id_="%s_stats" % pie_id, width=pie_diameter, height=pie_diameter, style="float: left") html.img(html.theme_url("images/globe.png"), class_="globe") html.open_table(class_=["hoststats"] + (["narrow"] if len(pies) > 0 else []), style="float:left") table_entries = pies while len(table_entries) < 6: table_entries = table_entries + [( ("", None, [], ""), HTML(" "))] table_entries.append(((_("Total"), "", [], ""), total)) for (name, color, table_url_vars, query), count in table_entries: url_vars = [ ("view_name", self._view_name()), ("filled_in", "filter"), ("search", "1"), ] + table_url_vars + self._dashlet_context_vars() url = html.makeuri_contextless(url_vars, filename="view.py") html.open_tr() html.th(html.render_a(name, href=url)) html.td('', class_="color", style="background-color: %s" % color if color else '') html.td(html.render_a(count, href=url)) html.close_tr() html.close_table() pie_parts = [] if total > 0: # Count number of non-empty classes num_nonzero = 0 for _info, value in pies: if value > 0: num_nonzero += 1 # Each non-zero class gets at least a view pixels of visible thickness. # We reserve that space right now. All computations are done in percent # of the radius. separator = 0.02 # 3% of radius remaining_separatorspace = num_nonzero * separator # space for separators remaining_radius = 1 - remaining_separatorspace # remaining space remaining_part = 1.0 # keep track of remaining part, 1.0 = 100% # Loop over classes, begin with most outer sphere. Inner spheres show # worse states and appear larger to the user (which is the reason we # are doing all this stuff in the first place) for (name, color, _unused, _q), value in pies[::1]: if value > 0 and remaining_part > 0: # skip empty classes # compute radius of this sphere *including all inner spheres!* The first # sphere always gets a radius of 1.0, of course. radius = remaining_separatorspace + remaining_radius * ( remaining_part**(1 / 3.0)) pie_parts.append('chart_pie("%s", %f, %f, %r, true);' % (pie_id, pie_right_aspect, radius, color)) pie_parts.append('chart_pie("%s", %f, %f, %r, false);' % (pie_id, pie_left_aspect, radius, color)) # compute relative part of this class part = float(value) / total # ranges from 0 to 1 remaining_part -= part remaining_separatorspace -= separator html.close_div() html.javascript( """ function chart_pie(pie_id, x_scale, radius, color, right_side) { var context = document.getElementById(pie_id + "_stats").getContext('2d'); if (!context) return; var pie_x = %(x)f; var pie_y = %(y)f; var pie_d = %(d)f; context.fillStyle = color; context.save(); context.translate(pie_x, pie_y); context.scale(x_scale, 1); context.beginPath(); if(right_side) context.arc(0, 0, (pie_d / 2) * radius, 1.5 * Math.PI, 0.5 * Math.PI, false); else context.arc(0, 0, (pie_d / 2) * radius, 0.5 * Math.PI, 1.5 * Math.PI, false); context.closePath(); context.fill(); context.restore(); context = null; } if (cmk.dashboard.has_canvas_support()) { %(p)s } """ % { "x": int(pie_diameter / 2.0), "y": int(pie_diameter / 2.0), "d": pie_diameter, 'p': '\n'.join(pie_parts) })
def show(self): # pie_id, what, table, filter, dashlet): pie_id = "dashlet_%d" % self._dashlet_id pie_diameter = 130 pie_left_aspect = 0.5 pie_right_aspect = 0.8 what = self._livestatus_table() table = self._table() filter_ = self._filter() if what == 'hosts': info = 'host' infos = [info] else: info = 'service' infos = ['host', 'service'] use_filters = visuals.filters_of_visual(self._dashlet_spec, infos) for filt in use_filters: if filt.available() and not isinstance(filt, FilterCRESite): filter_ += filt.filter(info) query = "GET %s\n" % what for entry in table: query += entry[3] query += filter_ site = self._dashlet_spec['context'].get('siteopt', {}).get('site') if site: sites.live().set_only_sites([site]) result = sites.live().query_row(query) sites.live().set_only_sites() else: try: result = sites.live().query_summed_stats(query) except MKLivestatusNotFoundError: result = [] pies = zip(table, result) total = sum([x[1] for x in pies]) html.open_div(class_="stats") html.canvas('', class_="pie", id_="%s_stats" % pie_id, width=pie_diameter, height=pie_diameter, style="float: left") html.img(html.theme_url("images/globe.png"), class_="globe") html.open_table(class_=["hoststats"] + (["narrow"] if len(pies) > 0 else []), style="float:left") table_entries = pies while len(table_entries) < 6: table_entries = table_entries + [( ("", None, "", ""), HTML(" "))] table_entries.append(((_("Total"), "", "all%s" % what, ""), total)) for (name, color, viewurl, query), count in table_entries: url = "view.py?view_name=" + viewurl + "&filled_in=filter&search=1" for filter_name, url_params in self._dashlet_spec['context'].items( ): if filter_name == "wato_folder" and html.request.has_var( "wato_folder"): url += "&wato_folder=" + html.request.var("wato_folder") elif filter_name == "svcstate": # The svcstate filter URL vars are controlled by dashlet continue else: url += '&' + html.urlencode_vars(url_params.items()) html.open_tr() html.th(html.render_a(name, href=url)) html.td('', class_="color", style="background-color: %s" % color if color else '') html.td(html.render_a(count, href=url)) html.close_tr() html.close_table() pie_parts = [] if total > 0: # Count number of non-empty classes num_nonzero = 0 for info, value in pies: if value > 0: num_nonzero += 1 # Each non-zero class gets at least a view pixels of visible thickness. # We reserve that space right now. All computations are done in percent # of the radius. separator = 0.02 # 3% of radius remaining_separatorspace = num_nonzero * separator # space for separators remaining_radius = 1 - remaining_separatorspace # remaining space remaining_part = 1.0 # keep track of remaining part, 1.0 = 100% # Loop over classes, begin with most outer sphere. Inner spheres show # worse states and appear larger to the user (which is the reason we # are doing all this stuff in the first place) for (name, color, viewurl, _q), value in pies[::1]: if value > 0 and remaining_part > 0: # skip empty classes # compute radius of this sphere *including all inner spheres!* The first # sphere always gets a radius of 1.0, of course. radius = remaining_separatorspace + remaining_radius * ( remaining_part**(1 / 3.0)) pie_parts.append('chart_pie("%s", %f, %f, %r, true);' % (pie_id, pie_right_aspect, radius, color)) pie_parts.append('chart_pie("%s", %f, %f, %r, false);' % (pie_id, pie_left_aspect, radius, color)) # compute relative part of this class part = float(value) / total # ranges from 0 to 1 remaining_part -= part remaining_separatorspace -= separator html.close_div() html.javascript( """ function chart_pie(pie_id, x_scale, radius, color, right_side) { var context = document.getElementById(pie_id + "_stats").getContext('2d'); if (!context) return; var pie_x = %(x)f; var pie_y = %(y)f; var pie_d = %(d)f; context.fillStyle = color; context.save(); context.translate(pie_x, pie_y); context.scale(x_scale, 1); context.beginPath(); if(right_side) context.arc(0, 0, (pie_d / 2) * radius, 1.5 * Math.PI, 0.5 * Math.PI, false); else context.arc(0, 0, (pie_d / 2) * radius, 0.5 * Math.PI, 1.5 * Math.PI, false); context.closePath(); context.fill(); context.restore(); context = null; } if (cmk.dashboard.has_canvas_support()) { %(p)s } """ % { "x": int(pie_diameter / 2.0), "y": int(pie_diameter / 2.0), "d": pie_diameter, 'p': '\n'.join(pie_parts) })
def _show_graph_legend(graph_artwork, graph_render_options) -> None: """Render legend that describe the metrics""" graph_width = graph_render_options["size"][0] * html_size_per_ex font_size_style = "font-size: %dpt;" % graph_render_options["font_size"] scalars = get_scalars(graph_artwork, graph_render_options) if graph_render_options["show_vertical_axis"] or graph_render_options[ "show_controls"]: legend_margin_left = 49 else: legend_margin_left = 0 style = [] legend_width = graph_width - legend_margin_left # In case there is no margin show: Add some to the legend since it looks # ugly when there is no space between the outer graph border and the legend if not graph_render_options["show_margin"]: legend_width -= 5 * 2 style.append("margin: 8px 5px 5px 5px") style.append("width:%dpx" % legend_width) if legend_margin_left: style.append("margin-left:%dpx" % legend_margin_left) html.open_table(class_="legend", style=style) # Render the title row html.open_tr() html.th("") for scalar, title, inactive in scalars: classes = ["scalar", scalar] if inactive and graph_artwork["step"] != 60: descr = _( "This graph is based on data consolidated with the function \"%s\". The " "values in this column are the \"%s\" values of the \"%s\" values " "aggregated in %s steps. Assuming a check interval of 1 minute, the %s " "values here are based on the %s value out of %d raw values." ) % ( graph_artwork["definition"]["consolidation_function"], scalar, graph_artwork["definition"]["consolidation_function"], artwork.get_step_label(graph_artwork["step"]), scalar, graph_artwork["definition"]["consolidation_function"], (graph_artwork["step"] / 60), ) descr += "\n\n" + _("Click here to change the graphs " "consolidation function to \"%s\".") % scalar classes.append("inactive") else: descr = "" html.th(title, class_=classes, style=font_size_style, title=descr) html.close_tr() # Render the curve related rows for curve in graph_curves(graph_artwork): html.open_tr() html.open_td(style=font_size_style) html.write_html(render_color_icon(curve["color"])) html.write_text(curve["title"]) html.close_td() for scalar, title, inactive in scalars: if scalar == "pin" and not show_pin_time(graph_artwork, graph_render_options): continue if inactive and graph_artwork["step"] != 60: inactive_cls: Optional[str] = "inactive" else: inactive_cls = None html.td(curve["scalars"][scalar][1], class_=inactive_cls, style=font_size_style) html.close_tr() # Render scalar values if graph_artwork["horizontal_rules"]: first = True for _value, readable, color, title in graph_artwork[ "horizontal_rules"]: html.open_tr(class_=["scalar", "first" if first else None]) html.open_td(style=font_size_style) html.write_html(render_color_icon(color)) html.write_text(title) html.close_td() # A colspan of 5 has to be used here, since the pin that is added by a click into # the graph introduces a new column. html.td(readable, colspan=5, class_="scalar", style=font_size_style) html.close_tr() first = False html.close_table()