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')) dashboard.load_dashboards() topic_views = visuals_by_topic( views.get_permitted_views().items() + dashboard.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", user_folders.values()[0], 'cmk.sidebar.wato_tree_click')
def render_bi_availability(view: "View", aggr_rows: 'Rows') -> None: config.user.need_permission("general.see_availability") av_mode = html.request.get_ascii_input_mandatory("av_mode", "availability") avoptions = get_availability_options_from_url("bi") title = view_title(view.spec) if av_mode == "timeline": title = _("Timeline of") + " " + title else: title = _("Availability of") + " " + title if html.output_format != "csv_export": html.body_start(title) breadcrumb = view.breadcrumb() breadcrumb.append( BreadcrumbItem( title=title, url=breadcrumb[-1].url + "&mode=availability", )) html.top_heading(title, breadcrumb) html.begin_context_buttons() html.toggle_button("avoptions", False, "painteroptions", _("Configure details of the report")) html.context_button(_("Status View"), html.makeuri([("mode", "status")]), "status") if config.reporting_available() and config.user.may("general.reporting"): html.context_button(_("Export as PDF"), html.makeuri([], filename="report_instant.py"), "report") if config.user.may("general.csv_export"): html.context_button(_("Export as CSV"), html.makeuri([("output_format", "csv_export")]), "download_csv") if av_mode == "timeline": html.context_button(_("Availability"), html.makeuri([("av_mode", "availability")]), "availability") elif len(aggr_rows) == 1: aggr_name = aggr_rows[0]["aggr_name"] aggr_group = aggr_rows[0]["aggr_group"] timeline_url = html.makeuri([("av_mode", "timeline"), ("av_aggr_name", aggr_name), ("av_aggr_group", aggr_group)]) html.context_button(_("Timeline"), timeline_url, "timeline") html.end_context_buttons() avoptions = render_availability_options("bi") if not html.has_user_errors(): logrow_limit = avoptions["logrow_limit"] if logrow_limit == 0: livestatus_limit = None else: livestatus_limit = (len(aggr_rows) * logrow_limit) spans: List[AVSpan] = [] # iterate all aggregation rows timewarpcode = HTML() timewarp = html.request.get_integer_input("timewarp") has_reached_logrow_limit = False timeline_containers, fetched_rows = availability.get_timeline_containers( aggr_rows, avoptions, timewarp, livestatus_limit + 1 if livestatus_limit is not None else None) if livestatus_limit and fetched_rows > livestatus_limit: has_reached_logrow_limit = True for timeline_container in timeline_containers: tree = timeline_container.aggr_tree these_spans = timeline_container.timeline timewarp_tree_state = timeline_container.timewarp_state spans += these_spans # render selected time warp for the corresponding aggregation row (should be matched by only one) if timewarp and timewarp_tree_state: state, assumed_state, node, _subtrees = timewarp_tree_state eff_state = state if assumed_state is not None: eff_state = assumed_state row = { "aggr_tree": tree, "aggr_treestate": timewarp_tree_state, "aggr_state": state, # state disregarding assumptions "aggr_assumed_state": assumed_state, # is None, if there are no assumptions "aggr_effective_state": eff_state, # is assumed_state, if there are assumptions, else real state "aggr_name": node["title"], "aggr_output": eff_state["output"], "aggr_hosts": node["reqhosts"], "aggr_function": node["func"], "aggr_group": html.request.var("aggr_group"), } renderer = bi.FoldableTreeRendererTree( row, omit_root=False, expansion_level=config.user.bi_expansion_level, only_problems=False, lazy=False) tdclass, htmlcode = renderer.css_class(), renderer.render() with html.plugged(): # TODO: SOMETHING IS WRONG IN HERE (used to be the same situation in original code!) # FIXME: WHAT is wrong in here?? html.open_h3() # render icons for back and forth button_back_shown = False button_forth_shown = False if int(these_spans[0]["from"]) == timewarp: html.disabled_icon_button("back_off") button_back_shown = True previous_span = None for span in these_spans: if not button_back_shown and int( span["from"]) == timewarp and previous_span is not None: html.icon_button( html.makeuri([("timewarp", str(int(previous_span["from"])))]), _("Jump one phase back"), "back") button_back_shown = True # Multiple followup spans can have the same "from" time # We only show one forth-arrow with an actual time difference elif not button_forth_shown and previous_span and int( previous_span["from"]) == timewarp and int( span["from"]) != timewarp: html.icon_button(html.makeuri([("timewarp", str(int(span["from"])))]), _("Jump one phase forth"), "forth") button_forth_shown = True previous_span = span if not button_forth_shown: html.disabled_icon_button("forth_off") html.write_text(" ") html.icon_button(html.makeuri([("timewarp", "")]), _("Close Timewarp"), "closetimewarp") html.write_text("%s %s" % (_("Timewarp to "), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timewarp)))) html.close_h3() html.open_table(class_=["data", "table", "timewarp"]) html.open_tr(class_=["data", "odd0"]) html.open_td(class_=tdclass) html.write_html(htmlcode) html.close_td() html.close_tr() html.close_table() timewarpcode += html.drain() # Note: 'spans_by_object' returns two arguments which are used by # all availability views but not by BI. There we have to take # only complete aggregations av_rawdata = availability.spans_by_object(spans, None)[0] av_data = availability.compute_availability("bi", av_rawdata, avoptions) # If we abolish the limit we have to fetch the data again # with changed logrow_limit = 0, which means no limit if has_reached_logrow_limit: text = _( "Your query matched more than %d log entries. " "<b>Note:</b> The shown data does not necessarily reflect the " "matched entries and the result might be incomplete. ") % avoptions["logrow_limit"] text += html.render_a(_('Repeat query without limit.'), html.makeuri([("_unset_logrow_limit", "1")])) html.show_warning(text) if html.output_format == "csv_export" and config.user.may("general.csv_export"): _output_csv("bi", av_mode, av_data, avoptions) return html.write(timewarpcode) do_render_availability("bi", av_rawdata, av_data, av_mode, None, avoptions) html.bottom_footer() html.body_end()
def _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, serviceinfo["item"], 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, serviceinfo["item"], 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, serviceinfo["item"], 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, None, 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 _render_headers(self, actions_enabled, actions_visible, empty_columns): if self.options["omit_headers"]: return table_id = self.id html.open_tr() first_col = True for nr, (header, css, help_txt, sortable) in enumerate(self.headers): if self.options["omit_empty_columns"] and empty_columns[nr]: continue text = header if help_txt: header = '<span title="%s">%s</span>' % ( html.attrencode(help_txt), header) css_class = "header_%s" % css if css else None if not self.options["sortable"] or not sortable: html.open_th(class_=css_class) else: reverse = 0 sort = html.request.var('_%s_sort' % table_id) if sort: sort_col, sort_reverse = map(int, sort.split(',', 1)) if sort_col == nr: reverse = 1 if sort_reverse == 0 else 0 action_uri = html.makeactionuri([('_%s_sort' % table_id, '%d,%d' % (nr, reverse))]) html.open_th(class_=["sort", css_class], title=_("Sort by %s") % text, onclick="location.href='%s'" % action_uri) # Add the table action link if first_col: first_col = False if actions_enabled: if not header: header = " " # Fixes layout problem with white triangle if actions_visible: state = '0' help_txt = _('Hide table actions') img = 'table_actions_on' else: state = '1' help_txt = _('Display table actions') img = 'table_actions_off' html.open_div(class_=["toggle_actions"]) html.icon_button(html.makeuri([('_%s_actions' % table_id, state)]), help_txt, img, cssclass='toggle_actions') html.open_span() html.write(header) html.close_span() html.close_div() else: html.write(header) else: html.write(header) html.close_th() html.close_tr()
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: result = sites.live().query_summed_stats(query) 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": pie_diameter / 2, "y": pie_diameter / 2, "d": pie_diameter, 'p': '\n'.join(pie_parts) })
def write_line(left, right): html.open_tr() html.td(left, class_="left") html.td(html.render_strong(right), class_="right") html.close_tr()
def show_job_row_headers(cls): html.open_tr() for header in cls.get_headers(): html.th(header) html.close_tr()
def _render_headers(self, actions_enabled: bool, actions_visible: bool, empty_columns: List[bool]) -> None: if self.options["omit_headers"]: return table_id = self.id html.open_tr() first_col = True for nr, header in enumerate(self.headers): if self.options["omit_empty_columns"] and empty_columns[nr]: continue if header.help_txt: header_title: Union[int, HTML, str] = html.render_span(header.title, title=header.help_txt) else: header_title = header.title if not isinstance(header.css, list): css_class: 'CSSSpec' = [header.css] else: css_class = header.css assert isinstance(css_class, list) css_class = [("header_%s" % c) for c in css_class if c is not None] if not self.options["sortable"] or not header.sortable: html.open_th(class_=css_class) else: css_class.insert(0, "sort") reverse = 0 sort = html.request.get_ascii_input('_%s_sort' % table_id) if sort: sort_col, sort_reverse = map(int, sort.split(',', 1)) if sort_col == nr: reverse = 1 if sort_reverse == 0 else 0 action_uri = html.makeactionuri([('_%s_sort' % table_id, '%d,%d' % (nr, reverse))]) html.open_th(class_=css_class, title=_("Sort by %s") % header.title, onclick="location.href='%s'" % action_uri) # Add the table action link if first_col: first_col = False if actions_enabled: if not header_title: header_title = " " # Fixes layout problem with white triangle if actions_visible: state = '0' help_txt = _('Hide table actions') img = 'table_actions_on' else: state = '1' help_txt = _('Display table actions') img = 'table_actions_off' html.open_div(class_=["toggle_actions"]) html.icon_button(makeuri(request, [('_%s_actions' % table_id, state)]), help_txt, img, cssclass='toggle_actions') html.open_span() html.write(header_title) html.close_span() html.close_div() else: html.write(header_title) else: html.write(header_title) html.close_th() html.close_tr()
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 config.user.may("general.act"): return init_rowselect(_get_view_name(view))
def write_line(left, right, show_more): html.open_tr(class_="show_more_mode" if show_more else "basic") html.td(left, class_="left") html.td(html.render_strong(right), class_="right") html.close_tr()
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 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_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_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_master_control_site(self, site_id, site_status_info, items): # type: (sites.SiteId, Dict[sites.SiteId, List], 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 Check_MK Micro Core if is_cmc and title == _("Event handlers"): continue if not is_cmc and title == _("Alert handlers"): continue colvalue = site_info[i] url = html.makeactionuri_contextless( [ ("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_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 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("%s" % rendered[1][1]) html.close_td() html.open_td(class_=["tr", rendered[2][0]]) html.write("%s" % rendered[2][1]) html.close_td() html.close_tr() html.open_tr() html.open_td(colspan=2, class_=["center", rendered[0][0]]) html.write("%s" % 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("%s" % cont) html.close_td() html.close_tr() html.open_tr() html.open_td(class_=["bl", rendered[3][0]]) html.write("%s" % rendered[3][1]) html.close_td() html.open_td(class_=["br", rendered[4][0]]) html.write("%s" % 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 config.user.may("general.act"): return init_rowselect(_get_view_name(view))
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 = ParentScanSettings(**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.text_input("timeout", str(self._settings.timeout), size=2, cssclass="number") 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.text_input("probes", str(self._settings.probes), size=2, cssclass="number") 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.text_input("max_ttl", str(self._settings.max_ttl), size=2, cssclass="number") 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.text_input("ping_probes", str(self._settings.ping_probes), size=2, cssclass="number") html.close_td() html.close_tr() html.close_table() # Configuring parent forms.section(_("Configuration")) html.checkbox( "force_explicit", deflt=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", default_value=self._settings.alias) forms.end() # Start button html.button("_start", _("Start")) html.hidden_fields() html.end_form()
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 config.user.may("general.act"): return init_rowselect(_get_view_name(view))
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( make_confirm_link( url=makeactionuri(request, transactions, [(ActionHandler.stop_job_var, job_id)]), message=_("Stop job %s%s?") % (job_id, cls._get_extra_info(job_status)), ), _("Stop this job"), "disable_test", ) if job_status.get("may_delete"): html.icon_button( make_confirm_link( url=makeactionuri( request, transactions, [(ActionHandler.delete_job_var, job_id)]), message=_("Delete job %s%s?") % (job_id, cls._get_extra_info(job_status)), ), _("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"), str(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 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(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(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(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.open_td(class_=tdclass) html.write(content) html.close_td() if len(cells) > 2: html.close_tr() if len(cells) > 2: html.close_table() html.close_td() html.close_tr() html.close_table()
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 = 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 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 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) # type: List[int] 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="%d" % pie_diameter, height="%d" % 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 = [] # type: List[Tuple] 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 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.button("_save", _("Save")) html.button("_test", _("Save & Test")) 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) 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(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 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 html.request.has_var( '_ack') and not html.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=html.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 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"): EmailAddress().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_version.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 uid = None if self._user_id is None else UserId(self._user_id) if (self._is_new_user or (config.user_may(uid, 'general.edit_profile') and config.user_may(uid, '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")) is_member_of_at_least_one = False for role_id, role in sorted(self._roles.items(), key=lambda x: (x[1]["alias"], x[0])): 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")) user_np = self._user.get("notification_period") if not isinstance(user_np, str): raise Exception("invalid notification period %r" % (user_np, )) choices: Choices = [(id_, "%s" % (tp["alias"])) for (id_, tp) in self._timeperiods.items()] html.dropdown("notification_period", choices, deflt=user_np, 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() if self._is_new_user: html.set_focus("user_id") else: html.set_focus("alias") html.hidden_fields() html.end_form()
def section_close() -> None: if g_section_open: html.close_td() html.close_tr()
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 foldable_container( *, treename: str, id_: str, isopen: bool, title: HTMLContent, indent: Union[str, None, bool] = True, first: bool = False, icon: Optional[str] = None, fetch_url: Optional[str] = None, title_url: Optional[str] = None, title_target: Optional[str] = None, padding: int = 15, ) -> Iterator[bool]: isopen = user.get_tree_state(treename, id_, isopen) onclick = foldable_container_onclick(treename, id_, fetch_url) img_id = foldable_container_img_id(treename, id_) container_id = foldable_container_id(treename, id_) html.open_div(class_=["foldable", "open" if isopen else "closed"]) if isinstance(title, HTML): # custom HTML code html.write_text(title) else: html.open_b(class_=["treeangle", "title"], onclick=None if title_url else onclick) if title_url: html.a(title, href=title_url, target=title_target) else: html.write_text(title) html.close_b() if icon: html.img( id_=img_id, class_=[ "treeangle", "title", # Although foldable_sidebar is given via the argument icon it should not be # displayed as big as an icon. "icon" if icon != "foldable_sidebar" else None, "open" if isopen else "closed", ], src=theme.detect_icon_path(icon, "icon_"), onclick=onclick) else: html.img(id_=img_id, class_=["treeangle", "open" if isopen else "closed"], src=theme.url("images/tree_closed.svg"), onclick=onclick) if indent != "form" or not isinstance(title, HTML): html.br() indent_style = "padding-left: %dpx; " % (padding if indent else 0) if indent == "form": html.close_td() html.close_tr() html.close_table() indent_style += "margin: 0; " html.open_ul(id_=container_id, class_=["treeangle", "open" if isopen else "closed"], style=indent_style) yield isopen html.close_ul() html.close_div()
def _output_analysed_ruleset(self, all_rulesets, rulespec, service, 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(service) if service else ''), ]) varname = rulespec.name valuespec = rulespec.valuespec url = watolib.folder_preserving_link([ ('mode', 'edit_ruleset'), ('varname', varname), ('host', self._hostname), ('item', watolib.mk_repr(service)), ]) forms.section(html.render_a(rulespec.title, url)) ruleset = all_rulesets.get(varname) setting, rules = ruleset.analyse_ruleset(self._hostname, service) 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_file(site, host_name, file_name): int_filename = form_file_to_int(file_name) html.header(_("Logfiles of Host %s: %s") % (host_name, file_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 elif log_chunks == []: html.end_context_buttons() html.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()