def write_snapin_exception(e: Exception) -> None: html.open_div(class_=["snapinexception"]) html.h2(_("Error")) html.p(str(e)) html.div(traceback.format_exc().replace("\n", "<br>"), style="display:none;") html.close_div()
def _show_form(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id) webauthn_credentials = credentials["webauthn_credentials"] backup_codes = credentials["backup_codes"] html.begin_form("two_factor", method="POST") html.div("", id_="webauthn_message") forms.header(_("Credentials")) forms.section(_("Registered credentials"), simple=True) if webauthn_credentials: self._show_credentials(webauthn_credentials) else: html.i(_("No credentials registered")) forms.section(_("Backup codes"), simple=True) if backup_codes: html.p( _("You have %d unused backup codes left. You can use them as one-time password " "if your key is not available.") % len(backup_codes)) html.i( _("If you regenerate backup codes, you automatically invalidate the existing codes." )) else: html.i(_("No backup codes created yet.")) forms.end() html.hidden_fields() html.end_form() html.footer()
def page(self) -> None: host_names = get_hostnames_from_checkboxes() hosts = { host_name: Folder.current().host(host_name) for host_name in host_names } current_host_hash = sha256(repr(hosts).encode()).hexdigest() # When bulk edit has been made with some hosts, then other hosts have been selected # and then another bulk edit has made, the attributes need to be reset before # rendering the form. Otherwise the second edit will have the attributes of the # first set. host_hash = request.var("host_hash") if not host_hash or host_hash != current_host_hash: request.del_vars(prefix="attr_") request.del_vars(prefix="bulk_change_") html.p("%s%s %s" % ( _("You have selected <b>%d</b> hosts for bulk edit. You can now change " "host attributes for all selected hosts at once. ") % len(hosts), _("If a select is set to <i>don't change</i> then currenty not all selected " "hosts share the same setting for this attribute. " "If you leave that selection, all hosts will keep their individual settings." ), _("In case you want to <i>unset</i> attributes on multiple hosts, you need to " "use the <i>bulk cleanup</i> action instead of bulk edit."), )) html.begin_form("edit_host", method="POST") html.prevent_password_auto_completion() html.hidden_field("host_hash", current_host_hash) configure_attributes(False, hosts, "bulk", parent=Folder.current()) forms.end() html.hidden_fields() html.end_form()
def page(self): html.p( _("This module can be used to define conditions for Check_MK rules in a central place. " "You can then refer to these conditions from different rulesets. Using these predefined " "conditions may save you a lot of redundant conditions when you need them in multiple " "rulesets.")) super().page()
def page(self) -> None: html.p( _("Here you can add icons, for example to use them in bookmarks or " "in custom actions of views. Allowed are single PNG image files " "with a maximum size of 80x80 px. Custom actions have to be defined " "in the global settings and can be used in the custom icons rules " "of hosts and services.")) html.begin_form("upload_form", method="POST") self._vs_upload().render_input("_upload_icon", None) html.hidden_fields() html.end_form() icons = sorted(self._load_custom_icons().items()) with table_element("icons", _("Custom Icons")) as table: for icon_name, category_name in icons: table.row() table.cell(_("Actions"), css=["buttons"]) delete_url = make_confirm_link( url=make_action_link([("mode", "icons"), ("_delete", icon_name)]), message=_( "Do you really want to delete the icon <b>%s</b>?") % icon_name, ) html.icon_button(delete_url, _("Delete this Icon"), "delete") table.cell(_("Icon"), html.render_icon(icon_name), css=["buttons"]) table.cell(_("Name"), icon_name) table.cell(_("Category"), IconSelector.category_alias(category_name))
def show_details(self, crash_info: CrashInfo, row) -> None: if not crash_info["details"]: return html.h3(_("Details"), class_="table") html.p( _("No detail renderer for crash of type '%s' available. Details structure is:" ) % crash_info["crash_type"]) html.pre(pprint.pformat(crash_info["details"]))
def page(self): html.p( _("The WATO configuration can be set to read only mode for all users that are not " "permitted to ignore the read only mode. All users that are permitted to set the " "read only can disable it again when another permitted user enabled it before." )) html.begin_form("read_only", method="POST") self._vs().render_input("_read_only", self._settings) html.hidden_fields() html.end_form()
def page(self) -> None: html.p( _("This password management module stores the passwords you use in your checks and " "special agents in a central place. Please note that this password store is no " "kind of password safe. Your passwords will not be encrypted.")) html.p( _("All the passwords you store in your monitoring configuration, " "including this password store, are needed in plain text to contact remote systems " "for monitoring. So all those passwords have to be stored readable by the monitoring." )) super().page()
def show(self): self._load() if not active_config.virtual_host_trees: url = "wato.py?varname=virtual_host_trees&mode=edit_configvar" html.p( _("You have not defined any virtual host trees. You can do this " 'in the <a href="%s" target="main">global settings</a>.') % url) return self._show_tree_selection() self._show_tree()
def _show_import_ical_page(self) -> None: html.p( _("This page can be used to generate a new timeperiod definition based " "on the appointments of an iCalendar (<tt>*.ics</tt>) file. This import is normally used " "to import events like holidays, therefore only single whole day appointments are " "handled by this import.")) html.begin_form("import_ical", method="POST") self._vs_ical().render_input("ical", {}) forms.end() html.hidden_fields() html.end_form()
def _upload_form(self) -> None: html.begin_form("upload", method="POST") html.p( _( "Using this page you can import several hosts at once into the choosen folder. You can " "choose a CSV file from your workstation to be uploaded, paste a CSV files contents " "into the textarea or simply enter a list of hostnames (one per line) to the textarea." ) ) self._vs_upload().render_input("_upload", None) html.hidden_fields() html.end_form()
def _show_backup_codes(self, backup_codes: Sequence[str]) -> None: forms.header(_("Backup codes")) forms.section(_("Backup codes"), simple=True) if backup_codes: html.p( _("You have %d unused backup codes left.") % len(backup_codes)) html.i( _("If you regenerate backup codes, you automatically invalidate old codes." )) else: html.i(_("No backup codes created yet.")) forms.end()
def page(self) -> None: html.p( _( "To be able to download the key, you need to unlock the key by entering the " "passphrase. This is only done to verify that you are allowed to download the key. " "The key will be downloaded in encrypted form." ) ) html.begin_form("key", method="POST") html.prevent_password_auto_completion() self._vs_key().render_input("key", {}) self._vs_key().set_focus("key") html.hidden_fields() html.end_form()
def page(self): hosts = get_hosts_from_checkboxes() html.p( _("You have selected <b>%d</b> hosts for bulk cleanup. This means removing " "explicit attribute values from hosts. The hosts will then inherit attributes " "configured at the host list or folders or simply fall back to the builtin " "default values.") % len(hosts)) html.begin_form("bulkcleanup", method="POST") forms.header(_("Attributes to remove from hosts")) self._select_attributes_for_bulk_cleanup(hosts) html.hidden_fields() html.end_form()
def jqm_page_index_topic_renderer(topic: str, items: Items) -> None: has_items_for_topic = any(i for i in items if i[0] == topic) if not has_items_for_topic: return html.p(topic) html.open_ul(**{"data-role": "listview", "data-inset": "true"}) for top, href, title in items: if top == topic: html.open_li() html.open_a(href=href, **{"data-ajax": "false", "data-transition": "flip"}) html.write_html(HTML(title)) html.close_a() html.close_li() html.close_ul()
def render_werk_description(werk) -> HTML: with output_funnel.plugged(): html.open_p() in_list = False in_code = False for line in werk["body"]: if line.startswith("LI:"): if not in_list: html.open_ul() in_list = True html.li(line[3:]) else: if in_list: html.close_ul() in_list = False if line.startswith("H2:"): html.h3(line[3:]) elif line.startswith("C+:"): html.open_pre(class_="code") in_code = True elif line.startswith("F+:"): file_name = line[3:] if file_name: html.div(file_name, class_="filename") html.open_pre(class_="file") in_code = True elif line.startswith("C-:") or line.startswith("F-:"): html.close_pre() in_code = False elif line.startswith("OM:"): html.write_text("OMD[mysite]:~$ ") html.b(line[3:]) elif line.startswith("RP:"): html.write_text("root@myhost:~# ") html.b(line[3:]) elif not line.strip() and not in_code: html.p("") else: html.write_text(line + "\n") if in_list: html.close_ul() html.close_p() return HTML(output_funnel.drain())
def user_profile_async_replication_dialog(sites: Sequence[SiteId], back_url: str) -> None: html.p( _("In order to activate your changes available on all remote sites, your user profile needs " "to be replicated to the remote sites. This is done on this page now. Each site " "is being represented by a single image which is first shown gray and then fills " "to green during synchronisation.")) html.h3(_("Replication States")) html.open_div(id_="profile_repl") num_replsites = 0 for site_id in sites: site = active_config.sites[site_id] if "secret" not in site: status_txt = _("Not logged in.") start_sync = False icon = "repl_locked" else: status_txt = _("Waiting for replication to start") start_sync = True icon = "repl_pending" html.open_div(class_="site", id_="site-%s" % site_id) html.div("", title=status_txt, class_=["icon", "repl_status", icon]) if start_sync: changes_manager = ActivateChanges() changes_manager.load() estimated_duration = changes_manager.get_activation_time( site_id, ACTIVATION_TIME_PROFILE_SYNC, 2.0) html.javascript("cmk.profile_replication.start(%s, %d, %s);" % ( json.dumps(site_id), int(estimated_duration * 1000.0), json.dumps(_("Replication in progress")), )) num_replsites += 1 else: _add_profile_replication_change(site_id, status_txt) html.span(site.get("alias", site_id)) html.close_div() html.javascript("cmk.profile_replication.prepare(%d, %s);\n" % (num_replsites, json.dumps(back_url))) html.close_div()
def _show_form(self) -> None: assert user.id is not None users = userdb.load_users() change_reason = request.get_ascii_input("reason") if change_reason == "expired": html.p(_("Your password is too old, you need to choose a new password.")) elif change_reason == "enforced": html.p(_("You are required to change your password before proceeding.")) user_spec = users.get(user.id) if user_spec is None: html.show_warning(_("Sorry, your user account does not exist.")) html.footer() return locked_attributes = userdb.locked_attributes(user_spec.get("connector")) if "password" in locked_attributes: raise MKUserError( "cur_password", _("You can not change your password, because it is " "managed by another system."), ) html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") forms.header(self._page_title()) forms.section(_("Current Password")) html.password_input("cur_password", autocomplete="new-password") forms.section(_("New Password")) html.password_input("password", autocomplete="new-password") forms.section(_("New Password Confirmation")) html.password_input("password2", autocomplete="new-password") forms.end() html.close_div() html.hidden_fields() html.end_form() html.footer()
def page(self) -> None: row = self._get_crash_row() crash_info = self._get_crash_info(row) title = _("Crash report: %s") % self._crash_id breadcrumb = self._breadcrumb(title) make_header(html, title, breadcrumb, self._page_menu(breadcrumb, crash_info)) # Do not reveal crash context information to unauthenticated users or not permitted # users to prevent disclosure of internal information if not user.may("general.see_crash_reports"): html.show_error("<b>%s:</b> %s" % (_("Internal error"), crash_info["exc_value"])) html.p( _("An internal error occurred while processing your request. " "You can report this issue to your Checkmk administrator. " "Detailed information can be found on the crash report page " "or in <tt>var/log/web.log</tt>.")) html.footer() return if request.has_var("_report") and transactions.check_transaction(): details = self._handle_report_form(crash_info) else: details = ReportSubmitDetails(name="", mail="") if crash_info["crash_type"] == "gui": html.show_error("<b>%s:</b> %s" % (_("Internal error"), crash_info["exc_value"])) html.p( _("An internal error occured while processing your request. " "You can report this issue to the Checkmk team to help " "fixing this issue. Please use the form below for reporting." )) self._warn_about_local_files(crash_info) self._show_report_form(crash_info, details) self._show_crash_report(crash_info) self._show_crash_report_details(crash_info, row) html.footer()
def render_mobile_list(rows, view, group_cells, cells, num_columns, show_checkboxes): if not is_mobile(request, response): html.show_error(_("This view can only be used in mobile mode.")) return # Force relative timestamp always. This saves space. painter_options = PainterOptions.get_instance() painter_options.set("ts_format", "rel") html.open_ul(class_="mobilelist", **{"data-role": "listview"}) # Paint data rows for row in rows: html.open_li() rendered_cells = [cell.render(row) for cell in cells] if rendered_cells: # First cell (assumedly state) is left rendered_class, rendered_content = rendered_cells[0] html.p(rendered_content, class_=["ui-li-aside", "ui-li-desc", rendered_class]) if len(rendered_cells) > 1: content = HTML(" · ").join([ rendered_cell[1] for rendered_cell in rendered_cells[1:num_columns + 1] ]) html.h3(content) for rendered_cell, cell in zip( rendered_cells[num_columns + 1:], cells[num_columns + 1:]): rendered_class, rendered_content = rendered_cell html.open_p(class_="ui-li-desc") cell.paint_as_header() html.write_text(": ") html.span(rendered_content, class_=rendered_class) html.close_p() html.close_li() html.close_ul() html.javascript('$("ul.mobilelist a").attr("data-ajax", "false");')
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( **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>") % Folder.current_disk_folder().title(), ) html.br() html.radiobutton( "where", "here", self._settings.where == "here", _("directly in the folder <b>%s</b>") % 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()