2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
10 # | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
26 """Modes for managing notification configuration"""
32 import cmk
.store
as store
34 import cmk
.gui
.view_utils
35 import cmk
.gui
.wato
.user_profile
36 import cmk
.gui
.userdb
as userdb
37 import cmk
.gui
.config
as config
38 import cmk
.gui
.watolib
as watolib
39 import cmk
.gui
.table
as table
40 import cmk
.gui
.forms
as forms
41 from cmk
.gui
.exceptions
import MKUserError
42 from cmk
.gui
.i18n
import _
43 from cmk
.gui
.globals import html
44 from cmk
.gui
.valuespec
import (
65 from cmk
.gui
.plugins
.wato
import (
76 class NotificationsMode(EventsMode
):
77 # TODO: Clean this up. Use inheritance
79 def _rule_match_conditions(cls
):
80 return cls
._generic
_rule
_match
_conditions
() \
81 + cls
._event
_rule
_match
_conditions
(flavour
="notify") \
82 + cls
._notification
_rule
_match
_conditions
()
85 def _notification_rule_match_conditions(cls
):
86 def transform_ec_rule_id_match(val
):
87 if isinstance(val
, list):
94 title
= _("Restrict to n<sup>th</sup> to m<sup>th</sup> notification"),
95 orientation
= "float",
99 help = _("Let through notifications counting from this number. "
100 "For normal alerts The first notification has the number 1. "
101 "For custom notifications the number is 0."),
108 help = _("Let through notifications counting upto this number"),
109 default_value
= 999999,
116 ( "match_escalation_throttle",
118 title
= _("Throttle periodic notifications"),
119 help = _("This match option allows you to throttle periodic notifications after "
120 "a certain number of notifications have been created by the monitoring "
121 "core. If you for example select 10 as the beginning and 5 as the rate "
122 "then you will receive the notification 1 through 10 and then 15, 20, "
123 "25... and so on. Note that recovery notifications are not affected by throttling."),
124 orientation
= "float",
127 label
= _("beginning from notification number"),
132 label
= _("send only every"),
134 unit
= _("th notification"),
140 ( "match_notification_comment",
142 title
= _("Match notification comment"),
143 help = _("This match only makes sense for custom notifications. When a user creates "
144 "a custom notification then he/she can enter a comment. This comment is shipped "
145 "in the notification context variable <tt>NOTIFICATIONCOMMENT</tt>. Here you can "
146 "make a condition of that comment. It is a regular expression matching the beginning "
149 mode
= RegExpUnicode
.prefix
,
153 title
= _("Event Console alerts"),
154 help = _("The Event Console can have events create notifications in Check_MK. "
155 "These notifications will be processed by the rule based notification "
156 "system of Check_MK. This matching option helps you distinguishing "
157 "and also gives you access to special event fields."),
160 FixedValue(False, title
= _("Do not match Event Console alerts"), totext
=""),
162 title
= _("Match only Event Console alerts"),
167 ID(title
= _("Match event rule"), label
= _("Rule ID:"), size
=12, allow_empty
=False),
168 add_label
= _("Add Rule ID"),
169 title
= _("Rule IDs")
171 forth
= transform_ec_rule_id_match
,
176 title
= _("Match syslog priority"),
177 help = _("Define a range of syslog priorities this rule matches"),
178 orientation
= "horizontal",
181 DropdownChoice(label
= _("from:"), choices
= cmk
.gui
.mkeventd
.syslog_priorities
, default_value
= 4),
182 DropdownChoice(label
= _(" to:"), choices
= cmk
.gui
.mkeventd
.syslog_priorities
, default_value
= 0),
188 title
= _("Match syslog facility"),
189 help = _("Make the rule match only if the event has a certain syslog facility. "
190 "Messages not having a facility are classified as <tt>user</tt>."),
191 choices
= cmk
.gui
.mkeventd
.syslog_facilities
,
196 title
= _("Match event comment"),
197 help = _("This is a regular expression for matching the event's comment."),
198 mode
= RegExpUnicode
.prefix
,
208 def _render_notification_rules(self
,
217 html
.message(_("You have not created any rules yet."))
220 vs_match_conditions
= Dictionary(elements
=self
._rule
_match
_conditions
())
226 title
= _("Notification rules")
228 url
= html
.makeuri([("mode", "user_notifications"), ("user", userid
)])
229 code
= html
.render_icon_button(url
, _("Edit this user's notifications"), "edit")
230 title
= code
+ _("Notification rules of user %s") % userid
232 title
= _("Global notification rules")
233 table
.begin(title
=title
, limit
=None, sortable
=False)
236 analyse_rules
, _analyse_plugins
= analyse
239 for nr
, rule
in enumerate(rules
):
244 table
.cell(css
="buttons")
245 what
, _anarule
, reason
= analyse_rules
[nr
+ start_nr
]
247 html
.icon(_("This rule matches"), "rulematch")
249 html
.icon(_("This rule does not match: %s") % reason
, "rulenmatch")
252 listmode
= "user_notifications_p"
254 listmode
= "user_notifications"
256 listmode
= "notifications"
258 actions_allowed
= config
.user
.may(
259 "notification_plugin.%s" % rule
['notify_plugin'][0])
261 if show_buttons
and actions_allowed
:
262 anavar
= html
.var("analyse", "")
263 delete_url
= make_action_link([("mode", listmode
), ("user", userid
),
265 drag_url
= make_action_link([("mode", listmode
), ("analyse", anavar
),
266 ("user", userid
), ("_move", nr
)])
267 suffix
= "_p" if profilemode
else ""
268 edit_url
= watolib
.folder_preserving_link([("mode",
269 "notification_rule" + suffix
),
270 ("edit", nr
), ("user", userid
)])
271 clone_url
= watolib
.folder_preserving_link([("mode",
272 "notification_rule" + suffix
),
273 ("clone", nr
), ("user", userid
)])
275 table
.cell(_("Actions"), css
="buttons")
276 html
.icon_button(edit_url
, _("Edit this notification rule"), "edit")
277 html
.icon_button(clone_url
, _("Create a copy of this notification rule"), "clone")
278 html
.element_dragger_url("tr", base_url
=drag_url
)
279 html
.icon_button(delete_url
, _("Delete this notification rule"), "delete")
281 table
.cell("", css
="buttons")
283 html
.empty_icon_button()
285 table
.cell("", css
="narrow")
286 if rule
.get("disabled"):
287 html
.icon(_("This rule is currently disabled and will not be applied"), "disabled")
289 html
.empty_icon_button()
291 notify_method
= rule
["notify_plugin"]
292 # Catch rules with empty notify_plugin key
293 # Maybe this should be avoided somewhere else (e.g. rule editor)
294 if not notify_method
:
295 notify_method
= (None, [])
296 notify_plugin
= notify_method
[0]
298 table
.cell(_("Type"), css
="narrow")
299 if notify_method
[1] == None:
300 html
.icon(_("Cancel notifications for this plugin type"), "notify_cancel")
302 html
.icon(_("Create a notification"), "notify_create")
304 table
.cell(_("Plugin"), notify_plugin
or _("Plain Email"), css
="narrow nowrap")
306 table
.cell(_("Bulk"), css
="narrow")
307 if "bulk" in rule
or "bulk_period" in rule
:
308 html
.icon(_("This rule configures bulk notifications."), "bulk")
310 table
.cell(_("Description"))
311 url
= rule
.get("docu_url")
313 html
.icon_button(url
, _("Context information about this rule"), "url", target
="_blank")
315 html
.write_text(rule
["description"])
316 table
.cell(_("Contacts"))
318 if rule
.get("contact_object"):
319 infos
.append(_("all contacts of the notified object"))
320 if rule
.get("contact_all"):
321 infos
.append(_("all users"))
322 if rule
.get("contact_all_with_email"):
323 infos
.append(_("all users with and email address"))
324 if rule
.get("contact_users"):
325 infos
.append(_("users: ") + (", ".join(rule
["contact_users"])))
326 if rule
.get("contact_groups"):
327 infos
.append(_("contact groups: ") + (", ".join(rule
["contact_groups"])))
328 if rule
.get("contact_emails"):
329 infos
.append(_("email addresses: ") + (", ".join(rule
["contact_emails"])))
331 html
.i(_("(no one)"))
335 html
.write("• %s" % line
)
338 table
.cell(_("Conditions"), css
="rule_conditions")
339 num_conditions
= len([key
for key
in rule
if key
.startswith("match_")])
341 title
= _("%d conditions") % num_conditions
342 html
.begin_foldable_container(
343 treename
="rule_%s_%d" % (userid
, nr
),
348 tree_img
="tree_black",
350 html
.write(vs_match_conditions
.value_to_text(rule
))
351 html
.end_foldable_container()
353 html
.i(_("(no conditions)"))
357 def _add_change(self
, log_what
, log_text
):
358 add_change(log_what
, log_text
, need_restart
=False)
360 def _vs_notification_bulkby(self
):
362 title
= _("Create separate notification bulks based on"),
364 ( "folder", _("Folder") ),
365 ( "host", _("Host") ),
366 ( "service", _("Service description") ),
367 ( "sl", _("Service level") ),
368 ( "check_type", _("Check type") ),
369 ( "state", _("Host/Service state") ),
370 ( "ec_contact", _("Event Console contact") ),
371 ( "ec_comment", _("Event Console comment") ),
373 default_value
= [ "host" ],
377 @mode_registry.register
378 class ModeNotifications(NotificationsMode
):
381 return "notifications"
384 def permissions(cls
):
385 return ["notifications"]
388 super(ModeNotifications
, self
).__init
__()
389 options
= config
.user
.load_file("notification_display_options", {})
390 self
._show
_user
_rules
= options
.get("show_user_rules", False)
391 self
._show
_backlog
= options
.get("show_backlog", False)
392 self
._show
_bulks
= options
.get("show_bulks", False)
395 return _("Notification configuration")
399 html
.context_button(_("New Rule"), watolib
.folder_preserving_link([("mode", "notification_rule")]), "new")
400 if self
._show
_user
_rules
:
401 html
.context_button(_("Hide user rules"), html
.makeactionuri([("_show_user", "")]), "users")
403 html
.context_button(_("Show user rules"), html
.makeactionuri([("_show_user", "1")]), "users")
405 if self
._show
_backlog
:
406 html
.context_button(_("Hide Analysis"), html
.makeactionuri([("_show_backlog", "")]), "analyze")
408 html
.context_button(_("Analyse"), html
.makeactionuri([("_show_backlog", "1")]), "analyze")
411 html
.context_button(_("Hide Bulks"), html
.makeactionuri([("_show_bulks", "")]), "bulk")
413 html
.context_button(_("Show Bulks"), html
.makeactionuri([("_show_bulks", "1")]), "bulk")
416 if html
.has_var("_show_user"):
417 if html
.check_transaction():
418 self
._show
_user
_rules
= bool(html
.var("_show_user"))
419 self
._save
_notification
_display
_options
()
421 elif html
.has_var("_show_backlog"):
422 if html
.check_transaction():
423 self
._show
_backlog
= bool(html
.var("_show_backlog"))
424 self
._save
_notification
_display
_options
()
426 elif html
.has_var("_show_bulks"):
427 if html
.check_transaction():
428 self
._show
_bulks
= bool(html
.var("_show_bulks"))
429 self
._save
_notification
_display
_options
()
431 elif html
.has_var("_replay"):
432 if html
.check_transaction():
433 nr
= int(html
.var("_replay"))
434 watolib
.check_mk_local_automation("notification-replay", [str(nr
)], None)
435 return None, _("Replayed notifiation number %d") % (nr
+ 1)
438 return self
._generic
_rule
_list
_actions
(self
._get
_notification
_rules
(), "notification", _("notification rule"), watolib
.save_notification_rules
)
440 def _get_notification_rules(self
):
441 return watolib
.load_notification_rules()
443 def _save_notification_display_options(self
):
444 config
.user
.save_file(
445 "notification_display_options", {
446 "show_user_rules": self
._show
_user
_rules
,
447 "show_backlog": self
._show
_backlog
,
448 "show_bulks": self
._show
_bulks
,
452 self
._show
_not
_enabled
_warning
()
453 self
._show
_no
_fallback
_contact
_warning
()
454 self
._show
_bulk
_notifications
()
455 self
._show
_notification
_backlog
()
458 def _show_not_enabled_warning(self
):
459 # Check setting of global notifications. Are they enabled? If not, display
460 # a warning here. Note: this is a main.mk setting, so we cannot access this
462 current_settings
= watolib
.load_configuration_settings()
463 if not current_settings
.get("enable_rulebased_notifications"):
464 url
= 'wato.py?mode=edit_configvar&varname=enable_rulebased_notifications'
466 _("<b>Warning</b><br><br>Rule based notifications are disabled in your global settings. "
467 "The rules that you edit here will have affect only on notifications that are "
468 "created by the Event Console. Normal monitoring alerts will <b>not</b> use the "
469 "rule based notifications now."
471 "You can change this setting <a href=\"%s\">here</a>.") % url
)
473 def _show_no_fallback_contact_warning(self
):
474 if not self
._fallback
_mail
_contacts
_configured
():
475 url
= 'wato.py?mode=edit_configvar&varname=notification_fallback_email'
477 _("<b>Warning</b><br><br>You haven't configured a "
478 "<a href=\"%s\">fallback email address</a> nor enabled receiving fallback emails for "
479 "any user. If your monitoring produces a notification that is not matched by any of your "
480 "notification rules, the notification will not be sent out. To prevent that, please "
481 "configure either the global setting or enable the fallback contact option for at least "
482 "one of your users.") % url
)
484 def _fallback_mail_contacts_configured(self
):
485 current_settings
= watolib
.load_configuration_settings()
486 if current_settings
.get("notification_fallback_email"):
489 for user
in userdb
.load_users(lock
=False).itervalues():
490 if user
.get("fallback_contact", False):
495 def _show_bulk_notifications(self
):
497 # Warn if there are unsent bulk notifications
498 if not self
._render
_bulks
(only_ripe
=False):
499 html
.message(_("Currently there are no unsent notification bulks pending."))
501 # Warn if there are unsent bulk notifications
502 self
._render
_bulks
(only_ripe
=True)
504 def _render_bulks(self
, only_ripe
):
505 bulks
= watolib
.check_mk_local_automation("notification-get-bulks",
506 ["1" if only_ripe
else "0"], None)
511 table
.begin(title
= _("Overdue bulk notifications!"))
513 table
.begin(title
= _("Open bulk notifications"))
515 for directory
, age
, interval
, timeperiod
, maxcount
, uuids
in bulks
:
516 dirparts
= directory
.split("/")
517 contact
= dirparts
[-3]
518 method
= dirparts
[-2]
519 bulk_id
= dirparts
[-1].split(",", 2)[-1]
521 table
.cell(_("Contact"), contact
)
522 table
.cell(_("Method"), method
)
523 table
.cell(_("Bulk ID"), bulk_id
)
524 table
.cell(_("Max. Age (sec)"), "%s" % interval
, css
="number")
525 table
.cell(_("Age (sec)"), "%d" % age
, css
="number")
526 if interval
and age
>= interval
:
527 html
.icon(_("Age of oldest notification is over maximum age"), "warning")
528 table
.cell(_("Timeperiod"), "%s" % timeperiod
)
529 table
.cell(_("Max. Count"), str(maxcount
), css
="number")
530 table
.cell(_("Count"), str(len(uuids
)), css
="number")
531 if len(uuids
) >= maxcount
:
532 html
.icon(_("Number of notifications exceeds maximum allowed number"), "warning")
536 def _show_notification_backlog(self
):
537 """Show recent notifications. We can use them for rule analysis"""
538 if not self
._show
_backlog
:
541 backlog
= store
.load_data_from_file(cmk
.paths
.var_dir
+ "/notify/backlog.mk", [])
545 table
.begin(table_id
= "backlog", title
= _("Recent notifications (for analysis)"), sortable
=False)
546 for nr
, context
in enumerate(backlog
):
547 self
._convert
_context
_to
_unicode
(context
)
549 table
.cell(" ", css
="buttons")
551 analyse_url
= html
.makeuri([("analyse", str(nr
))])
552 html
.icon_button(analyse_url
, _("Analyze ruleset with this notification"), "analyze")
554 html
.icon_button(None, _("Show / hide notification context"),
556 onclick
="toggle_container('notification_context_%d')" % nr
)
558 replay_url
= html
.makeactionuri([("_replay", str(nr
))])
559 html
.icon_button(replay_url
, _("Replay this notification, send it again!"), "replay")
561 if html
.var("analyse") and nr
== int(html
.var("analyse")):
562 html
.icon(_("You are analysing this notification"), "rulematch")
564 table
.cell(_("Nr."), nr
+1, css
="number")
565 if "MICROTIME" in context
:
566 date
= time
.strftime("%Y-%m-%d %H:%M:%S",
567 time
.localtime(int(context
["MICROTIME"]) / 1000000.0))
569 date
= context
.get("SHORTDATETIME") or \
570 context
.get("LONGDATETIME") or \
571 context
.get("DATE") or \
574 table
.cell(_("Date/Time"), date
, css
="nobr")
575 nottype
= context
.get("NOTIFICATIONTYPE", "")
576 table
.cell(_("Type"), nottype
)
578 if nottype
in ["PROBLEM", "RECOVERY"]:
579 if context
.get("SERVICESTATE"):
580 statename
= context
["SERVICESTATE"][:4]
581 state
= context
["SERVICESTATEID"]
582 css
= "state svcstate state%s" % state
584 statename
= context
.get("HOSTSTATE")[:4]
585 state
= context
["HOSTSTATEID"]
586 css
= "state hstate hstate%s" % state
587 table
.cell(_("State"), statename
, css
=css
)
588 elif nottype
.startswith("DOWNTIME"):
589 table
.cell(_("State"))
590 html
.icon(_("Downtime"), "downtime")
591 elif nottype
.startswith("ACK"):
592 table
.cell(_("State"))
593 html
.icon(_("Acknowledgement"), "ack")
594 elif nottype
.startswith("FLAP"):
595 table
.cell(_("State"))
596 html
.icon(_("Flapping"), "flapping")
598 table
.cell(_("State"), "")
600 table
.cell(_("Host"), context
.get("HOSTNAME", ""))
601 table
.cell(_("Service"), context
.get("SERVICEDESC", ""))
602 output
= context
.get("SERVICEOUTPUT", context
.get("HOSTOUTPUT"))
604 table
.cell(_("Plugin output"), cmk
.gui
.view_utils
.format_plugin_output(output
, shall_escape
=config
.escape_plugin_output
))
606 # Add toggleable notitication context
607 table
.row(class_
="notification_context hidden", id_
="notification_context_%d" % nr
)
608 table
.cell(colspan
=8)
611 for nr
, (key
, val
) in enumerate(sorted(context
.items())):
620 # This dummy row is needed for not destroying the odd/even row highlighting
621 table
.row(class_
="notification_context hidden")
625 def _convert_context_to_unicode(self
, context
):
626 # Convert all values to unicode
627 for key
, value
in context
.iteritems():
628 if isinstance(value
, str):
630 value_unicode
= value
.decode("utf-8")
633 value_unicode
= value
.decode("latin-1")
635 value_unicode
= u
"(Invalid byte sequence)"
636 context
[key
] = value_unicode
638 # TODO: Refactor this
639 def _show_rules(self
):
641 if html
.var("analyse"):
642 nr
= int(html
.var("analyse"))
643 analyse
= watolib
.check_mk_local_automation("notification-analyse", [str(nr
)], None)
648 rules
= self
._get
_notification
_rules
()
649 self
._render
_notification
_rules
(rules
, show_title
=True, analyse
=analyse
, start_nr
=start_nr
)
650 start_nr
+= len(rules
)
652 if self
._show
_user
_rules
:
653 users
= userdb
.load_users()
654 userids
= users
.keys()
655 userids
.sort() # Create same order as modules/notification.py
656 for userid
in userids
:
658 user_rules
= user
.get("notification_rules", [])
660 self
._render
_notification
_rules
(
667 start_nr
+= len(user_rules
)
670 table
.begin(table_id
= "plugins", title
= _("Resulting notifications"))
671 for contact
, plugin
, parameters
, bulk
in analyse
[1]:
673 if contact
.startswith('mailto:'):
674 contact
= contact
[7:] # strip of fake-contact mailto:-prefix
675 table
.cell(_("Recipient"), contact
)
676 table
.cell(_("Plugin"), self
._vs
_notification
_scripts
().value_to_text(plugin
))
677 table
.cell(_("Plugin parameters"), ", ".join(parameters
))
678 table
.cell(_("Bulking"))
680 html
.write(_("Time horizon") + ": " + Age().value_to_text(bulk
["interval"]))
681 html
.write_text(", %s: %d" % (_("Maximum count"), bulk
["count"]))
682 html
.write(", %s %s" % (_("group by"), self
._vs
_notification
_bulkby
().value_to_text(bulk
["groupby"])))
686 def _vs_notification_scripts(self
):
687 return DropdownChoice(
688 title
= _("Notification Script"),
689 choices
= watolib
.notification_script_choices
,
690 default_value
= "mail"
694 class UserNotificationsMode(NotificationsMode
):
696 super(UserNotificationsMode
, self
).__init
__()
697 self
._start
_async
_repl
= False
699 def _from_vars(self
):
700 self
._users
= userdb
.load_users(lock
=html
.is_transaction() or html
.has_var("_move"))
703 user
= self
._users
[self
._user
_id
()]
705 raise MKUserError(None, _('The requested user does not exist'))
707 self
._rules
= user
.setdefault("notification_rules", [])
711 raise NotImplementedError()
714 return _("Custom notification table for user ") + self
._user
_id
()
717 html
.context_button(_("All Users"), watolib
.folder_preserving_link([("mode", "users")]), "back")
718 html
.context_button(_("User Properties"),
719 watolib
.folder_preserving_link([("mode", "edit_user"), ("edit", self
._user
_id
())]), "edit")
720 html
.context_button(_("New Rule"),
721 watolib
.folder_preserving_link([("mode", "notification_rule"), ("user", self
._user
_id
())]), "new")
724 if html
.has_var("_delete"):
725 nr
= int(html
.var("_delete"))
726 rule
= self
._rules
[nr
]
727 c
= wato_confirm(_("Confirm notification rule deletion"),
728 _("Do you really want to delete the notification rule <b>%d</b> <i>%s</i>?") %
729 (nr
, rule
.get("description","")))
732 userdb
.save_users(self
._users
)
734 self
._add
_change
("notification-delete-user-rule",
735 _("Deleted notification rule %d of user %s") % (nr
, self
._user
_id
()))
741 elif html
.has_var("_move"):
742 if html
.check_transaction():
743 from_pos
= html
.get_integer_input("_move")
744 to_pos
= html
.get_integer_input("_index")
745 rule
= self
._rules
[from_pos
]
746 del self
._rules
[from_pos
] # make to_pos now match!
747 self
._rules
[to_pos
:to_pos
] = [rule
]
748 userdb
.save_users(self
._users
)
750 self
._add
_change
("notification-move-user-rule",
751 _("Changed position of notification rule %d of user %s") % (from_pos
, self
._user
_id
()))
754 if self
._start
_async
_repl
:
755 cmk
.gui
.wato
.user_profile
.user_profile_async_replication_dialog(
756 sites
=watolib
.get_notification_sync_sites())
757 html
.h3(_('Notification Rules'))
759 self
._render
_notification
_rules
(
762 profilemode
=isinstance(self
, ModePersonalUserNotifications
))
765 @mode_registry.register
766 class ModeUserNotifications(UserNotificationsMode
):
769 return "user_notifications"
772 def permissions(cls
):
776 return html
.get_unicode_input("user")
779 @mode_registry.register
780 class ModePersonalUserNotifications(UserNotificationsMode
):
783 return "user_notifications_p"
786 def permissions(cls
):
790 super(ModePersonalUserNotifications
, self
).__init
__()
791 config
.user
.need_permission("general.edit_notifications")
794 return config
.user
.id
796 def _add_change(self
, log_what
, log_text
):
797 if config
.has_wato_slave_sites():
798 self
._start
_async
_repl
= True
799 watolib
.log_audit(None, log_what
, log_text
)
801 super(ModePersonalUserNotifications
, self
)._add
_change
(log_what
, log_text
)
804 return _("Your personal notification rules")
807 html
.context_button(_("Profile"), "user_profile.py", "back")
808 html
.context_button(_("New Rule"), watolib
.folder_preserving_link([("mode", "notification_rule_p")]), "new")
811 # TODO: Split editing of user notification rule and global notification rule
812 # into separate classes
813 class EditNotificationRuleMode(NotificationsMode
):
815 super(EditNotificationRuleMode
, self
).__init
__()
816 self
._start
_async
_repl
= False
818 # TODO: Refactor this
819 def _from_vars(self
):
820 self
._edit
_nr
= html
.get_integer_input("edit", -1)
821 self
._clone
_nr
= html
.get_integer_input("clone", -1)
822 self
._new
= self
._edit
_nr
< 0
825 self
._users
= userdb
.load_users(lock
=html
.is_transaction())
826 if self
._user
_id
() not in self
._users
:
827 raise MKUserError(None, _("The user you are trying to edit "
828 "notification rules for does not exist."))
829 user
= self
._users
[self
._user
_id
()]
830 self
._rules
= user
.setdefault("notification_rules", [])
832 self
._rules
= watolib
.load_notification_rules(lock
=html
.is_transaction())
835 if self
._clone
_nr
>= 0 and not html
.var("_clear"):
838 self
._rule
.update(self
._rules
[self
._clone
_nr
])
840 raise MKUserError(None, _("This %s does not exist.") % "notification rule")
845 self
._rule
= self
._rules
[self
._edit
_nr
]
847 raise MKUserError(None, _("This %s does not exist.") % "notification rule")
849 def _valuespec(self
):
850 return self
._vs
_notification
_rule
(self
._user
_id
())
852 # TODO: Refactor this mess
853 def _vs_notification_rule(self
, userid
=None):
856 section_contacts
= []
857 section_override
= []
860 ( _("Contact Selection"), [ "contact_all", "contact_all_with_email", "contact_object",
861 "contact_users", "contact_groups", "contact_emails", "contact_match_macros",
862 "contact_match_groups", ] ),
868 title
= _("All contacts of the notified object"),
869 label
= _("Notify all contacts of the notified host or service."),
870 default_value
= True,
875 title
= _("All users"),
876 label
= _("Notify all users"),
879 ( "contact_all_with_email",
881 title
= _("All users with an email address"),
882 label
= _("Notify all users that have configured an email address in their profile"),
887 watolib
.UserSelection(only_contacts
= False),
888 title
= _("The following users"),
889 help = _("Enter a list of user IDs to be notified here. These users need to be members "
890 "of at least one contact group in order to be notified."),
892 add_label
= _("Add user"),
897 cmk
.gui
.plugins
.wato
.GroupSelection("contact"),
898 title
= _("The members of certain contact groups"),
904 valuespec
= EmailAddress(size
= 44),
905 title
= _("The following explicit email addresses"),
906 orientation
= "vertical",
909 ( "contact_match_macros",
914 title
= _("Name of the macro"),
915 help = _("As configured in the users settings. Do not add a leading underscore."),
919 title
= _("Required match (regular expression)"),
920 help = _("This expression must match the value of the variable"),
922 mode
= RegExp
.complete
,
926 title
= _("Restrict by custom macros"),
927 help = _("Here you can <i>restrict</i> the list of contacts that has been "
928 "built up by the previous options to those who have certain values "
929 "in certain custom macros. If you add more than one macro here then "
930 "<i>all</i> macros must match. The matches are regular expressions "
931 "that must fully match the value of the macro."),
932 add_label
= _("Add condition"),
935 ( "contact_match_groups",
937 cmk
.gui
.plugins
.wato
.GroupSelection("contact"),
938 title
= _("Restrict by contact groups"),
939 help = _("Here you can <i>restrict</i> the list of contacts that has been "
940 "built up by the previous options to those that are members of "
941 "selected contact groups. If you select more than one contact group here then "
942 "the user must be member of <i>all</i> these groups."),
943 add_label
= _("Add Group"),
951 title
= _("Overriding by users"),
952 help = _("If you uncheck this option then users are not allowed to deactive notifications "
953 "that are created by this rule."),
954 label
= _("allow users to deactivate this notification"),
955 default_value
= True,
962 title
= _("Maximum bulk size"),
963 label
= _("Bulk up to"),
964 unit
= _("Notifications"),
965 help = _("At most that many Notifications are kept back for bulking. A value of "
966 "1 essentially turns off notification bulking."),
967 default_value
= 1000,
971 self
._vs
_notification
_bulkby
(),
973 ("groupby_custom", ListOfStrings(
975 orientation
= "horizontal",
976 title
= _("Create separate notification bulks for different values of the following custom macros"),
977 help = _("If you enter the names of host/service-custom macros here then for each different "
978 "combination of values of those macros a separate bulk will be created. This can be used "
979 "in combination with the grouping by folder, host etc. Omit any leading underscore. "
980 "<b>Note</b>: If you are using "
981 "Nagios as a core you need to make sure that the values of the required macros are "
982 "present in the notification context. This is done in <tt>check_mk_templates.cfg</tt>. If you "
983 "macro is <tt>_FOO</tt> then you need to add the variables <tt>NOTIFY_HOST_FOO</tt> and "
984 "<tt>NOTIFY_SERVICE_FOO</tt>."),
986 ("bulk_subject", TextAscii(
987 title
= _("Subject for bulk notifications"),
988 help = _("Customize the subject for bulk notifications and overwrite "
989 "default subject <tt>Check_MK: $COUNT_NOTIFICATIONS$ notifications for HOST</tt>"
990 " resp. <tt>Check_MK: $COUNT_NOTIFICATIONS$ notifications for $COUNT_HOSTS$ hosts</tt>. "
991 "Both macros <tt>$COUNT_NOTIFICATIONS$</tt> and <tt>$COUNT_HOSTS$</tt> can be used in "
992 "any customized subject. If <tt>$COUNT_NOTIFICATIONS$</tt> is used, the amount of "
993 "notifications will be inserted and if you use <tt>$COUNT_HOSTS$</tt> then the "
994 "amount of hosts will be applied."),
996 default_value
= "Check_MK: $COUNT_NOTIFICATIONS$ notifications for $COUNT_HOSTS$ hosts"
1001 title
= _("Rule Properties"),
1002 elements
= rule_option_elements()
1004 + self
._rule
_match
_conditions
()
1009 watolib
.get_vs_notification_methods(),
1013 title
= "Notification Bulking",
1014 orientation
= "vertical",
1016 ("always", _("Always bulk"), Dictionary(
1017 help = _("Enabling the bulk notifications will collect several subsequent notifications "
1018 "for the same contact into one single notification, which lists of all the "
1019 "actual problems, e.g. in a single email. This cuts down the number of notifications "
1020 "in cases where many (related) problems occur within a short time."),
1023 title
= _("Time horizon"),
1024 label
= _("Bulk up to"),
1025 help = _("Notifications are kept back for bulking at most for this time."),
1030 optional_keys
= ["bulk_subject"],
1032 ("timeperiod", _("Bulk during timeperiod"), Dictionary(
1033 help = _("By enabling this option notifications will be bulked only if the "
1034 "specified timeperiod is active. When the timeperiod ends a "
1035 "bulk containing all notifications that appeared during that time "
1037 "If bulking should be enabled outside of the timeperiod as well, "
1038 "the option \"Also Bulk outside of timeperiod\" can be used."),
1041 watolib
.TimeperiodSelection(
1042 title
= _("Only bulk notifications during the following timeperiod"),
1044 ] + bulk_options
+ [
1045 ("bulk_outside", Dictionary(
1046 title
= _("Also bulk outside of timeperiod"),
1047 help = _("By enabling this option notifications will be bulked "
1048 "outside of the defined timeperiod as well."),
1051 title
= _("Time horizon"),
1052 label
= _("Bulk up to"),
1053 help = _("Notifications are kept back for bulking at most for this time."),
1058 optional_keys
= ["bulk_subject"],
1062 optional_keys
= ["bulk_subject", "bulk_outside"],
1066 forth
= lambda x
: x
if isinstance(x
, tuple) else ("always", x
)
1069 optional_keys
= [ "match_site", "match_folder", "match_hosttags", "match_hostgroups", "match_hosts", "match_exclude_hosts",
1070 "match_servicegroups", "match_exclude_servicegroups", "match_servicegroups_regex", "match_exclude_servicegroups_regex",
1071 "match_services", "match_exclude_services",
1072 "match_contacts", "match_contactgroups",
1073 "match_plugin_output",
1074 "match_timeperiod", "match_escalation", "match_escalation_throttle",
1075 "match_sl", "match_host_event", "match_service_event", "match_ec", "match_notification_comment",
1076 "match_checktype", "bulk", "contact_users", "contact_groups", "contact_emails",
1077 "contact_match_macros", "contact_match_groups" ],
1079 ( _("Rule Properties"), [ "description", "comment", "disabled", "docu_url", "allow_disable" ] ),
1080 ( _("Notification Method"), [ "notify_plugin", "notify_method", "bulk" ] ),]
1083 ( _("Conditions"), [ "match_site", "match_folder", "match_hosttags", "match_hostgroups",
1084 "match_hosts", "match_exclude_hosts", "match_servicegroups",
1085 "match_exclude_servicegroups", "match_servicegroups_regex", "match_exclude_servicegroups_regex",
1086 "match_services", "match_exclude_services",
1088 "match_contacts", "match_contactgroups",
1089 "match_plugin_output",
1091 "match_escalation", "match_escalation_throttle",
1092 "match_sl", "match_host_event", "match_service_event", "match_ec", "match_notification_comment" ] ),
1096 validate
= self
._validate
_notification
_rule
,
1099 def _validate_notification_rule(self
, rule
, varprefix
):
1100 if "bulk" in rule
and rule
["notify_plugin"][1] == None:
1101 raise MKUserError(varprefix
+ "_p_bulk_USE",
1102 _("It does not make sense to add a bulk configuration for cancelling rules."))
1104 if "bulk" in rule
or "bulk_period" in rule
:
1105 if rule
["notify_plugin"][0]:
1106 info
= watolib
.load_notification_scripts()[rule
["notify_plugin"][0]]
1107 if not info
["bulk"]:
1108 raise MKUserError(varprefix
+ "_p_notify_plugin",
1109 _("The notification script %s does not allow bulking.") % info
["title"])
1111 raise MKUserError(varprefix
+ "_p_notify_plugin",
1112 _("Legacy ASCII Emails do not support bulking. You can either disable notification "
1113 "bulking or choose another notification plugin which allows bulking."))
1117 raise NotImplementedError()
1120 def _back_mode(self
):
1121 raise NotImplementedError()
1126 return _("Create new notification rule for user %s") % self
._user
_id
()
1127 return _("Create new notification rule")
1130 return _("Edit notification rule %d of user %s") % (self
._edit
_nr
, self
._user
_id
())
1131 return _("Edit notification rule %d") % self
._edit
_nr
1134 html
.context_button(_("All Rules"),
1135 watolib
.folder_preserving_link([("mode", "notifications"), ("userid", self
._user
_id
())]), "back")
1138 if not html
.check_transaction():
1139 return self
._back
_mode
()
1141 vs
= self
._valuespec
()
1142 self
._rule
= vs
.from_html_vars("rule")
1144 self
._rule
["contact_users"] = [self
._user
_id
()] # Force selection of our user
1146 vs
.validate_value(self
._rule
, "rule")
1149 # User rules are always allow_disable
1150 # The parameter is set just after the validation, since the allow_disable
1151 # key isn't in the valuespec. Curiously, the validation does not fail
1152 # even the allow_disable key is set before the validate_value...
1153 self
._rule
["allow_disable"] = True
1155 if self
._new
and self
._clone
_nr
>= 0:
1156 self
._rules
[self
._clone
_nr
:self
._clone
_nr
] = [self
._rule
]
1158 self
._rules
[0:0] = [self
._rule
]
1160 self
._rules
[self
._edit
_nr
] = self
._rule
1163 userdb
.save_users(self
._users
)
1165 watolib
.save_notification_rules(self
._rules
)
1168 log_what
= "new-notification-rule"
1170 log_text
= _("Created new notification rule for user %s") % self
._user
_id
()
1172 log_text
= _("Created new notification rule")
1174 log_what
= "edit-notification-rule"
1176 log_text
= _("Changed notification rule %d of user %s") % (self
._edit
_nr
, self
._user
_id
())
1178 log_text
= _("Changed notification rule %d") % self
._edit
_nr
1179 self
._add
_change
(log_what
, log_text
)
1181 return self
._back
_mode
()
1184 if self
._start
_async
_repl
:
1185 cmk
.gui
.wato
.user_profile
.user_profile_async_replication_dialog(
1186 sites
=watolib
.get_notification_sync_sites())
1189 html
.begin_form("rule", method
="POST")
1190 vs
= self
._valuespec
()
1191 vs
.render_input("rule", self
._rule
)
1192 vs
.set_focus("rule")
1194 html
.button("save", _("Save"))
1195 html
.hidden_fields()
1199 @mode_registry.register
1200 class ModeEditNotificationRule(EditNotificationRuleMode
):
1203 return "notification_rule"
1206 def permissions(cls
):
1207 return ["notifications"]
1210 return html
.get_unicode_input("user")
1212 def _back_mode(self
):
1214 return "user_notifications"
1215 return "notifications"
1218 @mode_registry.register
1219 class ModeEditPersonalNotificationRule(EditNotificationRuleMode
):
1222 return "notification_rule_p"
1225 def permissions(cls
):
1229 super(ModeEditPersonalNotificationRule
, self
).__init
__()
1230 config
.user
.need_permission("general.edit_notifications")
1233 return config
.user
.id
1235 def _add_change(self
, log_what
, log_text
):
1236 if config
.has_wato_slave_sites():
1237 self
._start
_async
_repl
= True
1238 watolib
.log_audit(None, log_what
, log_text
)
1240 super(ModeEditPersonalNotificationRule
, self
)._add
_change
(log_what
, log_text
)
1242 def _back_mode(self
):
1243 if config
.has_wato_slave_sites():
1245 return "user_notifications_p"
1249 return _("Create new notification rule")
1250 return _("Edit notification rule %d") % self
._edit
_nr
1253 html
.context_button(_("All Rules"),
1254 watolib
.folder_preserving_link([("mode", "user_notifications_p")]), "back")