def _show_link( self, url: str, onclick: Optional[str], target: Optional[str], icon: Icon, title: str ) -> None: html.open_a(href=url, onclick=onclick, target=target) html.icon(icon or "trans") html.span(title) html.close_a()
def _create_status_box( self, site_ids: Collection[livestatus.SiteId], css_class: str, site_status: str, ): html.open_div(class_="spacertop") html.open_div(class_=css_class) message_template = ungettext("%d site is %s.", "%d sites are %s.", len(site_ids)) message = message_template % (len(site_ids), site_status) tooltip_template = ungettext( "Associated hosts, services and events are not included " "in the Tactical Overview. The %s site is %s.", "Associated hosts, services and events are not included " "in the Tactical Overview. The %s sites are %s.", len(site_ids), ) tooltip = tooltip_template % (site_status, ", ".join(site_ids)) if user.may("wato.sites"): url = makeuri_contextless(request, [("mode", "sites")], filename="wato.py") html.icon_button(url, tooltip, "sites", target="main") html.a(message, target="main", href=url) else: html.icon("sites", tooltip) html.write_text(message) html.close_div() html.close_div()
def _show_add_snapin_button(self) -> None: html.open_div(id_="add_snapin") html.open_a(href=makeuri_contextless(request, [], filename="sidebar_add_snapin.py"), target="main") html.icon("add", title=_("Add elements to your sidebar")) html.close_a() html.close_div()
def page(self): with table_element() as table: for index, connection in enumerate(load_connection_config()): table.row() table.cell(_("Actions"), css=["buttons"]) edit_url = folder_preserving_link([("mode", "edit_ldap_connection"), ("id", connection["id"])]) delete_url = make_confirm_link( url=make_action_link([("mode", "ldap_config"), ("_delete", index)]), message= _("Do you really want to delete the LDAP connection <b>%s</b>?" ) % connection["id"], ) drag_url = make_action_link([("mode", "ldap_config"), ("_move", index)]) clone_url = folder_preserving_link([ ("mode", "edit_ldap_connection"), ("clone", connection["id"]) ]) html.icon_button(edit_url, _("Edit this LDAP connection"), "edit") html.icon_button(clone_url, _("Create a copy of this LDAP connection"), "clone") html.element_dragger_url("tr", base_url=drag_url) html.icon_button(delete_url, _("Delete this LDAP connection"), "delete") table.cell("", css=["narrow"]) if connection.get("disabled"): html.icon( "disabled", _("This connection is currently not being used for synchronization." ), ) else: html.empty_icon_button() table.cell(_("ID"), connection["id"]) if cmk_version.is_managed_edition(): table.cell(_("Customer"), managed.get_customer_name(connection)) table.cell(_("Description")) url = connection.get("docu_url") if url: html.icon_button( url, _("Context information about this connection"), "url", target="_blank") html.write_text(" ") html.write_text(connection["description"])
def _show_item(self, item: TopicMenuItem) -> None: html.open_li(class_="show_more_mode" if item.is_show_more else None) html.open_a( href=item.url, target=item.target, onclick="cmk.popup_menu.close_popup()", ) if user.get_attribute("icons_per_item"): html.icon(item.icon or "dash") self._show_item_title(item) html.close_a() html.close_li()
def show(self): html.open_div(class_="mainmenu") for item in self._items: if not item.may_see(): continue html.open_a(href=item.get_url(), onfocus="if (this.blur) this.blur();") html.icon(item.icon, item.title) html.div(item.title, class_="title") html.div(item.description, class_="subtitle") html.close_a() html.close_div()
def _show_items(self, topic_id: str, topic: TopicMenuTopic) -> None: html.open_ul() for item in sorted(topic.items, key=lambda g: g.sort_index): self._show_item(item) html.open_li(class_="show_all_items") html.open_a(href="", onclick="cmk.popup_menu.mega_menu_show_all_items('%s')" % topic_id) if user.get_attribute("icons_per_item"): html.icon("trans") html.write_text(_("Show all")) html.close_a() html.close_li() html.close_ul()
def _show_diagnose_output(self): if not request.var("_save"): html.show_message( _( "You can diagnose the connection to a specific host using this dialog. " "You can either test whether your current configuration is still working " "or investigate in which ways a host can be reached. Simply configure the " "connection options you like to try on the right side of the screen and " 'press the "Test" button. The results will be displayed here.' ) ) return if user_errors: html.show_user_errors() return # TODO: Insert any vs_host valuespec validation # These tests can be called with invalid valuespec settings... # TODO: Replace hard coded icon paths with dynamic ones to old or new theme for ident, title in ModeDiagHost.diag_host_tests(): html.h3(title) html.open_table(class_=["data", "test"]) html.open_tr(class_=["data", "odd0"]) html.open_td(class_="icons") html.open_div() html.icon("reload", id_="%s_img" % ident) html.open_a(href="") html.icon( "reload", title=_("Retry this test"), cssclass="retry", id_="%s_retry" % ident ) html.close_a() html.close_div() html.close_td() html.open_td() html.div("", class_="log", id="%s_log" % ident) html.close_td() html.close_tr() html.close_table() html.javascript( "cmk.host_diagnose.start_test(%s, %s, %s)" % ( json.dumps(ident), json.dumps(self._hostname), json.dumps(transactions.fresh_transid()), ) )
def _show_link( self, entry: PageMenuEntry, url: Optional[str], onclick: Optional[str], target: Optional[str], ) -> None: html.open_a( href=url, onclick=onclick, target=target, id_=("menu_suggestion_%s" % entry.name if entry.name else None), ) html.icon(entry.icon_name or "trans") html.write_text(entry.shortcut_title or entry.title) html.close_a()
def _show_topic_title(self, menu_id: str, topic_id: str, topic: TopicMenuTopic) -> None: html.open_h2() html.open_a( class_="show_all_topics", href="", onclick="cmk.popup_menu.mega_menu_show_all_topics('%s')" % topic_id, ) html.icon(icon="collapse_arrow", title=_("Show all %s topics") % menu_id) html.close_a() if not user.get_attribute("icons_per_item") and topic.icon: html.icon(topic.icon) html.span(topic.title) html.close_h2()
def _change_table(self): with table_element( "changes", title=_("Pending changes (%s)") % self._get_amount_changes(), sortable=False, searchable=False, css="changes", limit=None, empty_text=_("Currently there are no changes to activate."), foldable=Foldable.FOLDABLE_STATELESS, ) as table: for _change_id, change in reversed(self._changes): css = [] if self._is_foreign(change): css.append("foreign") if not user.may("wato.activateforeign"): css.append("not_permitted") table.row(css=[" ".join(css)]) table.cell("", css=["buttons"]) rendered = render_object_ref_as_icon(change["object"]) if rendered: html.write_html(rendered) table.cell(_("Time"), render.date_and_time(change["time"]), css=["narrow nobr"]) table.cell(_("User"), css=["narrow nobr"]) html.write_text(change["user_id"] if change["user_id"] else "") if self._is_foreign(change): html.icon("foreign_changes", _("This change has been made by another user")) # Text is already escaped (see ActivateChangesWriter._add_change_to_site). We have # to handle this in a special way because of the SiteChanges file format. Would be # cleaner to transport the text type (like AuditLogStore is doing it). table.cell(_("Change"), HTML(change["text"])) table.cell(_("Affected sites"), css=["affected_sites"]) if self._affects_all_sites(change): html.write_text("<i>%s</i>" % _("All sites")) else: html.write_text(", ".join(sorted( change["affected_sites"])))
def _show_sidebar_head(self): html.open_div(id_="side_header") html.open_a( href=user.start_url or active_config.start_url, target="main", title=_("Go to main page"), ) _render_header_icon() html.close_a() html.close_div() MainMenuRenderer().show() html.open_div(id_="side_fold", title=_("Toggle the sidebar"), onclick="cmk.sidebar.toggle_sidebar()") html.icon("sidebar_folded", class_=["folded"]) html.icon("sidebar") if not user.get_attribute("nav_hide_icons_title"): html.div(_("Sidebar")) html.close_div()
def page(self): role_list = sorted(userdb_utils.load_roles().items(), key=lambda a: (a[1]["alias"], a[0])) for section in permission_section_registry.get_sorted_sections(): with table_element( section.name, section.title, foldable=Foldable.FOLDABLE_SAVE_STATE, ) as table: permission_list = permission_registry.get_sorted_permissions(section) if not permission_list: table.row() table.cell(_("Permission"), _("No entries"), css=["wide"]) continue for perm in permission_list: table.row() table.cell(_("Permission"), perm.title, css=["wide"]) html.help(perm.description) for role_id, role in role_list: base_on_id = role.get("basedon", role_id) pvalue = role["permissions"].get(perm.name) if pvalue is None: if base_on_id in perm.defaults: icon_name: Optional[str] = "perm_yes_default" else: icon_name = None else: icon_name = "perm_%s" % (pvalue and "yes" or "no") table.cell(role_id, css=["center"]) if icon_name: html.icon(icon_name) html.close_table()
def render(self, row: Row, cell: Cell) -> CellSpec: single_url = "view.py?" + urlencode_vars( [("view_name", "aggr_single"), ("aggr_name", row["aggr_name"])] ) avail_url = single_url + "&mode=availability" bi_map_url = "bi_map.py?" + urlencode_vars( [ ("aggr_name", row["aggr_name"]), ] ) with output_funnel.plugged(): html.icon_button(bi_map_url, _("Visualize this aggregation"), "aggr") html.icon_button(single_url, _("Show only this aggregation"), "showbi") html.icon_button( avail_url, _("Analyse availability of this aggregation"), "availability" ) if row["aggr_effective_state"]["in_downtime"] != 0: html.icon( "derived_downtime", _("A service or host in this aggregation is in downtime.") ) if row["aggr_effective_state"]["acknowledged"]: html.icon( "ack", _( "The critical problems that make this aggregation non-OK have been acknowledged." ), ) if not row["aggr_effective_state"]["in_service_period"]: html.icon( "outof_serviceperiod", _("This aggregation is currently out of its service period."), ) code = HTML(output_funnel.drain()) return "buttons", code
def page(self): # Show outcome of host validation. Do not validate new hosts errors = None if self._mode == "edit": errors = (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 = 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=Folder.current(), basic_attributes=basic_attributes, ) if self._mode != "edit": html.set_focus("host") forms.end() html.hidden_fields() html.end_form()
def iconlink(text: str, url: str, icon: Icon) -> None: html.open_a(class_=["iconlink", "link"], target="main", href=url) html.icon(icon, cssclass="inline") html.write_text(text) html.close_a() html.br()
def _iconlink(self, text, url, icon): html.open_a(class_=["iconlink", "link"], target="main", href=url) html.icon("/webconf/images/icon_%s.png" % icon, cssclass="inline") html.write_text(text) html.close_a() html.br()
def _activation_status(self): with table_element( "site-status", title=_("Activation status"), searchable=False, sortable=False, css="activation", foldable=Foldable.FOLDABLE_STATELESS, ) as table: for site_id, site in sort_sites(activation_sites()): table.row() site_status, status = self._get_site_status(site_id, site) is_online = self._site_is_online(status) is_logged_in = self._site_is_logged_in(site_id, site) has_foreign = self._site_has_foreign_changes(site_id) can_activate_all = not has_foreign or user.may( "wato.activateforeign") # Disable actions for offline sites and not logged in sites if not is_online or not is_logged_in: can_activate_all = False need_restart = self._is_activate_needed(site_id) need_sync = self.is_sync_needed(site_id) need_action = need_restart or need_sync nr_changes = len(self._changes_of_site(site_id)) # Activation checkbox table.cell("", css=["buttons"]) if can_activate_all and nr_changes: html.checkbox("site_%s" % site_id, need_action, cssclass="site_checkbox") # Iconbuttons table.cell(_("Actions"), css=["buttons"]) if user.may("wato.sites"): edit_url = folder_preserving_link([("mode", "edit_site"), ("site", site_id)]) html.icon_button(edit_url, _("Edit the properties of this site"), "edit") # State if can_activate_all and need_sync: html.icon_button( url="javascript:void(0)", id_="activate_%s" % site_id, cssclass="activate_site", title= _("This site is not update and needs a replication. Start it now." ), icon="need_replicate", onclick='cmk.activation.activate_changes("site", "%s")' % site_id, ) if can_activate_all and need_restart: html.icon_button( url="javascript:void(0)", id_="activate_%s" % site_id, cssclass="activate_site", title= _("This site needs a restart for activating the changes. Start it now." ), icon="need_restart", onclick='cmk.activation.activate_changes("site", "%s")' % site_id, ) if can_activate_all and not need_action: html.icon("siteuptodate", _("This site is up-to-date.")) site_url = site.get("multisiteurl") if site_url: html.icon_button( site_url, _("Open this site's local web user interface"), "url", target="_blank", ) table.cell(_("Site"), site.get("alias", site_id), css=["narrow nobr"]) # Livestatus table.cell(_("Status"), css=["narrow nobr"]) html.status_label(content=status, status=status, title=_("This site is %s") % status) # Livestatus-/Checkmk-Version table.cell(_("Version"), site_status.get("livestatus_version", ""), css=["narrow nobr"]) table.cell(_("Changes"), "%d" % nr_changes, css=["number narrow nobr"]) table.cell(_("Progress"), css=["repprogress"]) html.open_div(id_="site_%s_status" % site_id, class_=["msg"]) html.close_div() html.open_div(id_="site_%s_progress" % site_id, class_=["progress"]) html.close_div() table.cell(_("Details"), css=["details"]) html.open_div(id_="site_%s_details" % site_id) last_state = self._last_activation_state(site_id) if not is_logged_in: html.write_text(_("Is not logged in.") + " ") if not last_state: html.write_text(_("Has never been activated")) elif need_action and last_state[ "_state"] == activate_changes.STATE_SUCCESS: html.write_text(_("Activation needed")) else: html.javascript( "cmk.activation.update_site_activation_state(%s);" % json.dumps(last_state)) html.close_div()
def _show_patterns(self): import cmk.gui.logwatch as logwatch collection = SingleRulesetRecursively("logwatch_rules") collection.load() ruleset = collection.get("logwatch_rules") html.h3(_("Logfile patterns")) if ruleset.is_empty(): html.open_div(class_="info") html.write_text( "There are no logfile patterns defined. You may create " 'logfile patterns using the <a href="%s">Rule Editor</a>.' % folder_preserving_link([ ("mode", "edit_ruleset"), ("varname", "logwatch_rules"), ])) html.close_div() # Loop all rules for this ruleset already_matched = False abs_rulenr = 0 for folder, rulenr, rule in ruleset.get_rules(): # Check if this rule applies to the given host/service if self._hostname: service_desc = self._get_service_description( self._hostname, "logwatch", self._item) # If hostname (and maybe filename) try match it rule_matches = rule.matches_host_and_item( Folder.current(), self._hostname, self._item, service_desc) else: # If no host/file given match all rules rule_matches = True with foldable_container( treename="rule", id_=str(abs_rulenr), isopen=True, title=HTML("<b>Rule #%d</b>" % (abs_rulenr + 1)), indent=False, ), table_element("pattern_editor_rule_%d" % abs_rulenr, sortable=False, css="logwatch") as table: abs_rulenr += 1 # TODO: What's this? pattern_list = rule.value if isinstance(pattern_list, dict): pattern_list = pattern_list["reclassify_patterns"] # Each rule can hold no, one or several patterns. Loop them all here for state, pattern, comment in pattern_list: match_class = "" disp_match_txt = HTML("") match_img = "" if rule_matches: # Applies to the given host/service matched = re.search(pattern, self._match_txt) if matched: # Prepare highlighted search txt match_start = matched.start() match_end = matched.end() disp_match_txt = ( escape_to_html(self._match_txt[:match_start]) + HTMLWriter.render_span( self._match_txt[match_start:match_end], class_="match") + escape_to_html(self._match_txt[match_end:])) if not already_matched: # First match match_class = "match first" match_img = "match" match_title = _( "This logfile pattern matches first and will be used for " "defining the state of the given line.") already_matched = True else: # subsequent match match_class = "match" match_img = "imatch" match_title = _( "This logfile pattern matches but another matched first." ) else: match_img = "nmatch" match_title = _( "This logfile pattern does not match the given string." ) else: # rule does not match match_img = "nmatch" match_title = _("The rule conditions do not match.") table.row() table.cell(_("Match")) html.icon("rule%s" % match_img, match_title) cls = ([ "state%d" % logwatch.level_state(state), "fillbackground" ] if match_class == "match first" else []) table.cell(_("State"), HTMLWriter.render_span( logwatch.level_name(state)), css=cls) table.cell(_("Pattern"), HTMLWriter.render_tt(pattern)) table.cell(_("Comment"), comment) table.cell(_("Matched line"), disp_match_txt) table.row(fixed=True) table.cell(colspan=5) edit_url = folder_preserving_link([ ("mode", "edit_rule"), ("varname", "logwatch_rules"), ("rulenr", rulenr), ("item", mk_repr(self._item).decode()), ("rule_folder", folder.path()), ("rule_id", rule.id), ]) html.icon_button(edit_url, _("Edit this rule"), "edit")
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: Rule) -> str: return folder_preserving_link([ ("mode", "edit_rule"), ("varname", varname), ("rule_folder", rule.folder.path()), ("rule_id", rule.id), ("host", self._hostname), ( "item", mk_repr(svc_desc_or_item).decode() if svc_desc_or_item else "", ), ("service", mk_repr(svc_desc).decode() if svc_desc else ""), ]) varname = rulespec.name valuespec = rulespec.valuespec url = folder_preserving_link([ ("mode", "edit_ruleset"), ("varname", varname), ("host", self._hostname), ("item", mk_repr(svc_desc_or_item).decode()), ("service", mk_repr(svc_desc).decode()), ]) forms.section(HTMLWriter.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.span(_("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. if varname == "logwatch_rules": pass elif known_settings is not self._PARAMETERS_UNKNOWN: try: html.write_text(valuespec.value_to_html(known_settings)) except Exception as e: if active_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 Rulespec.NO_FACTORY_DEFAULT and rulespec.factory_default is not 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 Rulespec.FACTORY_DEFAULT_UNUSED: # Some rulesets are ineffective if they are empty html.write_text(_("(unused)")) elif rulespec.factory_default is not Rulespec.NO_FACTORY_DEFAULT: # If there is a factory default then show that one setting = rulespec.factory_default html.write_text(valuespec.value_to_html(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_text( valuespec.value_to_html(valuespec.default_value())) # We have a setting elif valuespec: if ruleset.match_type() == "all": for s in setting: html.write_text(valuespec.value_to_html(s)) else: html.write_text(valuespec.value_to_html(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(icon_name, title=_("yes") if setting else _("no")) html.close_td() html.close_tr() html.close_table()
def _show_node(self, tree, show_host, mousecode=None, img_class=None): # Check if we have an assumed state: comparing assumed state (tree[1]) with state (tree[0]) if tree[1] and tree[0] != tree[1]: addclass = ["assumed"] effective_state = tree[1] else: addclass = [] effective_state = tree[0] class_ = [ "content", "state", "state%d" % (effective_state["state"] if effective_state["state"] is not None else -1), ] + addclass html.open_span(class_=class_) html.write_text(self._render_bi_state(effective_state["state"])) html.close_span() if mousecode: if img_class: html.img( src=theme.url("images/tree_closed.svg"), class_=["treeangle", img_class], onclick=mousecode, ) html.open_span(class_=["content", "name"]) icon_name, icon_title = None, None if tree[0]["in_downtime"] == 2: icon_name = "downtime" icon_title = _( "This element is currently in a scheduled downtime.") elif tree[0]["in_downtime"] == 1: # only display host downtime if the service has no own downtime icon_name = "derived_downtime" icon_title = _( "One of the subelements is in a scheduled downtime.") if tree[0]["acknowledged"]: icon_name = "ack" icon_title = _("This problem has been acknowledged.") if not tree[0]["in_service_period"]: icon_name = "outof_serviceperiod" icon_title = _( "This element is currently not in its service period.") if icon_name and icon_title: html.icon(icon_name, title=icon_title, class_=["icon", "bi"]) yield if mousecode: if str(effective_state["state"]) in tree[2].get( "state_messages", {}): html.b(HTML("♦"), class_="bullet") html.write_text(tree[2]["state_messages"][str( effective_state["state"])]) html.close_span() output: HTML = cmk.gui.view_utils.format_plugin_output( effective_state["output"], shall_escape=active_config.escape_plugin_output) if output: output = HTMLWriter.render_b(HTML("♦"), class_="bullet") + output else: output = HTML() html.span(output, class_=["content", "output"])