def render(self, what): html.write_text(_('Downtime Comment') + ": ") html.text_input("_down_comment", "", size=60, submit="") html.hr() html.button("_down_from_now", _("From now for")) html.nbsp() html.text_input("_down_minutes", default_value="60", size=4, submit="_down_from_now", cssclass="number") html.write_text(" " + _("minutes")) html.hr() for time_range in config.user_downtime_timeranges: html.button("_downrange__%s" % time_range['end'], _u(time_range['title'])) if what != "aggr" and config.user.may("action.remove_all_downtimes"): html.write_text(" - ") html.button("_down_remove", _("Remove all")) html.hr() if config.adhoc_downtime and config.adhoc_downtime.get("duration"): adhoc_duration = config.adhoc_downtime.get("duration") adhoc_comment = config.adhoc_downtime.get("comment", "") html.button("_down_adhoc", _("Adhoc for %d minutes") % adhoc_duration) html.nbsp() html.write_text(_('with comment') + ": ") html.write(adhoc_comment) html.hr() html.button("_down_custom", _("Custom time range")) self._vs_down_from().render_input("_down_from", time.time()) html.write_text(" " + _('to') + " ") self._vs_down_to().render_input("_down_to", time.time() + 7200) html.hr() html.checkbox("_down_flexible", False, label="%s " % _('flexible with max. duration')) self._vs_duration().render_input("_down_duration", 7200) if what == "host": html.hr() html.checkbox("_include_childs", False, label=_('Also set downtime on child hosts')) html.write_text(" ") html.checkbox("_include_childs_recurse", False, label=_('Do this recursively')) elif what == "service": html.hr() html.checkbox("_on_hosts", False, label=_('Schedule downtimes on the affected ' '<b>hosts</b> instead of on the individual ' 'services')) if self._has_recurring_downtimes(): html.hr() html.checkbox("_down_do_recur", False, label=_("Repeat this downtime on a regular basis every")) html.write_text(" ") from cmk.gui.cee.plugins.wato.cmc import recurring_downtimes_types # pylint: disable=no-name-in-module recurring_selections = [ (str(k), v) for (k, v) in sorted(recurring_downtimes_types().items()) ] html.dropdown("_down_recurring", recurring_selections, deflt="3") html.write_text(_("(This only works when using CMC)"))
def show(self): if not watolib.is_wato_slave_site(): if not config.wato_enabled: html.write_text(_("WATO is disabled.")) return False user_folders = compute_foldertree() # # Render link target selection # selected_topic, selected_target = config.user.load_file( "foldertree", (_('Hosts'), 'allhosts')) topic_views = visuals_by_topic( list(views.get_permitted_views().items()) + list(dashboard.get_permitted_dashboards().items())) topics = [(t, t) for t, _s in topic_views] html.open_table() html.open_tr() html.td(_('Topic:'), class_="label") html.open_td() html.dropdown("topic", topics, deflt=selected_topic, onchange='cmk.sidebar.wato_tree_topic_changed(this)') html.close_td() html.close_tr() html.open_tr() html.td(_("View:"), class_="label") html.open_td() for topic, view_list in topic_views: targets = [] for t, title, name, is_view in view_list: if config.visible_views and name not in config.visible_views: continue if config.hidden_views and name in config.hidden_views: continue if t == topic: if not is_view: name = 'dashboard|' + name targets.append((name, title)) if topic != selected_topic: default = '' style = 'display:none' # type: Optional[str] else: default = selected_target style = None html.dropdown( "target_%s" % topic, targets, deflt=default, onchange='cmk.sidebar.wato_tree_target_changed(this)', style=style) html.close_td() html.close_tr() html.close_table() # Now render the whole tree if user_folders: render_tree_folder("wato-hosts", list(user_folders.values())[0], 'cmk.sidebar.wato_tree_click')
def page(self): search = get_search_expression() html.begin_form("role", method="POST") # ID forms.header(_("Basic properties"), css="wide") forms.section(_("Internal ID"), simple="builtin" in self._role, is_required=True) if self._role.get("builtin"): html.write_text("%s (%s)" % (self._role_id, _("builtin role"))) html.hidden_field("id", self._role_id) else: html.text_input("id", self._role_id) html.set_focus("id") # Alias forms.section(_("Alias")) html.help(_("An alias or description of the role")) html.text_input("alias", self._role.get("alias", ""), size=50) # Based on if not self._role.get("builtin"): forms.section(_("Based on role")) html.help( _("Each user defined role is based on one of the builtin roles. " "When created it will start with all permissions of that role. When due to a software " "update or installation of an addons new permissions appear, the user role will get or " "not get those new permissions based on the default settings of the builtin role it's " "based on.")) role_choices: Choices = [(i, r["alias"]) for i, r in self._roles.items() if r.get("builtin")] html.dropdown("basedon", role_choices, deflt=self._role.get("basedon", "user"), ordered=True) forms.end() html.h2(_("Permissions")) # Permissions base_role_id = self._role.get("basedon", self._role_id) html.help( _("When you leave the permissions at "default" then they get their " "settings from the factory defaults (for builtin roles) or from the " "factory default of their base role (for user define roles). Factory defaults " "may change due to software updates. When choosing another base role, all " "permissions that are on default will reflect the new base role." )) for section in permission_section_registry.get_sorted_sections(): # Now filter by the optional search term filtered_perms = [] for perm in permission_registry.get_sorted_permissions(section): if search and (search not in perm.title.lower() and search not in perm.name.lower()): continue filtered_perms.append(perm) if not filtered_perms: continue forms.header(section.title, isopen=search is not None, css="wide") for perm in filtered_perms: forms.section(perm.title) pvalue = self._role["permissions"].get(perm.name) def_value = base_role_id in perm.defaults choices: Choices = [ ("yes", _("yes")), ("no", _("no")), ("default", _("default (%s)") % (def_value and _("yes") or _("no"))), ] deflt = {True: "yes", False: "no"}.get(pvalue, "default") html.dropdown("perm_" + perm.name, choices, deflt=deflt, style="width: 130px;") html.help(perm.description) forms.end() html.hidden_fields() html.end_form()
def display(self) -> None: html.write_text(_("from:") + " ") html.text_input(self.ident + "_from", size=8, cssclass="number") html.write_text(" %s: " % _("to")) html.text_input(self.ident + "_to", size=8, cssclass="number")
def _activation_status(self): with table_element("site-status", searchable=False, sortable=False, css="activation") as table: for site_id, site in sort_sites(config.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 config.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 config.user.may("wato.sites"): edit_url = watolib.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"] == cmk.gui.watolib.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 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 _show_login_page(self) -> None: html.set_render_headfoot(False) html.add_body_css_class("login") html.header(get_page_heading(), Breadcrumb(), javascripts=[]) default_origtarget = ("index.py" if requested_file_name(request) in ["login", "logout"] else makeuri(request, [])) origtarget = request.get_url_input("_origtarget", default_origtarget) # Never allow the login page to be opened in the iframe. Redirect top page to login page. # This will result in a full screen login page. html.javascript("""if(top != self) { window.top.location.href = location; }""") # When someone calls the login page directly and is already authed redirect to main page if requested_file_name(request) == "login" and _check_auth(request): raise HTTPRedirect(origtarget) html.open_div(id_="login") html.open_div(id_="login_window") html.open_a(href="https://checkmk.com") html.img( src=theme.detect_icon_path(icon_name="logo", prefix="mk-"), id_="logo", class_="custom" if theme.has_custom_logo() else None, ) html.close_a() html.begin_form("login", method="POST", add_transid=False, action="login.py") html.hidden_field("_login", "1") html.hidden_field("_origtarget", origtarget) html.label("%s:" % _("Username"), id_="label_user", class_=["legend"], for_="_username") html.br() html.text_input("_username", id_="input_user") html.label("%s:" % _("Password"), id_="label_pass", class_=["legend"], for_="_password") html.br() html.password_input("_password", id_="input_pass", size=None) if user_errors: html.open_div(id_="login_error") html.show_user_errors() html.close_div() html.open_div(id_="button_text") html.button("_login", _("Login"), cssclass="hot") html.close_div() html.close_div() html.open_div(id_="foot") if active_config.login_screen.get("login_message"): html.open_div(id_="login_message") html.show_message(active_config.login_screen["login_message"]) html.close_div() footer: List[HTML] = [] for title, url, target in active_config.login_screen.get( "footer_links", []): footer.append(html.render_a(title, href=url, target=target)) if "hide_version" not in active_config.login_screen: footer.append( escape_to_html("Version: %s" % cmk_version.__version__)) footer.append( HTML("© %s" % html.render_a( "tribe29 GmbH", href="https://tribe29.com", target="_blank"))) html.write_html(HTML(" - ").join(footer)) if cmk_version.is_raw_edition(): html.br() html.br() html.write_text( _('You can use, modify and distribute Check_MK under the terms of the <a href="%s" target="_blank">' "GNU GPL Version 2</a>.") % "https://checkmk.com/gpl.html") html.close_div() html.set_focus("_username") html.hidden_fields() html.end_form() html.close_div() html.footer()
def _show_patterns(self): import cmk.gui.logwatch as logwatch collection = watolib.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>.' % watolib.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( watolib.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) 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 reason_class = 'reason' 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_html_permissive(self._match_txt[:match_start]) \ + html.render_span(self._match_txt[match_start:match_end], class_="match")\ + escape_html_permissive(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 reason_class = 'noreason' match_img = 'nmatch' match_title = _('The rule conditions do not match.') table.row(css=reason_class) table.cell(_('Match')) html.icon("rule%s" % match_img, match_title) cls: List[str] = [] if match_class == 'match first': cls = [ 'state%d' % logwatch.level_state(state), 'fillbackground' ] table.cell(_('State'), html.render_span(logwatch.level_name(state)), css=cls) table.cell(_('Pattern'), html.render_tt(pattern)) table.cell(_('Comment'), comment) table.cell(_('Matched line'), disp_match_txt) table.row(fixed=True) table.cell(colspan=5) edit_url = watolib.folder_preserving_link([ ("mode", "edit_rule"), ("varname", "logwatch_rules"), ("rulenr", rulenr), ("item", ensure_str(watolib.mk_repr(self._item))), ("rule_folder", folder.path()), ("rule_id", rule.id), ]) html.icon_button(edit_url, _("Edit this rule"), "edit")
def show(self): # type: () -> None filename = Path(cmk.utils.paths.omd_root).joinpath( 'var/dokuwiki/data/pages/sidebar.txt') html.open_form(id_="wiki_search", onsubmit="cmk.sidebar.wiki_search('%s');" % config.omd_site()) html.input(id_="wiki_search_field", type_="text", name="wikisearch") html.icon_button("#", _("Search"), "wikisearch", onclick="cmk.sidebar.wiki_search('%s');" % config.omd_site()) html.close_form() html.div('', id_="wiki_side_clear") start_ul = True ul_started = False try: title = None for line in filename.open(encoding="utf-8").readlines(): line = line.strip() if line == "": if ul_started: html.end_foldable_container() start_ul = True ul_started = False elif line.endswith(":"): title = line[:-1] elif line == "----": pass # html.br() elif line.startswith("*"): if start_ul: if title: html.begin_foldable_container("wikisnapin", title, True, title, indent=True) else: html.open_ul() start_ul = False ul_started = True erg = re.findall(r'\[\[(.*)\]\]', line) if len(erg) == 0: continue erg = erg[0].split('|') if len(erg) > 1: link = erg[0] name = erg[1] else: link = erg[0] name = erg[0] if link.startswith("http://") or link.startswith( "https://"): simplelink(name, link, "_blank") else: erg = name.split(':') if len(erg) > 0: name = erg[-1] else: name = erg[0] bulletlink( name, "/%s/wiki/doku.php?id=%s" % (config.omd_site(), link)) else: html.write_text(line) if ul_started: html.close_ul() except IOError: html.write_html( html.render_p( html.render_text( "To get a navigation menu, you have to create a ") + html.render_a("sidebar", href="/%s/wiki/doku.php?id=%s" % (config.omd_site(), _("sidebar")), target="main") + # html.render_text(" in your wiki first.")))
def _show_service_info(self, all_rulesets): assert self._service is not None serviceinfo = watolib.check_mk_automation( self._host.site_id(), "analyse-service", [self._hostname, self._service]) if not serviceinfo: return forms.header(_("Check origin and parameters"), isopen=True, narrow=True, css="rulesettings") origin = serviceinfo["origin"] origin_txt = { "active": _("Active check"), "static": _("Manual check"), "auto": _("Inventorized check"), "classic": _("Classical check"), }[origin] self._render_rule_reason(_("Type of check"), None, "", "", False, origin_txt) # First case: discovered checks. They come from var/check_mk/autochecks/HOST. if origin == "auto": checkgroup = serviceinfo["checkgroup"] checktype = serviceinfo["checktype"] if not checkgroup: self._render_rule_reason( _("Parameters"), None, "", "", True, _("This check is not configurable via WATO")) # Logwatch needs a special handling, since it is not configured # via checkgroup_parameters but via "logwatch_rules" in a special # WATO module. elif checkgroup == "logwatch": rulespec = rulespec_registry["logwatch_rules"] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=serviceinfo["parameters"]) else: # Note: some discovered checks have a check group but # *no* ruleset for discovered checks. One example is "ps". # That can be configured as a manual check or created by # inventory. But in the later case all parameters are set # by the inventory. This will be changed in a later version, # but we need to address it anyway. grouprule = "checkgroup_parameters:" + checkgroup if grouprule not in rulespec_registry: try: rulespec = rulespec_registry["static_checks:" + checkgroup] except KeyError: rulespec = None if rulespec: url = watolib.folder_preserving_link([ ('mode', 'edit_ruleset'), ('varname', "static_checks:" + checkgroup), ('host', self._hostname) ]) self._render_rule_reason( _("Parameters"), url, _("Determined by discovery"), None, False, rulespec.valuespec._elements[2].value_to_text( serviceinfo["parameters"])) else: self._render_rule_reason( _("Parameters"), None, "", "", True, _("This check is not configurable via WATO")) else: rulespec = rulespec_registry[grouprule] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=serviceinfo["parameters"]) elif origin == "static": checkgroup = serviceinfo["checkgroup"] checktype = serviceinfo["checktype"] if not checkgroup: html.write_text(_("This check is not configurable via WATO")) else: rulespec = rulespec_registry["static_checks:" + checkgroup] itemspec = rulespec.item_spec if itemspec: item_text = itemspec.value_to_text(serviceinfo["item"]) title = rulespec.item_spec.title() else: item_text = serviceinfo["item"] title = _("Item") self._render_rule_reason(title, None, "", "", False, item_text) self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=self._PARAMETERS_OMIT) html.write(rulespec.valuespec._elements[2].value_to_text( serviceinfo["parameters"])) html.close_td() html.close_tr() html.close_table() elif origin == "active": checktype = serviceinfo["checktype"] rulespec = rulespec_registry["active_checks:" + checktype] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=None, svc_desc=None, known_settings=serviceinfo["parameters"]) elif origin == "classic": ruleset = all_rulesets.get("custom_checks") origin_rule_result = self._get_custom_check_origin_rule( ruleset, self._hostname, self._service) if origin_rule_result is None: raise MKUserError( None, _("Failed to determine origin rule of %s / %s") % (self._hostname, self._service)) rule_folder, rule_index, _rule = origin_rule_result 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 _output_analysed_ruleset(self, all_rulesets, rulespec, svc_desc_or_item, svc_desc, known_settings=None): if known_settings is None: known_settings = self._PARAMETERS_UNKNOWN def rule_url(rule): return watolib.folder_preserving_link([ ('mode', 'edit_rule'), ('varname', varname), ('rule_folder', rule.folder.path()), ('rulenr', rule.index()), ('host', self._hostname), ('item', watolib.mk_repr(svc_desc_or_item) if svc_desc_or_item else ''), ('service', watolib.mk_repr(svc_desc) if svc_desc else ''), ]) varname = rulespec.name valuespec = rulespec.valuespec url = watolib.folder_preserving_link([ ('mode', 'edit_ruleset'), ('varname', varname), ('host', self._hostname), ('item', watolib.mk_repr(svc_desc_or_item)), ('service', watolib.mk_repr(svc_desc)), ]) forms.section(html.render_a(rulespec.title, url)) ruleset = all_rulesets.get(varname) setting, rules = ruleset.analyse_ruleset(self._hostname, svc_desc_or_item, svc_desc) html.open_table(class_="setting") html.open_tr() html.open_td(class_="reason") # Show reason for the determined value if len(rules) == 1: rule_folder, rule_index, rule = rules[0] url = rule_url(rule) html.a(_("Rule %d in %s") % (rule_index + 1, rule_folder.title()), href=rule_url(rule)) elif len(rules) > 1: html.a("%d %s" % (len(rules), _("Rules")), href=url) else: html.i(_("Default Value")) html.close_td() # Show the resulting value or factory setting html.open_td( class_=["settingvalue", "used" if len(rules) > 0 else "unused"]) if isinstance(known_settings, dict) and "tp_computed_params" in known_settings: computed_at = known_settings["tp_computed_params"]["computed_at"] html.write_text( _("Timespecific parameters computed at %s") % cmk.utils.render.date_and_time(computed_at)) html.br() known_settings = known_settings["tp_computed_params"]["params"] # In some cases we now the settings from a check_mk automation if known_settings is self._PARAMETERS_OMIT: return # Special handling for logwatch: The check parameter is always None. The actual # patterns are configured in logwatch_rules. We do not have access to the actual # patterns here but just to the useless "None". In order not to complicate things # we simply display nothing here. if 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 render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): html.write_text("[\n") html.write(repr([cell.export_title() for cell in cells])) html.write_text(",\n") for row in rows: html.write_text("[") for cell in cells: joined_row = join_row(row, cell) _tdclass, content = cell.render_content(joined_row) html.write(repr(html.strip_tags(content))) html.write_text(",") html.write_text("],") html.write_text("\n]\n")
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): html.write("%s(\n" % html.request.var('jsonp', 'myfunction')) super(LayoutJSONP, self).render(rows, view, group_cells, cells, num_columns, show_checkboxes) html.write_text(");\n")
def _show_page_user_profile(change_pw): start_async_replication = False if not config.user.id: raise MKUserError(None, _('Not logged in.')) if not config.user.may('general.edit_profile') and not config.user.may( 'general.change_password'): raise MKAuthException( _("You are not allowed to edit your user profile.")) if not config.wato_enabled: raise MKAuthException( _('User profiles can not be edited (WATO is disabled).')) success = None if html.request.has_var('_save') and html.check_transaction(): users = userdb.load_users(lock=True) try: # Profile edit (user options like language etc.) if config.user.may('general.edit_profile'): if not change_pw: set_lang = html.get_checkbox('_set_lang') language = html.request.var('language') # Set the users language if requested if set_lang: if language == '': language = None # Set custom language users[config.user.id]['language'] = language config.user.language = language html.set_language_cookie(language) else: # Remove the customized language if 'language' in users[config.user.id]: del users[config.user.id]['language'] config.user.reset_language() # load the new language cmk.gui.i18n.localize(config.user.language) user = users.get(config.user.id) if user is None: raise Exception("current user is not in user DB") if config.user.may('general.edit_notifications' ) and user.get("notifications_enabled"): value = forms.get_input( watolib.get_vs_flexible_notifications(), "notification_method") users[config.user.id]["notification_method"] = value # Custom attributes if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): if not attr.permission() or config.user.may( attr.permission()): vs = attr.valuespec() value = vs.from_html_vars('ua_' + name) vs.validate_value(value, "ua_" + name) users[config.user.id][name] = value # Change the password if requested password_changed = False if config.user.may('general.change_password'): cur_password = html.request.var('cur_password') password = html.request.var('password') password2 = html.request.var('password2', '') if change_pw: # Force change pw mode if not cur_password: raise MKUserError( "cur_password", _("You need to provide your current password.")) if not password: raise MKUserError( "password", _("You need to change your password.")) if cur_password == password: raise MKUserError( "password", _("The new password must differ from your current one." )) if cur_password and password: if userdb.hook_login(config.user.id, cur_password) is False: raise MKUserError("cur_password", _("Your old password is wrong.")) if password2 and password != password2: raise MKUserError( "password2", _("The both new passwords do not match.")) watolib.verify_password_policy(password) users[config.user.id]['password'] = hash_password(password) users[config.user.id]['last_pw_change'] = int(time.time()) if change_pw: # Has been changed, remove enforcement flag del users[config.user.id]['enforce_pw_change'] # Increase serial to invalidate old cookies if 'serial' not in users[config.user.id]: users[config.user.id]['serial'] = 1 else: users[config.user.id]['serial'] += 1 password_changed = True # Now, if in distributed environment where users can login to remote sites, # set the trigger for pushing the new auth information to the slave sites # asynchronous if config.user.authorized_login_sites(): start_async_replication = True userdb.save_users(users) if password_changed: # Set the new cookie to prevent logout for the current user login.set_auth_cookie(config.user.id) success = True except MKUserError as e: html.add_user_error(e.varname, e) else: users = userdb.load_users() watolib.init_wato_datastructures(with_wato_lock=True) # When in distributed setup, display the replication dialog instead of the normal # profile edit dialog after changing the password. if start_async_replication: user_profile_async_replication_page() return if change_pw: title = _("Change Password") else: title = _("Edit User Profile") html.header(title) # Rule based notifications: The user currently cannot simply call the according # WATO module due to WATO permission issues. So we cannot show this button # right now. if not change_pw: rulebased_notifications = watolib.load_configuration_settings().get( "enable_rulebased_notifications") if rulebased_notifications and config.user.may( 'general.edit_notifications'): html.begin_context_buttons() url = "wato.py?mode=user_notifications_p" html.context_button(_("Notifications"), url, "notifications") html.end_context_buttons() else: reason = html.request.var('reason') if reason == 'expired': html.p( _('Your password is too old, you need to choose a new password.' )) else: html.p( _('You are required to change your password before proceeding.' )) if success: html.reload_sidebar() if change_pw: html.show_message(_("Your password has been changed.")) raise HTTPRedirect( html.request.get_str_input_mandatory('_origtarget', 'index.py')) html.show_message(_("Successfully updated user profile.")) # Ensure theme changes are applied without additional user interaction html.immediate_browser_redirect(0.5, html.makeuri([])) if html.has_user_errors(): html.show_user_errors() user = users.get(config.user.id) if user is None: html.show_warning(_("Sorry, your user account does not exist.")) html.footer() return # Returns true if an attribute is locked and should be read only. Is only # checked when modifying an existing user locked_attributes = userdb.locked_attributes(user.get('connector')) def is_locked(attr): return attr in locked_attributes html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") forms.header(_("Personal Settings")) if not change_pw: forms.section(_("Name"), simple=True) html.write_text(user.get("alias", config.user.id)) if config.user.may( 'general.change_password') and not is_locked('password'): 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") if not change_pw and config.user.may('general.edit_profile'): select_language(user) # Let the user configure how he wants to be notified if not rulebased_notifications \ and config.user.may('general.edit_notifications') \ and user.get("notifications_enabled"): forms.section(_("Notifications")) html.help( _("Here you can configure how you want to be notified about host and service problems and " "other monitoring events.")) watolib.get_vs_flexible_notifications().render_input( "notification_method", user.get("notification_method")) if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): vs = attr.valuespec() forms.section(_u(vs.title())) value = user.get(name, vs.default_value()) if not attr.permission() or config.user.may( attr.permission()): vs.render_input("ua_" + name, value) html.help(_u(vs.help())) else: html.write(vs.value_to_text(value)) # Save button forms.end() html.button("_save", _("Save")) html.close_div() html.hidden_fields() html.end_form() html.footer()
def page(self): # TODO: remove subclass specific things specifict things (everything with _type == 'user') html.begin_form("attr") forms.header(_("Properties")) forms.section(_("Name"), simple=not self._new) html.help( _("The name of the attribute is used as an internal key. It cannot be " "changed later.")) if self._new: html.text_input("name", self._attr.get('name', '')) html.set_focus("name") else: html.write_text(self._name) html.set_focus("title") forms.section(_("Title") + "<sup>*</sup>") html.help(_("The title is used to label this attribute.")) html.text_input("title", self._attr.get('title', '')) forms.section(_('Topic')) html.help( _('The attribute is added to this section in the edit dialog.')) html.dropdown('topic', self._topics, deflt=self._attr.get('topic', self._default_topic)) forms.section(_('Help Text') + "<sup>*</sup>") html.help( _('You might want to add some helpful description for the attribute.' )) html.text_area('help', self._attr.get('help', '')) forms.section(_('Data type')) html.help(_('The type of information to be stored in this attribute.')) if self._new: html.dropdown('type', custom_attr_types(), deflt=self._attr.get('type', '')) else: html.write(dict(custom_attr_types())[self._attr.get('type')]) self._add_extra_form_sections() forms.section(_('Show in WATO host table')) html.help( _('This attribute is only visibile on the edit host and folder pages by default, but ' 'you can also make it visible in the host overview tables of WATO.' )) html.checkbox( 'show_in_table', self._attr.get('show_in_table', False), label=_( "Show the setting of the attribute in the WATO host list table" )) forms.section(_('Add to monitoring configuration')) html.help(self._macro_help) html.checkbox('add_custom_macro', self._attr.get('add_custom_macro', False), label=self._macro_label) forms.end() html.show_localization_hint() html.button("save", _("Save")) html.hidden_fields() html.end_form()
def _show_start_form(self): html.begin_form("parentscan", method="POST") html.hidden_fields() # Mode of action html.open_p() if not self._complete_folder: num_selected = len(get_hosts_from_checkboxes()) html.write_text( _("You have selected <b>%d</b> hosts for parent scan. ") % num_selected) html.p( _("The parent scan will try to detect the last gateway " "on layer 3 (IP) before a host. This will be done by " "calling <tt>traceroute</tt>. If a gateway is found by " "that way and its IP address belongs to one of your " "monitored hosts, that host will be used as the hosts " "parent. If no such host exists, an artifical ping-only " "gateway host will be created if you have not disabled " "this feature.")) forms.header(_("Settings for Parent Scan")) self._settings = config.user.load_file( "parentscan", { "where": "subfolder", "alias": _("Created by parent scan"), "recurse": True, "select": "noexplicit", "timeout": 8, "probes": 2, "ping_probes": 5, "max_ttl": 10, "force_explicit": False, }) # Selection forms.section(_("Selection")) if self._complete_folder: html.checkbox("recurse", self._settings["recurse"], label=_("Include all subfolders")) html.br() html.radiobutton( "select", "noexplicit", self._settings["select"] == "noexplicit", _("Skip hosts with explicit parent definitions (even if empty)") + "<br>") html.radiobutton( "select", "no", self._settings["select"] == "no", _("Skip hosts hosts with non-empty parents (also if inherited)") + "<br>") html.radiobutton("select", "ignore", self._settings["select"] == "ignore", _("Scan all hosts") + "<br>") # Performance forms.section(_("Performance")) html.open_table() html.open_tr() html.open_td() html.write_text(_("Timeout for responses") + ":") html.close_td() html.open_td() html.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.get("ping_probes", 5)), size=2, cssclass="number") html.close_td() html.close_tr() html.close_table() # Configuring parent forms.section(_("Configuration")) html.checkbox( "force_explicit", self._settings["force_explicit"], label= _("Force explicit setting for parents even if setting matches that of the folder" )) # Gateway creation forms.section(_("Creation of gateway hosts")) html.write_text(_("Create gateway hosts in")) html.open_ul() html.radiobutton( "where", "subfolder", self._settings["where"] == "subfolder", _("in the subfolder <b>%s/Parents</b>") % watolib.Folder.current_disk_folder().title()) html.br() html.radiobutton( "where", "here", self._settings["where"] == "here", _("directly in the folder <b>%s</b>") % watolib.Folder.current_disk_folder().title()) html.br() html.radiobutton("where", "there", self._settings["where"] == "there", _("in the same folder as the host")) html.br() html.radiobutton("where", "nowhere", self._settings["where"] == "nowhere", _("do not create gateway hosts")) html.close_ul() html.write_text(_("Alias for created gateway hosts") + ": ") html.text_input("alias", self._settings["alias"]) forms.end() # Start button html.button("_start", _("Start")) html.hidden_fields() html.end_form()
def _show_user_list(self): visible_custom_attrs = [(name, attr) for name, attr in userdb.get_user_attributes() if attr.show_in_table()] users = userdb.load_users() entries = list(users.items()) html.begin_form("bulk_delete_form", method="POST") roles = userdb_utils.load_roles() timeperiods = watolib.timeperiods.load_timeperiods() contact_groups = load_contact_group_information() with table_element("users", None, empty_text=_("No users are defined yet.")) as table: online_threshold = time.time() - config.user_online_maxage for uid, user in sorted( entries, key=lambda x: x[1].get("alias", x[0]).lower()): table.row() # Checkboxes table.cell(html.render_input( "_toggle_group", type_="button", class_="checkgroup", onclick="cmk.selection.toggle_all_rows();", value='X'), sortable=False, css="checkbox") if uid != config.user.id: html.checkbox( "_c_user_%s" % ensure_str(base64.b64encode(uid.encode("utf-8")))) user_connection_id = cleanup_connection_id( user.get('connector')) connection = get_connection(user_connection_id) # Buttons table.cell(_("Actions"), css="buttons") if connection: # only show edit buttons when the connector is available and enabled edit_url = watolib.folder_preserving_link([("mode", "edit_user"), ("edit", uid)]) html.icon_button(edit_url, _("Properties"), "edit") clone_url = watolib.folder_preserving_link([ ("mode", "edit_user"), ("clone", uid) ]) html.icon_button(clone_url, _("Create a copy of this user"), "clone") delete_url = make_action_link([("mode", "users"), ("_delete", uid)]) html.icon_button(delete_url, _("Delete"), "delete") notifications_url = watolib.folder_preserving_link([ ("mode", "user_notifications"), ("user", uid) ]) if rulebased_notifications_enabled(): html.icon_button( notifications_url, _("Custom notification table of this user"), "notifications") # ID table.cell(_("ID"), uid) # Online/Offline if config.user.may("wato.show_last_user_activity"): last_seen = userdb.get_last_activity(uid, user) user.get('last_seen', 0) if last_seen >= online_threshold: title = _('Online') img_txt = 'online' elif last_seen != 0: title = _('Offline') img_txt = 'offline' elif last_seen == 0: title = _('Never logged in') img_txt = 'inactive' title += ' (%s %s)' % (render.date(last_seen), render.time_of_day(last_seen)) table.cell(_("Act.")) html.icon(img_txt, title) table.cell(_("Last seen")) if last_seen != 0: html.write_text("%s %s" % (render.date(last_seen), render.time_of_day(last_seen))) else: html.write_text(_("Never logged in")) if cmk_version.is_managed_edition(): table.cell(_("Customer"), managed.get_customer_name(user)) # Connection if connection: table.cell( _("Connection"), '%s (%s)' % (connection.short_title(), user_connection_id)) locked_attributes = userdb.locked_attributes( user_connection_id) else: table.cell( _("Connection"), "%s (%s) (%s)" % (_("UNKNOWN"), user_connection_id, _("disabled")), css="error") locked_attributes = [] # Authentication if "automation_secret" in user: auth_method = _("Automation") elif user.get("password") or 'password' in locked_attributes: auth_method = _("Password") else: auth_method = "<i>%s</i>" % _("none") table.cell(_("Authentication"), auth_method) table.cell(_("State")) if user.get("locked", False): html.icon('user_locked', _('The login is currently locked')) if "disable_notifications" in user and isinstance( user["disable_notifications"], bool): disable_notifications_opts = { "disable": user["disable_notifications"] } else: disable_notifications_opts = user.get( "disable_notifications", {}) if disable_notifications_opts.get("disable", False): html.icon('notif_disabled', _('Notifications are disabled')) # Full name / Alias table.text_cell(_("Alias"), user.get("alias", "")) # Email table.text_cell(_("Email"), user.get("email", "")) # Roles table.cell(_("Roles")) if user.get("roles", []): role_links = [(watolib.folder_preserving_link([ ("mode", "edit_role"), ("edit", role) ]), roles[role].get("alias")) for role in user["roles"]] html.write_html( HTML(", ").join( html.render_a(alias, href=link) for (link, alias) in role_links)) # contact groups table.cell(_("Contact groups")) cgs = user.get("contactgroups", []) if cgs: cg_aliases = [ contact_groups[c]['alias'] if c in contact_groups else c for c in cgs ] cg_urls = [ watolib.folder_preserving_link([("mode", "edit_contact_group"), ("edit", c)]) for c in cgs ] html.write_html( HTML(", ").join( html.render_a(content, href=url) for (content, url) in zip(cg_aliases, cg_urls))) else: html.i(_("none")) #table.cell(_("Sites")) #html.write(vs_authorized_sites().value_to_text(user.get("authorized_sites", # vs_authorized_sites().default_value()))) # notifications if not rulebased_notifications_enabled(): table.cell(_("Notifications")) if not cgs: html.i(_("not a contact")) elif not user.get("notifications_enabled", True): html.write_text(_("disabled")) elif user.get("host_notification_options", "") == "" and \ user.get("service_notification_options", "") == "": html.write_text(_("all events disabled")) else: tp = user.get("notification_period", "24X7") if tp not in timeperiods: tp = tp + _(" (invalid)") elif tp not in watolib.timeperiods.builtin_timeperiods( ): url = watolib.folder_preserving_link([ ("mode", "edit_timeperiod"), ("edit", tp) ]) tp = html.render_a(timeperiod_spec_alias( timeperiods[tp], tp), href=url) else: tp = timeperiod_spec_alias(timeperiods[tp], tp) html.write(tp) # the visible custom attributes for name, attr in visible_custom_attrs: vs = attr.valuespec() table.cell(escaping.escape_attribute(_u(vs.title()))) html.write( vs.value_to_text(user.get(name, vs.default_value()))) html.button("_bulk_delete_users", _("Bulk Delete"), "submit", style="margin-top:10px") html.hidden_fields() html.end_form() if not load_contact_group_information(): url = "wato.py?mode=contact_groups" html.open_div(class_="info") html.write( _("Note: you haven't defined any contact groups yet. If you <a href='%s'>" "create some contact groups</a> you can assign users to them und thus " "make them monitoring contacts. Only monitoring contacts can receive " "notifications.") % url) html.write( " you can assign users to them und thus " "make them monitoring contacts. Only monitoring contacts can receive " "notifications.") html.close_div()
def _render_result(self, result): html.open_li() html.open_a(href=result.url, target="main", onclick="cmk.popup_menu.close_popup()") html.write_text(result.title) html.close_a() html.close_li()
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))
def show(self): hosts = self._get_hosts() num_hosts = len(hosts) if num_hosts > 900: html.write_text( _("Sorry, I will not display more than 900 hosts.")) return # Choose smallest square number large enough # to show all hosts n = 1 while n * n < num_hosts: n += 1 rows = int(num_hosts / n) lastcols = num_hosts % n if lastcols > 0: rows += 1 # Calculate cell size (Automatic sizing with 100% does not work here) # - Get cell spacing: 1px between each cell # - Substract the cell spacing for each column from the total width # - Then divide the total width through the number of columns # - Then get the full-digit width of the cell and summarize the rest # to be substracted from the cell width # This is not a 100% solution but way better than having no links cell_spacing = 1 cell_size = int((snapin_width - cell_spacing * (n + 1)) / n) cell_size, cell_size_rest = divmod(cell_size, 1) style = 'width:%spx' % (snapin_width - n * cell_size_rest) html.open_table(class_=["content_center", "hostmatrix"], cellspacing="0", style=["border-collapse:collapse;", style]) col = 1 row = 1 for site, host, state, has_been_checked, worstsvc, downtimedepth in sorted( hosts): if col == 1: html.open_tr() if downtimedepth > 0: s = "d" elif not has_been_checked: s = "p" elif worstsvc == 2 or state == 1: s = "2" elif worstsvc == 3 or state == 2: s = "3" elif worstsvc == 1: s = "1" else: s = "0" url = "view.py?view_name=host&site=%s&host=%s" % ( html.urlencode(site), html.urlencode(host)) html.open_td(class_=["state", "state%s" % s]) html.a( '', href=url, title=host, target="main", style=["width:%spx;" % cell_size, "height:%spx;" % cell_size]) html.close_td() if col == n or (row == rows and n == lastcols): html.open_tr() col = 1 row += 1 else: col += 1 html.close_table()
def iconlink(text, url, icon): 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 _rename_tags_after_confirmation(operation): """Handle renaming and deletion of tags Find affected hosts, folders and rules. Remove or fix those rules according the users' wishes. """ repair_mode = html.request.var("_repair") if repair_mode is not None: try: mode = TagCleanupMode(repair_mode) except ValueError: raise MKUserError("_repair", "Invalid mode") if mode == TagCleanupMode.ABORT: raise MKUserError("id_0", _("Aborting change.")) # make attribute unknown to system, important for save() operations if isinstance(operation, OperationRemoveTagGroup): watolib.host_attributes.undeclare_host_tag_attribute( operation.tag_group_id) affected_folders, affected_hosts, affected_rulesets = \ _change_host_tags_in_folders(operation, mode, watolib.Folder.root_folder()) return _("Modified folders: %d, modified hosts: %d, modified rulesets: %d") % \ (len(affected_folders), len(affected_hosts), len(affected_rulesets)) message = "" affected_folders, affected_hosts, affected_rulesets = \ _change_host_tags_in_folders(operation, TagCleanupMode.CHECK, watolib.Folder.root_folder()) if affected_folders: message += _("Affected folders with an explicit reference to this tag " "group and that are affected by the change") + ":<ul>" for folder in affected_folders: message += '<li><a href="%s">%s</a></li>' % (folder.edit_url(), folder.alias_path()) message += "</ul>" if affected_hosts: message += _("Hosts where this tag group is explicitely set " "and that are effected by the change") + ":<ul><li>" for nr, host in enumerate(affected_hosts): if nr > 20: message += "... (%d more)" % (len(affected_hosts) - 20) break elif nr > 0: message += ", " message += '<a href="%s">%s</a>' % (host.edit_url(), host.name()) message += "</li></ul>" if affected_rulesets: message += _( "Rulesets that contain rules with references to the changed tags" ) + ":<ul>" for ruleset in affected_rulesets: message += '<li><a href="%s">%s</a></li>' % ( watolib.folder_preserving_link([("mode", "edit_ruleset"), ("varname", ruleset.name) ]), ruleset.title()) message += "</ul>" if message: wato_html_head(operation.confirm_title()) html.open_div(class_="really") html.h3(_("Your modifications affect some objects")) html.write_text(message) html.br() html.write_text( _("WATO can repair things for you. It can rename tags in folders, host and rules. " "Removed tag groups will be removed from hosts and folders, removed tags will be " "replaced with the default value for the tag group (for hosts and folders). What " "rules concern, you have to decide how to proceed.")) html.begin_form("confirm") if affected_rulesets and _is_removing_tags(operation): html.br() html.b( _("Some tags that are used in rules have been removed by you. What " "shall we do with that rules?")) html.open_ul() html.radiobutton( "_repair", "remove", True, _("Just remove the affected tags from the rules.")) html.br() html.radiobutton( "_repair", "delete", False, _("Delete rules containing tags that have been removed, if tag is used in a positive sense. Just remove that tag if it's used negated." )) else: html.open_ul() html.radiobutton("_repair", "repair", True, _("Fix affected folders, hosts and rules.")) html.br() html.radiobutton("_repair", "abort", False, _("Abort your modifications.")) html.close_ul() html.button("_do_confirm", _("Proceed"), "") html.hidden_fields(add_action_vars=True) html.end_form() html.close_div() return False return True
def display(self) -> None: html.write_text(_("Min. Version:")) html.text_input(self.htmlvars[0], size=7) html.write_text(" ") html.write_text(_("Max. Version:")) html.text_input(self.htmlvars[1], size=7)
def show(self): html.write_text("dummy")
def _show_entry_cells(self, table, ident, entry): table.cell(_("Title"), entry["title"]) table.cell(_("Conditions")) html.open_ul(class_="conditions") html.open_li() html.write_text( "%s: %s" % (_("Folder"), Folder.folder(entry["conditions"]["host_folder"]).alias_path()) ) html.close_li() html.close_ul() html.write_text(vs_conditions().value_to_html(entry["conditions"])) table.cell(_("Editable by")) if entry["owned_by"] is None: html.write_text( _( "Administrators (having the permission " '"Write access to all predefined conditions")' ) ) else: html.write_text(self._contact_group_alias(entry["owned_by"])) table.cell(_("Shared with")) if not entry["shared_with"]: html.write_text(_("Not shared")) else: html.write_text(", ".join([self._contact_group_alias(g) for g in entry["shared_with"]]))
def render_bi_availability(title, aggr_rows): config.user.need_permission("general.see_availability") av_mode = html.request.var("av_mode", "availability") avoptions = get_availability_options_from_url("bi") if av_mode == "timeline": title = _("Timeline of") + " " + title else: title = _("Availability of") + " " + title if html.output_format != "csv_export": html.body_start(title) html.top_heading(title) html.begin_context_buttons() html.toggle_button("avoptions", False, "painteroptions", _("Configure details of the report")) html.context_button(_("Status View"), html.makeuri([("mode", "status")]), "status") if config.reporting_available() and config.user.may( "general.reporting"): html.context_button(_("Export as PDF"), html.makeuri([], filename="report_instant.py"), "report") if av_mode == "availability" and config.user.may("general.csv_export"): html.context_button( _("Export as CSV"), html.makeuri([("output_format", "csv_export")]), "download_csv") if av_mode == "timeline": html.context_button(_("Availability"), html.makeuri([("av_mode", "availability")]), "availability") elif len(aggr_rows) == 1: aggr_name = aggr_rows[0]["aggr_name"] aggr_group = aggr_rows[0]["aggr_group"] timeline_url = html.makeuri([("av_mode", "timeline"), ("av_aggr_name", aggr_name), ("av_aggr_group", aggr_group)]) html.context_button(_("Timeline"), timeline_url, "timeline") html.end_context_buttons() avoptions = render_availability_options("bi") if not html.has_user_errors(): logrow_limit = avoptions["logrow_limit"] if logrow_limit == 0: livestatus_limit = None else: livestatus_limit = (len(aggr_rows) * logrow_limit) spans = [] # iterate all aggregation rows timewarpcode = "" try: timewarp = int(html.request.var("timewarp")) except (ValueError, TypeError): timewarp = None has_reached_logrow_limit = False timeline_containers, fetched_rows = availability.get_timeline_containers( aggr_rows, avoptions, timewarp, livestatus_limit + 1 if livestatus_limit is not None else None) if livestatus_limit and fetched_rows > livestatus_limit: has_reached_logrow_limit = True for timeline_container in timeline_containers: tree = timeline_container.aggr_tree these_spans = timeline_container.timeline timewarp_tree_state = timeline_container.timewarp_state spans += these_spans # render selected time warp for the corresponding aggregation row (should be matched by only one) if timewarp and timewarp_tree_state: state, assumed_state, node, _subtrees = timewarp_tree_state eff_state = state if assumed_state is not None: eff_state = assumed_state row = { "aggr_tree": tree, "aggr_treestate": timewarp_tree_state, "aggr_state": state, # state disregarding assumptions "aggr_assumed_state": assumed_state, # is None, if there are no assumptions "aggr_effective_state": eff_state, # is assumed_state, if there are assumptions, else real state "aggr_name": node["title"], "aggr_output": eff_state["output"], "aggr_hosts": node["reqhosts"], "aggr_function": node["func"], "aggr_group": html.request.var("aggr_group"), } renderer = bi.FoldableTreeRendererTree( row, omit_root=False, expansion_level=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_availability_csv("bi", av_data, avoptions) return html.write(timewarpcode) do_render_availability("bi", av_rawdata, av_data, av_mode, None, avoptions) html.bottom_footer() html.body_end()
def _show_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: Optional[str] = "assumed" effective_state = tree[1] else: addclass = None effective_state = tree[0] class_: List[Optional[str]] = [ "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=config.escape_plugin_output) if output: output = html.render_b(HTML("♦"), class_="bullet") + output else: output = HTML() html.span(output, class_=["content", "output"])
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: HTML = 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 = 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 = makeactionuri(request, transactions, [("_%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 = HTML( " " ) # 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.span(header_title) html.close_div() else: html.write_text(header_title) else: html.write_text(header_title) html.close_th() html.close_tr()
def _rename_tags_after_confirmation( breadcrumb: Breadcrumb, operation: ABCOperation) -> Union[bool, str]: """Handle renaming and deletion of tags Find affected hosts, folders and rules. Remove or fix those rules according the users' wishes. """ repair_mode = html.request.var("_repair") if repair_mode is not None: try: mode = TagCleanupMode(repair_mode) except ValueError: raise MKUserError("_repair", "Invalid mode") if mode == TagCleanupMode.ABORT: raise MKUserError("id_0", _("Aborting change.")) # make attribute unknown to system, important for save() operations if isinstance(operation, OperationRemoveTagGroup): watolib.host_attributes.undeclare_host_tag_attribute( operation.tag_group_id) affected_folders, affected_hosts, affected_rulesets = \ change_host_tags_in_folders(operation, mode, watolib.Folder.root_folder()) return _("Modified folders: %d, modified hosts: %d, modified rulesets: %d") % \ (len(affected_folders), len(affected_hosts), len(affected_rulesets)) message = u"" affected_folders, affected_hosts, affected_rulesets = \ change_host_tags_in_folders(operation, TagCleanupMode.CHECK, watolib.Folder.root_folder()) if affected_folders: message += _("Affected folders with an explicit reference to this tag " "group and that are affected by the change") + ":" with html.plugged(): _show_affected_folders(affected_folders) message += html.drain() if affected_hosts: message += _("Hosts where this tag group is explicitely set " "and that are effected by the change") + ":" with html.plugged(): _show_affected_hosts(affected_hosts) message += html.drain() if affected_rulesets: message += _( "Rulesets that contain rules with references to the changed tags" ) + ":" with html.plugged(): _show_affected_rulesets(affected_rulesets) message += html.drain() if message: wato_html_head(title=operation.confirm_title(), breadcrumb=breadcrumb) html.open_div(class_="really") html.h3(_("Your modifications affect some objects")) html.write_text(message) html.br() html.write_text( _("WATO can repair things for you. It can rename tags in folders, host and rules. " "Removed tag groups will be removed from hosts and folders, removed tags will be " "replaced with the default value for the tag group (for hosts and folders). What " "rules concern, you have to decide how to proceed.")) html.begin_form("confirm", method="POST") if affected_rulesets and _is_removing_tags(operation): html.br() html.b( _("Some tags that are used in rules have been removed by you. What " "shall we do with that rules?")) html.open_ul() html.radiobutton( "_repair", "remove", True, _("Just remove the affected tags from the rules.")) html.br() html.radiobutton( "_repair", "delete", False, _("Delete rules containing tags that have been removed, if tag is used in a positive sense. Just remove that tag if it's used negated." )) else: html.open_ul() html.radiobutton("_repair", "repair", True, _("Fix affected folders, hosts and rules.")) html.br() html.radiobutton("_repair", "abort", False, _("Abort your modifications.")) html.close_ul() html.button("_do_confirm", _("Proceed"), "") html.hidden_fields(add_action_vars=True) html.end_form() html.close_div() return False return True
def render(self, what): html.write_text(_('Comment') + ": ") html.text_input("_comment", size=33, submit="_add_comment") html.write_text(" ") html.button("_add_comment", _("Add comment"))