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()
def _show_service_info(self, all_rulesets): serviceinfo = watolib.check_mk_automation( self._host.site_id(), "analyse-service", [self._hostname, self._service]) if not serviceinfo: return forms.header(_("Check origin and parameters"), isopen=True, narrow=True, css="rulesettings") origin = serviceinfo["origin"] origin_txt = { "active": _("Active check"), "static": _("Manual check"), "auto": _("Inventorized check"), "classic": _("Classical check"), }[origin] self._render_rule_reason(_("Type of check"), None, "", "", False, origin_txt) # First case: discovered checks. They come from var/check_mk/autochecks/HOST. if origin == "auto": checkgroup = serviceinfo["checkgroup"] checktype = serviceinfo["checktype"] if not checkgroup: self._render_rule_reason( _("Parameters"), None, "", "", True, _("This check is not configurable via WATO")) # Logwatch needs a special handling, since it is not configured # via checkgroup_parameters but via "logwatch_rules" in a special # WATO module. elif checkgroup == "logwatch": rulespec = rulespec_registry["logwatch_rules"] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=serviceinfo["parameters"]) else: # Note: some discovered checks have a check group but # *no* ruleset for discovered checks. One example is "ps". # That can be configured as a manual check or created by # inventory. But in the later case all parameters are set # by the inventory. This will be changed in a later version, # but we need to address it anyway. grouprule = "checkgroup_parameters:" + checkgroup if grouprule not in rulespec_registry: try: rulespec = rulespec_registry["static_checks:" + checkgroup] except KeyError: rulespec = None if rulespec: url = watolib.folder_preserving_link([ ('mode', 'edit_ruleset'), ('varname', "static_checks:" + checkgroup), ('host', self._hostname) ]) self._render_rule_reason( _("Parameters"), url, _("Determined by discovery"), None, False, rulespec.valuespec._elements[2].value_to_text( serviceinfo["parameters"])) else: self._render_rule_reason( _("Parameters"), None, "", "", True, _("This check is not configurable via WATO")) else: rulespec = rulespec_registry[grouprule] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=serviceinfo["parameters"]) elif origin == "static": checkgroup = serviceinfo["checkgroup"] checktype = serviceinfo["checktype"] if not checkgroup: html.write_text(_("This check is not configurable via WATO")) else: rulespec = rulespec_registry["static_checks:" + checkgroup] itemspec = rulespec.item_spec if itemspec: item_text = itemspec.value_to_text(serviceinfo["item"]) title = rulespec.item_spec.title() else: item_text = serviceinfo["item"] title = _("Item") self._render_rule_reason(title, None, "", "", False, item_text) self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=self._PARAMETERS_OMIT) html.write(rulespec.valuespec._elements[2].value_to_text( serviceinfo["parameters"])) html.close_td() html.close_tr() html.close_table() elif origin == "active": checktype = serviceinfo["checktype"] rulespec = rulespec_registry["active_checks:" + checktype] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=None, svc_desc=None, known_settings=serviceinfo["parameters"]) elif origin == "classic": rule_nr = serviceinfo["rule_nr"] rules = all_rulesets.get("custom_checks").get_rules() rule_folder, rule_index, _rule = rules[rule_nr] url = watolib.folder_preserving_link([('mode', 'edit_ruleset'), ('varname', "custom_checks"), ('host', self._hostname)]) forms.section(html.render_a(_("Command Line"), href=url)) url = watolib.folder_preserving_link([('mode', 'edit_rule'), ('varname', "custom_checks"), ('rule_folder', rule_folder.path()), ('rulenr', rule_index), ('host', self._hostname)]) html.open_table(class_="setting") html.open_tr() html.open_td(class_="reason") html.a("%s %d %s %s" % (_("Rule"), rule_index + 1, _("in"), rule_folder.title()), href=url) html.close_td() html.open_td(class_=["settingvalue", "used"]) if "command_line" in serviceinfo: html.tt(serviceinfo["command_line"]) else: html.write_text(_("(no command line, passive check)")) html.close_td() html.close_tr() html.close_table() self._show_labels(serviceinfo.get("labels", {}), "service", serviceinfo.get("label_sources", {}))
def _write_table( self, rows: TableRows, num_rows_unlimited: int, actions_enabled: bool, actions_visible: bool, search_term: Optional[str], ) -> None: if not self.options["omit_update_header"]: row_info = _("1 row") if len( rows) == 1 else _("%d rows") % num_rows_unlimited html.javascript("cmk.utils.update_row_info(%s);" % json.dumps(row_info)) table_id = self.id num_cols = self._get_num_cols(rows) empty_columns = self._get_empty_columns(rows, num_cols) if self.options["omit_empty_columns"]: 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 not isinstance(self.rows[0], GroupHeader): self._render_headers( actions_enabled, actions_visible, empty_columns, ) if actions_enabled and actions_visible: html.open_tr(class_=["data", "even0", "actions"]) html.open_td(colspan=num_cols) if not html.in_form(): html.begin_form("%s_actions" % table_id) if request.has_var("_%s_sort" % table_id): html.open_div(class_=["sort"]) html.button("_%s_reset_sorting" % table_id, _("Reset sorting")) html.close_div() if not html.in_form(): html.begin_form("%s_actions" % table_id) html.hidden_fields() html.end_form() html.close_tr() for nr, row in enumerate(rows): # Intermediate header if isinstance(row, GroupHeader): # Show the header only, if at least one (non-header) row follows if nr < len(rows) - 1 and not isinstance( rows[nr + 1], GroupHeader): html.open_tr(class_="groupheader") html.open_td(colspan=num_cols) html.h3(row.title) html.close_td() html.close_tr() self._render_headers(actions_enabled, actions_visible, empty_columns) continue oddeven_name = "even" if nr % 2 == 0 else "odd" class_ = ["data", "%s%d" % (oddeven_name, row.state)] if isinstance(row.css, list): class_.extend([c for c in row.css if c is not None]) elif row.css is not None: class_.append(row.css) html.open_tr(class_=class_, id_=row.id_, onmouseover=row.onmouseover, onmouseout=row.onmouseout) for col_index, cell in enumerate(row.cells): if self.options["omit_empty_columns"] and empty_columns[ col_index]: continue html.td(cell.content, class_=cell.css, colspan=cell.colspan) html.close_tr() if not rows and search_term: html.open_tr(class_=["data", "odd0", "no_match"]) html.td( _("Found no matching rows. Please try another search term."), colspan=num_cols) html.close_tr() html.close_table()
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): header_majorities = self._matrix_find_majorities_for_header( rows, group_cells) value_counts, row_majorities = self._matrix_find_majorities( rows, cells) painter_options = PainterOptions.get_instance() for groups, unique_row_ids, matrix_cells in create_matrices( rows, group_cells, cells, num_columns): # Paint the matrix. Begin with the group headers html.open_table(class_="data matrix") odd = "odd" for cell_nr, cell in enumerate(group_cells): odd = "even" if odd == "odd" else "odd" html.open_tr(class_="data %s0" % odd) html.open_td(class_="matrixhead") html.write_text(cell.title(use_short=False)) html.close_td() for _group, group_row in groups: tdclass, content = cell.render(group_row) if cell_nr > 0: gv = group_value(group_row, [cell]) majority_value = header_majorities.get( cell_nr - 1, None) if majority_value is not None and majority_value != gv: tdclass += " minority" html.open_td(class_=["left", tdclass]) html.write_text(content) html.close_td() html.close_tr() # Now for each unique service^H^H^H^H^H^H ID column paint one row for rid in unique_row_ids: # Omit rows where all cells have the same values if painter_options.get("matrix_omit_uniform"): at_least_one_different = False for counts in value_counts[rid].values(): if len(counts) > 1: at_least_one_different = True break if not at_least_one_different: continue odd = "even" if odd == "odd" else "odd" html.open_tr(class_="data %s0" % odd) tdclass, content = cells[0].render( list(matrix_cells[rid].values())[0]) html.open_td(class_=["left", tdclass]) html.write_text(content) html.close_td() # Now go through the groups and paint the rest of the # columns for group_id, group_row in groups: cell_row = matrix_cells[rid].get(group_id) if cell_row is None: html.td("") else: if len(cells) > 2: html.open_td(class_="cell") html.open_table() for cell_nr, cell in enumerate(cells[1:]): tdclass, content = cell.render(cell_row) gv = group_value(cell_row, [cell]) majority_value = row_majorities[rid].get( cell_nr, None) if majority_value is not None and majority_value != gv: tdclass += " minority" if len(cells) > 2: html.open_tr() html.td(content, class_=tdclass) if len(cells) > 2: html.close_tr() if len(cells) > 2: html.close_table() html.close_td() html.close_tr() html.close_table()
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 _write_table(self, rows, actions_enabled, actions_visible, search_term): headinfo = _("1 row") if len(rows) == 1 else _("%d rows") % len(rows) html.javascript("cmk.utils.update_header_info(%s);" % json.dumps(headinfo)) 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, empty_columns, ) if actions_enabled and actions_visible: html.open_tr(class_=["data", "even0", "actions"]) html.open_td(colspan=num_cols) if not html.in_form(): html.begin_form("%s_actions" % table_id) if self.options["searchable"]: html.open_div(class_="search") html.text_input("_%s_search" % table_id) html.button("_%s_submit" % table_id, _("Search")) html.button("_%s_reset" % table_id, _("Reset search")) html.set_focus("_%s_search" % table_id) html.close_div() if html.request.has_var('_%s_sort' % table_id): html.open_div(class_=["sort"]) html.button("_%s_reset_sorting" % table_id, _("Reset sorting")) html.close_div() if not html.in_form(): html.begin_form("%s_actions" % table_id) html.hidden_fields() html.end_form() 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: css = attrs.pop("class") # Intermediate header if state == "header": # Show the header only, if at least one (non-header) row follows if nr < len(rows) - 1 and rows[nr + 1][2] != "header": html.open_tr(class_="groupheader") html.open_td(colspan=num_cols) html.open_h3() html.write(row_spec) html.close_h3() html.close_td() html.close_tr() 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 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) html.write(cell_content) html.close_td() html.close_tr() if not rows and search_term: html.open_tr(class_=["data", "odd0", "no_match"]) html.td( _('Found no matching rows. Please try another search term.'), colspan=num_cols) html.close_tr() html.close_table()
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): html.open_table(class_="data tiled") last_group = None group_open = False for row in rows: # Show group header if group_cells: this_group = group_value(row, group_cells) if this_group != last_group: # paint group header if group_open: html.close_td() html.close_tr() html.open_tr() html.open_td() html.open_table(class_="groupheader") html.open_tr(class_="groupheader") painted = False for cell in group_cells: if painted: html.td(", ") painted = cell.paint(row) html.close_tr() html.close_table() html.close_td() html.close_tr() html.open_tr() html.open_td(class_="tiles") group_open = True last_group = this_group # background color of tile according to item state state = row.get("service_state", -1) if state == -1: hbc = row.get("host_has_been_checked", 1) if hbc: state = row.get("host_state", 0) sclass = "hhstate%d" % state else: sclass = "hhstatep" else: hbc = row.get("service_has_been_checked", 1) if hbc: sclass = "sstate%d" % state else: sclass = "sstatep" if not group_open: html.open_tr() html.open_td(class_="tiles") group_open = True html.open_div(class_=["tile", sclass]) html.open_table() # We need at least five cells if len(cells) < 5: cells = cells + ([EmptyCell(view)] * (5 - len(cells))) rendered = [cell.render(row) for cell in cells] html.open_tr() html.open_td(class_=["tl", rendered[1][0]]) if show_checkboxes: render_checkbox(view, row, len(cells) - 1) html.write_text(rendered[1][1]) html.close_td() html.open_td(class_=["tr", rendered[2][0]]) html.write_text(rendered[2][1]) html.close_td() html.close_tr() html.open_tr() html.open_td(colspan=2, class_=["center", rendered[0][0]]) html.write_text(rendered[0][1]) html.close_td() html.close_tr() for css, cont in rendered[5:]: html.open_tr() html.open_td(colspan=2, class_=["cont", css]) html.write_text(cont) html.close_td() html.close_tr() html.open_tr() html.open_td(class_=["bl", rendered[3][0]]) html.write_text(rendered[3][1]) html.close_td() html.open_td(class_=["br", rendered[4][0]]) html.write_text(rendered[4][1]) html.close_td() html.close_tr() html.close_table() html.close_div() if group_open: html.close_td() html.close_tr() html.close_table() if not user.may("general.act"): return init_rowselect(_get_view_name(view))
def render_bi_availability(title, aggr_rows): config.user.need_permission("general.see_availability") av_mode = html.request.var("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 av_mode == "availability" and 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 = [] # iterate all aggregation rows timewarpcode = "" try: timewarp = int(html.request.var("timewarp")) except (ValueError, TypeError): timewarp = None 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=bi.load_ex_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_availability_csv("bi", 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 _show_start_form(self): html.begin_form("parentscan", method="POST") html.hidden_fields() # Mode of action html.open_p() if not self._complete_folder: num_selected = len(get_hosts_from_checkboxes()) html.write_text( _("You have selected <b>%d</b> hosts for parent scan. ") % num_selected) html.p( _("The parent scan will try to detect the last gateway " "on layer 3 (IP) before a host. This will be done by " "calling <tt>traceroute</tt>. If a gateway is found by " "that way and its IP address belongs to one of your " "monitored hosts, that host will be used as the hosts " "parent. If no such host exists, an artifical ping-only " "gateway host will be created if you have not disabled " "this feature.")) forms.header(_("Settings for Parent Scan")) self._settings = config.user.load_file( "parentscan", { "where": "subfolder", "alias": _("Created by parent scan"), "recurse": True, "select": "noexplicit", "timeout": 8, "probes": 2, "ping_probes": 5, "max_ttl": 10, "force_explicit": False, }) # Selection forms.section(_("Selection")) if self._complete_folder: html.checkbox("recurse", self._settings["recurse"], label=_("Include all subfolders")) html.br() html.radiobutton( "select", "noexplicit", self._settings["select"] == "noexplicit", _("Skip hosts with explicit parent definitions (even if empty)") + "<br>") html.radiobutton( "select", "no", self._settings["select"] == "no", _("Skip hosts hosts with non-empty parents (also if inherited)") + "<br>") html.radiobutton("select", "ignore", self._settings["select"] == "ignore", _("Scan all hosts") + "<br>") # Performance forms.section(_("Performance")) html.open_table() html.open_tr() html.open_td() html.write_text(_("Timeout for responses") + ":") html.close_td() html.open_td() html.number_input("timeout", self._settings["timeout"], size=2) html.write_text(_("sec")) html.close_td() html.close_tr() html.open_tr() html.open_td() html.write_text(_("Number of probes per hop") + ":") html.close_td() html.open_td() html.number_input("probes", self._settings["probes"], size=2) html.close_td() html.close_tr() html.open_tr() html.open_td() html.write_text(_("Maximum distance (TTL) to gateway") + ":") html.close_td() html.open_td() html.number_input("max_ttl", self._settings["max_ttl"], size=2) html.close_td() html.close_tr() html.open_tr() html.open_td() html.write_text(_("Number of PING probes") + ":") html.help( _("After a gateway has been found, Check_MK checks if it is reachable " "via PING. If not, it is skipped and the next gateway nearer to the " "monitoring core is being tried. You can disable this check by setting " "the number of PING probes to 0.")) html.close_td() html.open_td() html.number_input("ping_probes", self._settings.get("ping_probes", 5), size=2) html.close_td() html.close_tr() html.close_table() # Configuring parent forms.section(_("Configuration")) html.checkbox( "force_explicit", self._settings["force_explicit"], label= _("Force explicit setting for parents even if setting matches that of the folder" )) # Gateway creation forms.section(_("Creation of gateway hosts")) html.write_text(_("Create gateway hosts in")) html.open_ul() html.radiobutton( "where", "subfolder", self._settings["where"] == "subfolder", _("in the subfolder <b>%s/Parents</b>") % watolib.Folder.current_disk_folder().title()) html.br() html.radiobutton( "where", "here", self._settings["where"] == "here", _("directly in the folder <b>%s</b>") % watolib.Folder.current_disk_folder().title()) html.br() html.radiobutton("where", "there", self._settings["where"] == "there", _("in the same folder as the host")) html.br() html.radiobutton("where", "nowhere", self._settings["where"] == "nowhere", _("do not create gateway hosts")) html.close_ul() html.write_text(_("Alias for created gateway hosts") + ": ") html.text_input("alias", self._settings["alias"]) forms.end() # Start button html.button("_start", _("Start")) html.hidden_fields() html.end_form()
def page(self): # Let exceptions from loading notification scripts happen now watolib.load_notification_scripts() html.begin_form("user", method="POST") html.prevent_password_auto_completion() forms.header(_("Identity")) # ID forms.section(_("Username"), simple=not self._is_new_user) if self._is_new_user: vs_user_id = UserID(allow_empty=False) else: vs_user_id = FixedValue(self._user_id) vs_user_id.render_input("user_id", self._user_id) def lockable_input(name, dflt): if not self._is_locked(name): html.text_input(name, self._user.get(name, dflt), size=50) else: html.write_text(self._user.get(name, dflt)) html.hidden_field(name, self._user.get(name, dflt)) # Full name forms.section(_("Full name")) lockable_input('alias', self._user_id) html.help(_("Full name or alias of the user")) # Email address forms.section(_("Email address")) email = self._user.get("email", "") if not self._is_locked("email"): EmailAddressUnicode().render_input("email", email) else: html.write_text(email) html.hidden_field("email", email) html.help( _("The email address is optional and is needed " "if the user is a monitoring contact and receives notifications " "via Email.")) forms.section(_("Pager address")) lockable_input('pager', '') html.help(_("The pager address is optional ")) if cmk.is_managed_edition(): forms.section(self._vs_customer.title()) self._vs_customer.render_input("customer", managed.get_customer_id(self._user)) html.help(self._vs_customer.help()) vs_sites = self._vs_sites() forms.section(vs_sites.title()) authorized_sites = self._user.get("authorized_sites", vs_sites.default_value()) if not self._is_locked("authorized_sites"): vs_sites.render_input("authorized_sites", authorized_sites) else: html.write_html(vs_sites.value_to_text(authorized_sites)) html.help(vs_sites.help()) self._show_custom_user_attributes('ident') forms.header(_("Security")) forms.section(_("Authentication")) is_automation = self._user.get("automation_secret", None) is not None html.radiobutton("authmethod", "password", not is_automation, _("Normal user login with password")) html.open_ul() html.open_table() html.open_tr() html.td(_("password:"******"_password_" + self._pw_suffix(), autocomplete="new-password") html.close_td() html.close_tr() html.open_tr() html.td(_("repeat:")) html.open_td() html.password_input("_password2_" + self._pw_suffix(), autocomplete="new-password") html.write_text(" (%s)" % _("optional")) html.close_td() html.close_tr() html.open_tr() html.td("%s:" % _("Enforce change")) html.open_td() # Only make password enforcement selection possible when user is allowed to change the PW if self._is_new_user or config.user_may(self._user_id, 'general.edit_profile') and config.user_may( self._user_id, 'general.change_password'): html.checkbox("enforce_pw_change", self._user.get("enforce_pw_change", False), label=_("Change password at next login or access")) else: html.write_text( _("Not permitted to change the password. Change can not be enforced.")) else: html.i(_('The password can not be changed (It is locked by the user connector).')) html.hidden_field('_password', '') html.hidden_field('_password2', '') html.close_td() html.close_tr() html.close_table() html.close_ul() html.radiobutton("authmethod", "secret", is_automation, _("Automation secret for machine accounts")) html.open_ul() html.text_input("_auth_secret", self._user.get("automation_secret", ""), size=30, id_="automation_secret") html.write_text(" ") html.open_b(style=["position: relative", "top: 4px;"]) html.write(" ") html.icon_button("javascript:cmk.wato.randomize_secret('automation_secret', 20);", _("Create random secret"), "random") html.close_b() html.close_ul() html.help( _("If you want the user to be able to login " "then specify a password here. Users without a login make sense " "if they are monitoring contacts that are just used for " "notifications. The repetition of the password is optional. " "<br>For accounts used by automation processes (such as fetching " "data from views for further procession), set the method to " "<u>secret</u>. The secret will be stored in a local file. Processes " "with read access to that file will be able to use Multisite as " "a webservice without any further configuration.")) # Locking forms.section(_("Disable password"), simple=True) if not self._is_locked('locked'): html.checkbox("locked", self._user.get("locked", False), label=_("disable the login to this account")) else: html.write_text( _('Login disabled') if self._user.get("locked", False) else _('Login possible')) html.hidden_field('locked', '1' if self._user.get("locked", False) else '') html.help( _("Disabling the password will prevent a user from logging in while " "retaining the original password. Notifications are not affected " "by this setting.")) forms.section(_("Idle timeout")) idle_timeout = self._user.get("idle_timeout") if not self._is_locked("idle_timeout"): watolib.get_vs_user_idle_timeout().render_input("idle_timeout", idle_timeout) else: html.write_text(idle_timeout) html.hidden_field("idle_timeout", idle_timeout) # Roles forms.section(_("Roles")) entries = self._roles.items() entries.sort(key=lambda x: (x[1]["alias"], x[0])) is_member_of_at_least_one = False for role_id, role in entries: if not self._is_locked("roles"): html.checkbox("role_" + role_id, role_id in self._user.get("roles", [])) url = watolib.folder_preserving_link([("mode", "edit_role"), ("edit", role_id)]) html.a(role["alias"], href=url) html.br() else: is_member = role_id in self._user.get("roles", []) if is_member: is_member_of_at_least_one = True url = watolib.folder_preserving_link([("mode", "edit_role"), ("edit", role_id)]) html.a(role["alias"], href=url) html.br() html.hidden_field("role_" + role_id, '1' if is_member else '') if self._is_locked('roles') and not is_member_of_at_least_one: html.i(_('No roles assigned.')) self._show_custom_user_attributes('security') # Contact groups forms.header(_("Contact Groups"), isopen=False) forms.section() groups_page_url = watolib.folder_preserving_link([("mode", "contact_groups")]) group_assign_url = watolib.folder_preserving_link([("mode", "rulesets"), ("group", "grouping")]) if not self._contact_groups: html.write( _("Please first create some <a href='%s'>contact groups</a>") % groups_page_url) else: entries = sorted([(group['alias'] or c, c) for c, group in self._contact_groups.items() ]) is_member_of_at_least_one = False for alias, gid in entries: is_member = gid in self._user.get("contactgroups", []) if not self._is_locked('contactgroups'): html.checkbox("cg_" + gid, gid in self._user.get("contactgroups", [])) else: if is_member: is_member_of_at_least_one = True html.hidden_field("cg_" + gid, '1' if is_member else '') if not self._is_locked('contactgroups') or is_member: url = watolib.folder_preserving_link([("mode", "edit_contact_group"), ("edit", gid)]) html.a(alias, href=url) html.br() if self._is_locked('contactgroups') and not is_member_of_at_least_one: html.i(_('No contact groups assigned.')) html.help( _("Contact groups are used to assign monitoring " "objects to users. If you haven't defined any contact groups yet, " "then first <a href='%s'>do so</a>. Hosts and services can be " "assigned to contact groups using <a href='%s'>rules</a>.<br><br>" "If you do not put the user into any contact group " "then no monitoring contact will be created for the user.") % (groups_page_url, group_assign_url)) forms.header(_("Notifications"), isopen=False) if not self._rbn_enabled(): forms.section(_("Enabling"), simple=True) html.checkbox("notifications_enabled", self._user.get("notifications_enabled", False), label=_("enable notifications")) html.help( _("Notifications are sent out " "when the status of a host or service changes.")) # Notification period forms.section(_("Notification time period")) choices = [(id_, "%s" % (tp["alias"])) for (id_, tp) in self._timeperiods.items()] html.dropdown("notification_period", choices, deflt=self._user.get("notification_period"), ordered=True) html.help( _("Only during this time period the " "user will get notifications about host or service alerts.")) # Notification options notification_option_names = { # defined here: _() must be executed always! "host": { "d": _("Host goes down"), "u": _("Host gets unreachble"), "r": _("Host goes up again"), }, "service": { "w": _("Service goes into warning state"), "u": _("Service goes into unknown state"), "c": _("Service goes into critical state"), "r": _("Service recovers to OK"), }, "both": { "f": _("Start or end of flapping state"), "s": _("Start or end of a scheduled downtime"), } } forms.section(_("Notification Options")) for title, what, opts in [(_("Host events"), "host", "durfs"), (_("Service events"), "service", "wucrfs")]: html.write_text("%s:" % title) html.open_ul() user_opts = self._user.get(what + "_notification_options", opts) for opt in opts: opt_name = notification_option_names[what].get( opt, notification_option_names["both"].get(opt)) html.checkbox(what + "_" + opt, opt in user_opts, label=opt_name) html.br() html.close_ul() html.help( _("Here you specify which types of alerts " "will be notified to this contact. Note: these settings will only be saved " "and used if the user is member of a contact group.")) forms.section(_("Notification Method")) watolib.get_vs_flexible_notifications().render_input( "notification_method", self._user.get("notification_method")) else: forms.section(_("Fallback notifications"), simple=True) html.checkbox("fallback_contact", self._user.get("fallback_contact", False), label=_("Receive fallback notifications")) html.help( _("In case none of your notification rules handles a certain event a notification " "will be sent to this contact. This makes sure that in that case at least <i>someone</i> " "gets notified. Furthermore this contact will be used for notifications to any host or service " "that is not known to the monitoring. This can happen when you forward notifications " "from the Event Console.<br><br>Notification fallback can also configured in the global " "setting <a href=\"wato.py?mode=edit_configvar&varname=notification_fallback_email\">" "Fallback email address for notifications</a>.")) self._show_custom_user_attributes('notify') forms.header(_("Personal Settings"), isopen=False) select_language(self._user) self._show_custom_user_attributes('personal') # Later we could add custom macros here, which then could be used # for notifications. On the other hand, if we implement some check_mk # --notify, we could directly access the data in the account with the need # to store values in the monitoring core. We'll see what future brings. forms.end() html.button("save", _("Save")) if self._is_new_user: html.set_focus("user_id") else: html.set_focus("alias") html.hidden_fields() html.end_form()
def show(self) -> None: mode = self._host_mode_ident() sites.live().set_prepend_site(True) query = "GET hosts\nColumns: name state worst_service_state\nLimit: 100\n" view = "host" if mode == "problems": view = "problemsofhost" # Exclude hosts and services in downtime svc_query = "GET services\nColumns: host_name\n"\ "Filter: state > 0\nFilter: scheduled_downtime_depth = 0\n"\ "Filter: host_scheduled_downtime_depth = 0\nAnd: 3" problem_hosts = {x[1] for x in sites.live().query(svc_query)} query += "Filter: state > 0\nFilter: scheduled_downtime_depth = 0\nAnd: 2\n" for host in problem_hosts: query += "Filter: name = %s\n" % host query += "Or: %d\n" % (len(problem_hosts) + 1) hosts = sites.live().query(query) sites.live().set_prepend_site(False) hosts.sort() longestname = 0 for site, host, state, worstsvc in hosts: longestname = max(longestname, len(host)) if longestname > 15: num_columns = 1 else: num_columns = 2 html.open_table(class_="allhosts") col = 1 for site, host, state, worstsvc in hosts: if col == 1: html.open_tr() html.open_td() if state > 0 or worstsvc == 2: statecolor = 2 elif worstsvc == 1: statecolor = 1 elif worstsvc == 3: statecolor = 3 else: statecolor = 0 html.open_div(class_=["statebullet", "state%d" % statecolor]) html.nbsp() html.close_div() link(text=host, url=makeuri_contextless( request, [ ("view_name", view), ("host", host), ("site", site), ], filename="view.py", )) html.close_td() if col == num_columns: html.close_tr() col = 1 else: col += 1 if col < num_columns: html.close_tr() html.close_table()
def _write_table(self, rows, actions_enabled, actions_visible, search_term): # type: (TableRows, bool, bool, Optional[str]) -> None headinfo = _("1 row") if len(rows) == 1 else _("%d rows") % len(rows) html.javascript("cmk.utils.update_header_info(%s);" % json.dumps(headinfo)) table_id = self.id num_cols = self._get_num_cols(rows) empty_columns = self._get_empty_columns(rows, num_cols) if self.options["omit_empty_columns"]: 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 not isinstance(self.rows[0], GroupHeader): self._render_headers( actions_enabled, actions_visible, empty_columns, ) if actions_enabled and actions_visible: html.open_tr(class_=["data", "even0", "actions"]) html.open_td(colspan=num_cols) if not html.in_form(): html.begin_form("%s_actions" % table_id) if self.options["searchable"]: html.open_div(class_="search") html.text_input("_%s_search" % table_id) html.button("_%s_submit" % table_id, _("Search")) html.button("_%s_reset" % table_id, _("Reset search")) html.set_focus("_%s_search" % table_id) html.close_div() if html.request.has_var('_%s_sort' % table_id): html.open_div(class_=["sort"]) html.button("_%s_reset_sorting" % table_id, _("Reset sorting")) html.close_div() if not html.in_form(): html.begin_form("%s_actions" % table_id) html.hidden_fields() html.end_form() html.close_tr() for nr, row in enumerate(rows): # Intermediate header if isinstance(row, GroupHeader): # Show the header only, if at least one (non-header) row follows if nr < len(rows) - 1 and not isinstance( rows[nr + 1], GroupHeader): html.open_tr(class_="groupheader") html.open_td(colspan=num_cols) html.open_h3() html.write(row.title) html.close_h3() html.close_td() html.close_tr() self._render_headers(actions_enabled, actions_visible, empty_columns) continue oddeven_name = "even" if (nr - 1) % 2 == 0 else "odd" class_ = ["data", "%s%d" % (oddeven_name, row.state)] if row.css: class_.append(row.css) else: for k in ["class_", "class"]: if k in row.row_attributes: cls_spec = cast(CSSSpec, row.row_attributes.pop(k)) if isinstance(cls_spec, list): class_.extend( [c for c in cls_spec if c is not None]) elif cls_spec is not None: class_.append(cls_spec) html.open_tr(class_=class_, **row.row_attributes) for col_index, cell in enumerate(row.cells): if self.options["omit_empty_columns"] and empty_columns[ col_index]: continue html.open_td(class_=cell.css, colspan=cell.colspan) html.write(cell.content) html.close_td() html.close_tr() if not rows and search_term: html.open_tr(class_=["data", "odd0", "no_match"]) html.td( _('Found no matching rows. Please try another search term.'), colspan=num_cols) html.close_tr() html.close_table()
def show(self, width, context): hosts = self._get_livestatus(context) num_hosts = len(hosts) if num_hosts > 900: html.write_text(_("Sorry, I will not display more than 900 hosts.")) return # Choose smallest square number large enough # to show all hosts n = 1 while n * n < num_hosts: n += 1 rows = int(num_hosts / n) lastcols = num_hosts % n if lastcols > 0: rows += 1 # Calculate cell size (Automatic sizing with 100% does not work here) # This is not a 100% solution but way better than having no links cell_spacing = 3 cell_size = (width - cell_spacing * n) / n cell_height = 2 * cell_size / 3 # Add one cell_spacing so that the cells fill the whole snapin width. # The spacing of the last cell overflows on the right. html.open_table(class_=["hostmatrix"], style=["width:%spx" % (width + cell_spacing)]) col, row = 1, 1 for site, host, state, has_been_checked, worstsvc, downtimedepth in sorted(hosts): if col == 1: html.open_tr() if downtimedepth > 0: s = "d" elif not has_been_checked: s = "p" elif worstsvc == 2 or state == 1: s = "2" elif worstsvc == 3 or state == 2: s = "3" elif worstsvc == 1: s = "1" else: s = "0" url = "view.py?view_name=host&site=%s&host=%s" % (urlencode(site), urlencode(host)) html.open_td( style=[ "width:%.2fpx" % (cell_size + cell_spacing), "height:%.2fpx" % (cell_height + cell_spacing), ] ) html.a( "", href=url, title=host, target="main", class_=["state", "state%s" % s], style=["width:%.2fpx;" % cell_size, "height:%.2fpx;" % cell_height], ) html.close_td() if col == n or (row == rows and n == lastcols): html.open_tr() col = 1 row += 1 else: col += 1 html.close_table()
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(self): if not watolib.is_wato_slave_site(): if not config.wato_enabled: html.write_text(_("Setup is disabled.")) return False user_folders = compute_foldertree() # # Render link target selection # selected_topic, selected_target = user.load_file( "foldertree", (_("Hosts"), "allhosts")) # Apply some view specific filters views_to_show = [ (name, view) for name, view in views.get_permitted_views().items() if (not config.visible_views or name in config.visible_views) and ( not config.hidden_views or name not in config.hidden_views) ] visuals_to_show = [("views", e) for e in views_to_show] visuals_to_show += [ ("dashboards", e) for e in dashboard.get_permitted_dashboards().items() ] topics = make_topic_menu(visuals_to_show) topic_choices: Choices = [(topic.title, topic.title) for topic in topics] html.open_table() html.open_tr() html.open_td() html.dropdown( "topic", topic_choices, deflt=selected_topic, onchange="cmk.sidebar.wato_tree_topic_changed(this)", ) html.close_td() html.close_tr() html.open_tr() html.open_td() for topic in topics: targets: Choices = [] for item in topic.items: if item.url and item.url.startswith("dashboard.py"): name = "dashboard|" + item.name else: name = item.name targets.append((name, item.title)) if topic.title != selected_topic: default = "" style: Optional[str] = "display:none" else: default = selected_target style = None html.dropdown( "target_%s" % topic.title, targets, deflt=default, onchange="cmk.sidebar.wato_tree_target_changed(this)", style=style, ) html.close_td() html.close_tr() html.close_table() # Now render the whole tree if user_folders: render_tree_folder("wato-hosts", list(user_folders.values())[0], "cmk.sidebar.wato_tree_click")
def _render_group(self, rows_with_ids, header, view, group_cells, cells, num_columns, show_checkboxes): repeat_heading_every = 20 # in case column_headers is "repeat" html.open_table(class_="groupheader", cellspacing=0, cellpadding=0, border=0) html.open_tr(class_="groupheader") painted = False for cell in group_cells: if painted: html.td(", ") painted = cell.paint(rows_with_ids[0][1]) html.close_tr() html.close_table() html.open_table(class_="data") odd = "odd" column_headers = view.get("column_headers") if column_headers != "off": self._show_header_line(cells, show_checkboxes) groups, rows_with_ids = calculate_view_grouping_of_services( rows_with_ids, row_group_cells=None) visible_row_number = 0 group_hidden, num_grouped_rows = None, 0 for index, row in rows_with_ids: if view.get("column_headers") == "repeat": if visible_row_number > 0 and visible_row_number % repeat_heading_every == 0: self._show_header_line(cells, show_checkboxes) visible_row_number += 1 odd = "even" if odd == "odd" else "odd" # state = row.get("service_state", row.get("aggr_state")) state = utils.saveint(row.get("service_state")) if state is None: state = utils.saveint(row.get("host_state", 0)) if state > 0: state += 1 # 1 is critical for hosts num_cells = len(cells) if index in groups: group_spec, num_grouped_rows = groups[index] group_hidden = grouped_row_title(index, group_spec, num_grouped_rows, odd, num_cells) odd = "even" if odd == "odd" else "odd" css_classes = [] if is_stale(row): css_classes.append("stale") hide = "" if num_grouped_rows > 0: num_grouped_rows -= 1 if group_hidden: hide = "display:none" if group_hidden is not None and num_grouped_rows == 0: # last row in group css_classes.append("group_end") group_hidden = None css_classes.append("%s%d" % (odd, state)) html.open_tr(class_=["data"] + css_classes, style=hide) if show_checkboxes: render_checkbox_td(view, row, num_cells) for cell in cells: cell.paint(row) html.close_tr() html.close_table() init_rowselect(view)
def page(self): # Show outcome of host validation. Do not validate new hosts errors = None if self._mode == "edit": errors = ( watolib.validate_all_hosts([self._host.name()]).get(self._host.name(), []) + self._host.validation_errors() ) if errors: html.open_div(class_="info") html.open_table(class_="validationerror", boder="0", cellspacing="0", cellpadding="0") html.open_tr() html.open_td(class_="img") html.icon("validation_error") html.close_td() html.open_td() html.open_p() html.h3(_("Warning: This host has an invalid configuration!")) html.open_ul() for error in errors: html.li(error) html.close_ul() html.close_p() if html.form_submitted(): html.br() html.b(_("Your changes have been saved nevertheless.")) html.close_td() html.close_tr() html.close_table() html.close_div() lock_message = "" locked_hosts = watolib.Folder.current().locked_hosts() if locked_hosts: if locked_hosts is True: lock_message = _("Host attributes locked (You cannot edit this host)") elif isinstance(locked_hosts, str): lock_message = locked_hosts if lock_message: html.div(lock_message, class_="info") html.begin_form("edit_host", method="POST") html.prevent_password_auto_completion() basic_attributes = [ # attribute name, valuepec, default value ("host", self._vs_host_name(), self._host.name()), ] if self._is_cluster(): basic_attributes += [ # attribute name, valuepec, default value ( "nodes", self._vs_cluster_nodes(), self._host.cluster_nodes() if self._host else [], ), ] configure_attributes( new=self._mode != "edit", hosts={self._host.name(): self._host} if self._mode != "new" else {}, for_what="host" if not self._is_cluster() else "cluster", parent=watolib.Folder.current(), basic_attributes=basic_attributes, ) if self._mode != "edit": html.set_focus("host") forms.end() html.hidden_fields() html.end_form()
def page(self): html.open_div(id_="ldap") html.open_table() html.open_tr() html.open_td() html.begin_form("connection", method="POST") html.prevent_password_auto_completion() vs = self._valuespec() vs.render_input("connection", self._connection_cfg) vs.set_focus("connection") html.hidden_fields() html.end_form() html.close_td() html.open_td(style="padding-left:10px;vertical-align:top") html.h2(_('Diagnostics')) if not html.request.var('_test') or not self._connection_id: html.show_message( HTML('<p>%s</p><p>%s</p>' % ( _('You can verify the single parts of your ldap configuration using this ' 'dialog. Simply make your configuration in the form on the left side and ' 'hit the "Save & Test" button to execute the tests. After ' 'the page reload, you should see the results of the test here.' ), _('If you need help during configuration or experience problems, please refer ' 'to the <a target="_blank" ' 'href="https://checkmk.com/checkmk_multisite_ldap_integration.html">' 'LDAP Documentation</a>.')))) else: connection = userdb.get_connection(self._connection_id) assert isinstance(connection, LDAPUserConnector) for address in connection.servers(): html.h3("%s: %s" % (_('Server'), address)) with table_element('test', searchable=False) as table: for title, test_func in self._tests(): table.row() try: state, msg = test_func(connection, address) except Exception as e: state = False msg = _('Exception: %s') % html.render_text( "%s" % e) logger.exception("error testing LDAP %s for %s", title, address) if state: img = html.render_icon("success", _('Success')) else: img = html.render_icon("failed", _("Failed")) table.cell(_("Test"), title) table.cell(_("State"), img) table.cell(_("Details"), msg) connection.disconnect() html.close_td() html.close_tr() html.close_table() html.close_div()
def _render_group(self, rows_with_ids, header, view, group_cells, cells, num_columns, show_checkboxes): repeat_heading_every = 20 # in case column_headers is "repeat" if group_cells: self._show_group_header_table(group_cells, rows_with_ids[0][1]) html.open_table(class_="data") odd = "odd" column_headers = view.get("column_headers") if column_headers != "off": self._show_header_line(cells, show_checkboxes) groups, rows_with_ids = calculate_view_grouping_of_services( rows_with_ids, row_group_cells=None) visible_row_number = 0 group_hidden, num_grouped_rows = None, 0 for index, row in rows_with_ids: if view.get("column_headers") == "repeat": if visible_row_number > 0 and visible_row_number % repeat_heading_every == 0: self._show_header_line(cells, show_checkboxes) visible_row_number += 1 odd = "even" if odd == "odd" else "odd" # state = row.get("service_state", row.get("aggr_state")) state = utils.saveint(row.get("service_state")) if state is None: state = utils.saveint(row.get("host_state", 0)) if state > 0: state += 1 # 1 is critical for hosts num_cells = len(cells) if index in groups: group_spec, num_grouped_rows = groups[index] group_hidden = grouped_row_title(index, group_spec, num_grouped_rows, odd, num_cells) odd = "even" if odd == "odd" else "odd" css_classes = [] if is_stale(row): css_classes.append("stale") hide = "" if num_grouped_rows > 0: num_grouped_rows -= 1 if group_hidden: hide = "display:none" if group_hidden is not None and num_grouped_rows == 0: # last row in group css_classes.append("group_end") group_hidden = None css_classes.append("%s%d" % (odd, state)) html.open_tr(class_=["data"] + css_classes, style=hide) if show_checkboxes: render_checkbox_td(view, row, num_cells) for cell in cells: cell.paint(row) html.close_tr() html.close_table() # Don't make rows selectable when no commands can be fired # Ignore "C" display option here. Otherwise the rows will not be selectable # after view reload. if not user.may("general.act"): return init_rowselect(_get_view_name(view))
def show_file(site, host_name, file_name): int_filename = form_file_to_int(file_name) title = _("Logfiles of Host %s: %s") % (host_name, int_filename) breadcrumb = _show_file_breadcrumb(host_name, title) html.header( title, breadcrumb, _show_file_page_menu(breadcrumb, site, host_name, int_filename)) if request.has_var("_ack") and not request.var("_do_actions") == _("No"): do_log_ack(site, host_name, file_name) return try: log_chunks = parse_file(site, host_name, int_filename, hidecontext=request.var("_hidecontext", "no") == "yes") except Exception as e: if config.debug: raise html.show_error(_("Unable to show logfile: <b>%s</b>") % e) html.footer() return if log_chunks is None: html.show_error(_("The logfile does not exist on site.")) html.footer() return if log_chunks == []: html.show_message( _("This logfile contains no unacknowledged messages.")) html.footer() return html.open_div(id_="logwatch") for log in log_chunks: html.open_table(class_="groupheader") html.open_tr() html.td(form_level(log["level"]), class_=form_level(log["level"])) html.td(form_datetime(log["datetime"]), class_="date") html.close_tr() html.close_table() html.open_table(class_=["section"]) for line in log["lines"]: html.open_tr(class_=line["class"]) html.open_td(class_="lines") html.icon_button( analyse_url(site, host_name, int_filename, line["line"]), _("Analyze this line"), "analyze", ) html.write_text(line["line"].replace(" ", " ").replace( "\1", "<br>")) html.close_td() html.close_tr() html.close_table() html.close_div() html.footer()
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): repeat_heading_every = 20 # in case column_headers is "repeat" html.open_table(class_="data table") last_group = None odd = "odd" column = 1 group_open = False num_cells = len(cells) if show_checkboxes: num_cells += 1 if not group_cells and view.get("column_headers") != "off": self._show_header_line(cells, num_columns, show_checkboxes) rows_with_ids = [(row_id(view, row), row) for row in rows] groups, rows_with_ids = calculate_view_grouping_of_services( rows_with_ids, row_group_cells=group_cells) visible_row_number = 0 group_hidden, num_grouped_rows = None, 0 for index, row in rows_with_ids: # Show group header, if a new group begins. But only if grouping # is activated if group_cells: this_group = group_value(row, group_cells) if this_group != last_group: if column != 1: # not a the beginning of a new line for _i in range(column - 1, num_columns): html.td("", class_="gap") html.td("", class_="fillup", colspan=num_cells) html.close_tr() column = 1 group_open = True visible_row_number = 0 # paint group header, but only if it is non-empty header_is_empty = True for cell in group_cells: _tdclass, content = cell.render(row) if content: header_is_empty = False break if not header_is_empty: html.open_tr(class_="groupheader") html.open_td( class_="groupheader", colspan=(num_cells * (num_columns + 2) + (num_columns - 1)), ) html.open_table(class_="groupheader", cellspacing="0", cellpadding="0", border="0") html.open_tr() painted = False for cell in group_cells: if painted: html.td(", ") painted = cell.paint(row) html.close_tr() html.close_table() html.close_td() html.close_tr() odd = "odd" # Table headers if view.get("column_headers") != "off": self._show_header_line(cells, num_columns, show_checkboxes) last_group = this_group # Should we wrap over to a new line? if column >= num_columns + 1: html.close_tr() column = 1 # At the beginning of the line? Beginn new line if column == 1: if view.get("column_headers") == "repeat": if visible_row_number > 0 and visible_row_number % repeat_heading_every == 0: self._show_header_line(cells, num_columns, show_checkboxes) visible_row_number += 1 # In one-column layout we use the state of the service # or host - if available - to color the complete line if num_columns == 1: # render state, if available through whole tr if not row.get("service_description"): state = row.get("host_state", 0) if state > 0: state += 1 # 1 is critical for hosts else: state = row.get("service_state", 0) else: state = 0 if index in groups: group_spec, num_grouped_rows = groups[index] group_hidden = grouped_row_title(index, group_spec, num_grouped_rows, odd, num_cells) odd = "even" if odd == "odd" else "odd" css_classes = [] hide = "" if num_grouped_rows > 0: num_grouped_rows -= 1 if group_hidden: hide = "display:none" if group_hidden is not None and num_grouped_rows == 0: # last row in group css_classes.append("group_end") group_hidden = None odd = "even" if odd == "odd" else "odd" if num_columns > 1: css_classes.append("multicolumn") css_classes += ["%s%d" % (odd, state)] html.open_tr(class_=["data"] + css_classes, style=hide) # Not first columns: Create one empty column as separator else: html.open_td(class_="gap") html.close_td() if show_checkboxes: render_checkbox_td(view, row, num_cells) for cell in cells: cell.paint(row) column += 1 if group_open: for _i in range(column - 1, num_columns): html.td("", class_="gap") html.td("", class_="fillup", colspan=num_cells) html.close_tr() html.close_table() if not user.may("general.act"): return init_rowselect(_get_view_name(view))
def show(self): if not watolib.is_wato_slave_site(): if not config.wato_enabled: html.write_text(_("WATO is disabled.")) return False user_folders = compute_foldertree() # # Render link target selection # selected_topic, selected_target = config.user.load_file( "foldertree", (_('Hosts'), 'allhosts')) topic_views = visuals_by_topic( views.get_permitted_views().items() + dashboard.get_permitted_dashboards().items()) topics = [(t, t) for t, _s in topic_views] html.open_table() html.open_tr() html.td(_('Topic:'), class_="label") html.open_td() html.dropdown("topic", topics, deflt=selected_topic, onchange='cmk.sidebar.wato_tree_topic_changed(this)') html.close_td() html.close_tr() html.open_tr() html.td(_("View:"), class_="label") html.open_td() for topic, view_list in topic_views: targets = [] for t, title, name, is_view in view_list: if config.visible_views and name not in config.visible_views: continue if config.hidden_views and name in config.hidden_views: continue if t == topic: if not is_view: name = 'dashboard|' + name targets.append((name, title)) if topic != selected_topic: default, style = '', 'display:none' else: default, style = selected_target, None html.dropdown( "target_%s" % topic, targets, deflt=default, onchange='cmk.sidebar.wato_tree_target_changed(this)', style=style) html.close_td() html.close_tr() html.close_table() # Now render the whole tree if user_folders: render_tree_folder("wato-hosts", list(user_folders.values())[0], 'cmk.sidebar.wato_tree_click')
def show_file(site, host_name, file_name): int_filename = form_file_to_int(file_name) title = _("Logfiles of Host %s: %s") % (host_name, file_name) html.header(title, make_host_breadcrumb(host_name)) html.begin_context_buttons() html.context_button(_("Services"), services_url(site, host_name), 'services') html.context_button(_("All Logfiles of Host"), html.makeuri([('file', '')])) button_all_logfiles() html.context_button(_("Analyze patterns"), analyse_url(site, host_name, file_name), 'analyze') if html.request.var('_hidecontext', 'no') == 'yes': hide_context_label = _('Show Context') hide_context_param = 'no' hide = True else: hide_context_label = _('Hide Context') hide_context_param = 'yes' hide = False try: log_chunks = parse_file(site, host_name, int_filename, hide) except Exception as e: if config.debug: raise html.end_context_buttons() html.show_error(_("Unable to show logfile: <b>%s</b>") % e) html.footer() return if log_chunks is None: html.end_context_buttons() html.show_error(_("The logfile does not exist.")) html.footer() return if log_chunks == []: html.end_context_buttons() html.show_message( _("This logfile contains no unacknowledged messages.")) html.footer() return ack_button(site, host_name, int_filename) html.context_button(hide_context_label, html.makeuri([('_hidecontext', hide_context_param)])) html.end_context_buttons() html.open_div(id_="logwatch") for log in log_chunks: html.open_div(class_=["chunk"]) html.open_table(class_=["section"]) html.open_tr() html.td(form_level(log['level']), class_=form_level(log['level'])) html.td(form_datetime(log['datetime']), class_="date") html.close_tr() html.close_table() for line in log['lines']: html.open_p(class_=line['class']) html.icon_button( analyse_url(site, host_name, file_name, line['line']), _("Analyze this line"), "analyze") html.write_text(line['line'].replace(" ", " ").replace( "\1", "<br>")) html.close_p() html.close_div() html.close_div() html.footer()
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_master_control_site( self, site_id: sites.SiteId, site_status_info: Dict[sites.SiteId, List], items: List[Tuple[str, str]], ) -> None: site_state = sites.states().get(site_id) if not site_state: html.show_error(_("Site state is unknown")) return if site_state["state"] == "dead": html.show_error(site_state["exception"]) return if site_state["state"] == "disabled": html.show_message(_("Site is disabled")) return if site_state["state"] == "unknown": if site_state.get("exception"): html.show_error(site_state["exception"]) else: html.show_error(_("Site state is unknown")) return is_cmc = site_state["program_version"].startswith("Check_MK ") try: site_info = site_status_info[site_id] except KeyError: html.show_error(_("Site state is unknown")) return html.open_table(class_="master_control") for i, (colname, title) in enumerate(items): # Do not show event handlers on Checkmk Micro Core if is_cmc and title == _("Event handlers"): continue if not is_cmc and title == _("Alert handlers"): continue colvalue = site_info[i] url = makeactionuri_contextless( request, transactions, [ ("site", site_id), ("switch", colname), ("state", "%d" % (1 - colvalue)), ], filename="switch_master_state.py", ) onclick = ( "cmk.ajax.get_url('%s', cmk.utils.update_contents, 'snapin_master_control')" % url ) html.open_tr() html.td(title, class_="left") html.open_td() html.toggle_switch( enabled=colvalue, help_txt=_("Switch '%s' to '%s'") % (title, _("off") if colvalue else _("on")), onclick=onclick, ) html.close_td() html.close_tr() html.close_table()
def show(self): hosts = self._get_hosts() num_hosts = len(hosts) if num_hosts > 900: html.write_text( _("Sorry, I will not display more than 900 hosts.")) return # Choose smallest square number large enough # to show all hosts n = 1 while n * n < num_hosts: n += 1 rows = int(num_hosts / n) lastcols = num_hosts % n if lastcols > 0: rows += 1 # Calculate cell size (Automatic sizing with 100% does not work here) # - Get cell spacing: 1px between each cell # - Substract the cell spacing for each column from the total width # - Then divide the total width through the number of columns # - Then get the full-digit width of the cell and summarize the rest # to be substracted from the cell width # This is not a 100% solution but way better than having no links cell_spacing = 1 cell_size = int((snapin_width - cell_spacing * (n + 1)) / n) cell_size, cell_size_rest = divmod(cell_size, 1) style = 'width:%spx' % (snapin_width - n * cell_size_rest) html.open_table(class_=["content_center", "hostmatrix"], cellspacing="0", style=["border-collapse:collapse;", style]) col = 1 row = 1 for site, host, state, has_been_checked, worstsvc, downtimedepth in sorted( hosts): if col == 1: html.open_tr() if downtimedepth > 0: s = "d" elif not has_been_checked: s = "p" elif worstsvc == 2 or state == 1: s = "2" elif worstsvc == 3 or state == 2: s = "3" elif worstsvc == 1: s = "1" else: s = "0" url = "view.py?view_name=host&site=%s&host=%s" % ( html.urlencode(site), html.urlencode(host)) html.open_td(class_=["state", "state%s" % s]) html.a( '', href=url, title=host, target="main", style=["width:%spx;" % cell_size, "height:%spx;" % cell_size]) html.close_td() if col == n or (row == rows and n == lastcols): html.open_tr() col = 1 row += 1 else: col += 1 html.close_table()
def _output_analysed_ruleset(self, all_rulesets, rulespec, svc_desc_or_item, svc_desc, known_settings=None): if known_settings is None: known_settings = self._PARAMETERS_UNKNOWN def rule_url(rule): return watolib.folder_preserving_link([ ('mode', 'edit_rule'), ('varname', varname), ('rule_folder', rule.folder.path()), ('rulenr', rule.index()), ('host', self._hostname), ('item', watolib.mk_repr(svc_desc_or_item) if svc_desc_or_item else ''), ('service', watolib.mk_repr(svc_desc) if svc_desc else ''), ]) varname = rulespec.name valuespec = rulespec.valuespec url = watolib.folder_preserving_link([ ('mode', 'edit_ruleset'), ('varname', varname), ('host', self._hostname), ('item', watolib.mk_repr(svc_desc_or_item)), ('service', watolib.mk_repr(svc_desc)), ]) forms.section(html.render_a(rulespec.title, url)) ruleset = all_rulesets.get(varname) setting, rules = ruleset.analyse_ruleset(self._hostname, svc_desc_or_item, svc_desc) html.open_table(class_="setting") html.open_tr() html.open_td(class_="reason") # Show reason for the determined value if len(rules) == 1: rule_folder, rule_index, rule = rules[0] url = rule_url(rule) html.a(_("Rule %d in %s") % (rule_index + 1, rule_folder.title()), href=rule_url(rule)) elif len(rules) > 1: html.a("%d %s" % (len(rules), _("Rules")), href=url) else: html.i(_("Default Value")) html.close_td() # Show the resulting value or factory setting html.open_td( class_=["settingvalue", "used" if len(rules) > 0 else "unused"]) if isinstance(known_settings, dict) and "tp_computed_params" in known_settings: computed_at = known_settings["tp_computed_params"]["computed_at"] html.write_text( _("Timespecific parameters computed at %s") % cmk.utils.render.date_and_time(computed_at)) html.br() known_settings = known_settings["tp_computed_params"]["params"] # In some cases we now the settings from a check_mk automation if known_settings is self._PARAMETERS_OMIT: return # Special handling for logwatch: The check parameter is always None. The actual # patterns are configured in logwatch_rules. We do not have access to the actual # patterns here but just to the useless "None". In order not to complicate things # we simply display nothing here. elif varname == "logwatch_rules": pass elif known_settings is not self._PARAMETERS_UNKNOWN: try: html.write(valuespec.value_to_text(known_settings)) except Exception as e: if config.debug: raise html.write_text( _("Invalid parameter %r: %s") % (known_settings, e)) else: # For match type "dict" it can be the case the rule define some of the keys # while other keys are taken from the factory defaults. We need to show the # complete outcoming value here. if rules and ruleset.match_type() == "dict": if rulespec.factory_default is not watolib.Rulespec.NO_FACTORY_DEFAULT \ and rulespec.factory_default is not watolib.Rulespec.FACTORY_DEFAULT_UNUSED: fd = rulespec.factory_default.copy() fd.update(setting) setting = fd if valuespec and not rules: # show the default value if rulespec.factory_default is watolib.Rulespec.FACTORY_DEFAULT_UNUSED: # Some rulesets are ineffective if they are empty html.write_text(_("(unused)")) elif rulespec.factory_default is not watolib.Rulespec.NO_FACTORY_DEFAULT: # If there is a factory default then show that one setting = rulespec.factory_default html.write(valuespec.value_to_text(setting)) elif ruleset.match_type() in ("all", "list"): # Rulesets that build lists are empty if no rule matches html.write_text(_("(no entry)")) else: # Else we use the default value of the valuespec html.write( valuespec.value_to_text(valuespec.default_value())) # We have a setting elif valuespec: if ruleset.match_type() == "all": html.write(", ".join( [valuespec.value_to_text(e) for e in setting])) else: html.write(valuespec.value_to_text(setting)) # Binary rule, no valuespec, outcome is True or False else: icon_name = "rule_%s%s" % ("yes" if setting else "no", "_off" if not rules else '') html.icon(title=_("yes") if setting else _("no"), icon=icon_name) html.close_td() html.close_tr() html.close_table()
def _show_test_row(self, table, test_id, test_results_by_site, site_ids): table.row() table.cell(_("Actions"), css="buttons", sortable=False) html.icon_button( None, _("Toggle result details"), "toggle_details", onclick="cmk.wato.toggle_container('test_result_details_%s')" % test_id) worst_result = sorted(test_results_by_site["site_results"].values(), key=lambda result: result.status)[0] # Disabling of test in total is_test_disabled = self._is_test_disabled(test_id) if is_test_disabled: html.icon_button( html.makeactionuri([ ("_do", "enable"), ("_test_id", worst_result.test_id), ]), _("Reenable this test"), "enable_test", ) else: html.icon_button( html.makeactionuri([ ("_do", "disable"), ("_test_id", worst_result.test_id), ]), _("Disable this test"), "disable_test", ) # assume all have the same test meta information (title, help, ...) table.cell(_("Title"), css="title " + "stale" if is_test_disabled else "") html.write_text(test_results_by_site["test"]["title"]) # Now loop all sites to display their results for site_id in site_ids: if is_test_disabled: table.cell(site_id, "") table.cell("", "") continue result = test_results_by_site["site_results"].get(site_id) if result is None: table.cell(site_id, css="state state-1") table.cell("", css="buttons") continue is_acknowledged = self._is_acknowledged(result) if is_acknowledged or result.status == -1: css = "state stale" else: css = "state state%d" % result.status table.cell(site_id, css=css) html.open_div(title=result.text) html.write_text(result.status_name()) html.close_div() table.cell("", css="buttons") if result.status != 0: if is_acknowledged: html.icon_button( html.makeactionuri([ ("_do", "unack"), ("_site_id", result.site_id), ("_status_id", result.status), ("_test_id", result.test_id), ]), _("Unacknowledge this test result for site %s") % site_id, "unacknowledge_test", ) else: html.icon_button( html.makeactionuri([ ("_do", "ack"), ("_site_id", result.site_id), ("_status_id", result.status), ("_test_id", result.test_id), ]), _("Acknowledge this test result for site %s") % site_id, "acknowledge_test", ) else: html.write("") # Add toggleable notitication context table.row(class_="ac_test_details hidden", id_="test_result_details_%s" % test_id) table.cell(colspan=2 + 2 * len(site_ids)) html.write_text(test_results_by_site["test"]["help"]) if not is_test_disabled: html.open_table() for site_id in site_ids: result = test_results_by_site["site_results"].get(site_id) if result is None: continue html.open_tr() html.td(escaping.escape_attribute(site_id)) html.td("%s: %s" % (result.status_name(), result.text)) html.close_tr() html.close_table() # This dummy row is needed for not destroying the odd/even row highlighting table.row(class_="hidden")
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_=["tacticaloverview"], cellspacing="2", cellpadding="0", border="0") show_stales = self.parameters()["show_stale"] and 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"), class_="show_more_mode") html.th( html.render_span(_("Unhandled"), class_="more") + html.render_span(_("Unhandled p."), class_="less") ) 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 = makeuri_contextless(request, 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 = makeuri_contextless( request, getattr(row.views, ty) + context_vars, filename="view.py", ) html.open_td( class_=[ td_class, "states prob" if value != 0 else None, "show_more_mode" if ty == "handled" else "basic", ] ) link(str(value), url) html.close_td() if show_stales and has_stale_objects: if row.views.stale: url = makeuri_contextless( request, 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 page(self): # Show outcome of host validation. Do not validate new hosts errors = None if self._mode != "edit": watolib.Folder.current().show_breadcrump() else: errors = watolib.validate_all_hosts([self._host.name()]).get( self._host.name(), []) + self._host.validation_errors() if errors: html.open_div(class_="info") html.open_table(class_="validationerror", boder=0, cellspacing=0, cellpadding=0) html.open_tr() html.open_td(class_="img") html.icon(title=None, icon="validation_error") html.close_td() html.open_td() html.open_p() html.h3(_("Warning: This host has an invalid configuration!")) html.open_ul() for error in errors: html.li(error) html.close_ul() html.close_p() if html.form_submitted(): html.br() html.b(_("Your changes have been saved nevertheless.")) html.close_td() html.close_tr() html.close_table() html.close_div() lock_message = "" if watolib.Folder.current().locked_hosts(): if watolib.Folder.current().locked_hosts() is True: lock_message = _( "Host attributes locked (You cannot edit this host)") else: lock_message = watolib.Folder.current().locked_hosts() if len(lock_message) > 0: html.div(lock_message, class_="info") html.begin_form("edit_host", method="POST") html.prevent_password_auto_completion() forms.header(_("General Properties")) self._show_host_name() # Cluster: nodes if self._is_cluster(): forms.section(_("Nodes")) self._vs_cluster_nodes().render_input( "nodes", self._host.cluster_nodes() if self._host else []) html.help( _('Enter the host names of the cluster nodes. These ' 'hosts must be present in WATO. ')) configure_attributes( new=self._mode != "edit", hosts={self._host.name(): self._host} if self._mode != "new" else {}, for_what="host" if not self._is_cluster() else "cluster", parent=watolib.Folder.current()) forms.end() if not watolib.Folder.current().locked_hosts(): html.button("services", _("Save & go to Services"), "submit") html.button("save", _("Save & Finish"), "submit") if not self._is_cluster(): html.button("diag_host", _("Save & Test"), "submit") html.hidden_fields() html.end_form()