diff --git a/module/helper.py b/module/helper.py
index 7f5b571f..7583e43e 100644
--- a/module/helper.py
+++ b/module/helper.py
@@ -749,5 +749,160 @@ def get_contact_avatar(self, contact, size=24, with_name=True, with_link=True):
return s
+ def get_event_icon(self, event, disabled=False, label='', use_title=True):
+ '''
+ Get an Html formatted string to display a monitoring event
+
+ If disabled is True, the font used is greyed
+
+ If label is empty, only an icon is returned
+ If label is set as 'state', the icon title is used as text
+ Else, the content of label is used as text near the icon.
+
+ If use_title is False, do not include title attribute.
+
+ Returns a span element containing a Font Awesome icon that depicts
+ consistently the event and its state
+ '''
+ cls = event.get('type', 'unknown').lower()
+ state = event.get('state', 'n/a').upper()
+ state_type = event.get('state_type', 'n/a').upper()
+ hard = (state_type == 'HARD')
+
+ # Icons depending upon element and real state ...
+ # ; History
+ icons = {
+ "unknown": {
+ "class": "history_Unknown",
+ "text": "Unknown event",
+ "icon": "question"
+ },
+
+ "retention_load": {
+ "class": "history_RetentionLoad",
+ "text": "Retention load",
+ "icon": "save"
+ },
+ "retention_save": {
+ "class": "history_RetentionSave",
+ "text": "Retention save",
+ "icon": "save"
+ },
+
+ "alert": {
+ "class": "history_Alert",
+ "text": "Monitoring alert",
+ "icon": "bolt"
+ },
+
+ "notification": {
+ "class": "history_Notification",
+ "text": "Monitoring notification sent",
+ "icon": "paper-plane"
+ },
+
+ "check_result": {
+ "class": "history_CheckResult",
+ "text": "Check result",
+ "icon": "bolt"
+ },
+
+ "comment": {
+ "class": "history_WebuiComment",
+ "text": "WebUI comment",
+ "icon": "send"
+ },
+ "timeperiod_transition": {
+ "class": "history_TimeperiodTransition",
+ "text": "Timeperiod transition",
+ "icon": "clock-o"
+ },
+ "external_command": {
+ "class": "history_ExternalCommand",
+ "text": "External command",
+ "icon": "wrench"
+ },
+
+ "event_handler": {
+ "class": "history_EventHandler",
+ "text": "Monitoring event handler",
+ "icon": "bolt"
+ },
+ "flapping_start": {
+ "class": "history_FlappingStart",
+ "text": "Monitoring flapping start",
+ "icon": "flag"
+ },
+ "flapping_stop": {
+ "class": "history_FlappingStop",
+ "text": "Monitoring flapping stop",
+ "icon": "flag-o"
+ },
+ "downtime_start": {
+ "class": "history_DowntimeStart",
+ "text": "Monitoring downtime start",
+ "icon": "ambulance"
+ },
+ "downtime_cancelled": {
+ "class": "history_DowntimeCancelled",
+ "text": "Monitoring downtime cancelled",
+ "icon": "ambulance"
+ },
+ "downtime_end": {
+ "class": "history_DowntimeEnd",
+ "text": "Monitoring downtime stopped",
+ "icon": "ambulance"
+ },
+ "acknowledge_start": {
+ "class": "history_AckStart",
+ "text": "Monitoring acknowledge start",
+ "icon": "check"
+ },
+ "acknowledge_cancelled": {
+ "class": "history_AckCancelled",
+ "text": "Monitoring acknowledge cancelled",
+ "icon": "check"
+ },
+ "acknowledge_end": {
+ "class": "history_AckEnd",
+ "text": "Monitoring acknowledge expired",
+ "icon": "check"
+ },
+ }
+
+ back = '''''' \
+ % (state.lower() if not disabled else 'greyed')
+
+ icon_color = 'fa-inverse'
+ icon_style = ""
+ if not hard:
+ icon_style = 'style="opacity: 0.5"'
+
+ try:
+ icon = icons[cls]['icon']
+ title = icons[cls]['text']
+ except KeyError:
+ cls = 'unknown'
+ icon = icons[cls]['icon']
+ title = icons[cls]['text']
+
+ front = '''''' % (icon, icon_color)
+
+ if use_title:
+ icon_text = '''%s%s''' % (icon_style, title, back, front)
+ else:
+ icon_text = '''%s%s''' % (icon_style, back, front)
+
+ if label == '':
+ return icon_text
+
+ color = state.lower() if not disabled else 'greyed'
+ if label == 'title':
+ label = title
+ return '''
+
+ %s %s
+
+ ''' % (color, icon_text, label)
helper = Helper()
diff --git a/module/logevent.py b/module/logevent.py
new file mode 100755
index 00000000..0bde6d62
--- /dev/null
+++ b/module/logevent.py
@@ -0,0 +1,317 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2015-2016: Alignak team, see AUTHORS.txt file for contributors
+#
+# This file is part of Alignak.
+#
+# Alignak is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Alignak is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Alignak. If not, see .
+#
+#
+# This file incorporates work covered by the following copyright and
+# permission notice:
+#
+# Copyright (C) 2009-2014:
+# Thibault Cohen, titilambert@gmail.com
+# Grégory Starck, g.starck@gmail.com
+# aviau, alexandre.viau@savoirfairelinux.com
+# Sebastien Coavoux, s.coavoux@free.fr
+
+# This file is part of Shinken.
+#
+# Shinken is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Shinken is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Shinken. If not, see .
+"""
+This module lists provide facilities to parse log type Broks.
+The supported event are listed in the event_type variable
+"""
+
+import re
+
+# pylint: disable=bad-continuation
+EVENT_TYPE_PATTERN = re.compile(
+ r'^(TIMEPERIOD TRANSITION|EXTERNAL COMMAND|RETENTION SAVE|RETENTION LOAD|'
+ r'CURRENT HOST STATE|CURRENT SERVICE STATE|HOST COMMENT|SERVICE COMMENT|'
+ r'HOST NOTIFICATION|SERVICE NOTIFICATION|HOST ALERT|SERVICE ALERT|'
+ r'HOST EVENT HANDLER|SERVICE EVENT HANDLER|ACTIVE HOST CHECK|ACTIVE SERVICE CHECK|'
+ r'PASSIVE HOST CHECK|PASSIVE SERVICE CHECK|HOST ACKNOWLEDGE ALERT|SERVICE ACKNOWLEDGE ALERT|'
+ r'HOST DOWNTIME ALERT|SERVICE DOWNTIME ALERT|HOST FLAPPING ALERT|SERVICE FLAPPING ALERT)'
+ r'($|: .*)'
+)
+EVENT_TYPES = {
+ 'TIMEPERIOD': {
+ # [1490998324] RETENTION SAVE
+ 'pattern': r'^(TIMEPERIOD) (TRANSITION): (.*)',
+ 'properties': [
+ 'event_type', # 'TIMEPERIOD'
+ 'state_type', # 'TRANSITION'
+ 'output',
+ ]
+ },
+ 'RETENTION': {
+ # [1490998324] RETENTION SAVE
+ 'pattern': r'^(RETENTION) (LOAD|SAVE): (.*)',
+ 'properties': [
+ 'event_type', # 'RETENTION'
+ 'state_type', # 'LOAD' or 'SAVE'
+ 'output', # 'scheduler name
+ ]
+ },
+ 'EXTERNAL': {
+ # [1490997636] EXTERNAL COMMAND: [1490997512]
+ # PROCESS_HOST_CHECK_RESULT;ek3022sg-0001;0;EK3022SG-0001 is alive,
+ # uptime is 43639 seconds (0 days 12 hours 7 minutes 19 seconds 229 ms)|'Uptime'=43639
+ 'pattern': r'^(EXTERNAL COMMAND): (\[.*\]) (.*)$',
+ 'properties': [
+ 'event_type', # 'EXTERNAL COMMAND'
+ 'timestamp', # '[1490997512]'
+ 'command', # 'PROCESS_SERVICE_CHECK_RESULT;ek3022sg-0001;svc_Screensaver;0;Ok|'ScreensaverOff'=61c
+ ]
+ },
+ 'CURRENT': {
+ # ex: "[1498108167] CURRENT HOST STATE: localhost;UP;HARD;1;Host assumed to be UP"
+ # ex: "[1498108167] CURRENT SERVICE STATE: localhost;Maintenance;UNKNOWN;HARD;0;"
+ 'pattern': r'^CURRENT (HOST|SERVICE) (STATE): '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^\;]*);([^\;]*);([^\;]*)',
+ 'properties': [
+ 'item_type', # 'SERVICE' (or could be 'HOST')
+ 'event_type', # 'STATE'
+ 'hostname', # 'localhost'
+ 'service_desc', # 'Maintenance' (or could be None)
+ 'state', # 'UP'
+ 'state_type', # 'HARD'
+ 'attempts', # '0'
+ 'output', # 'WARNING - load average: 5.04, 4.67, 5.04'
+ ]
+ },
+ 'ACTIVE': {
+ # ex: "[1402515279] ACTIVE SERVICE CHECK: localhost;Nrpe-status;OK;HARD;1;NRPE v2.15"
+ 'pattern': r'^(ACTIVE) (HOST|SERVICE) (CHECK): '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^\;]*);([^\;]*);([^\;]*)',
+ 'properties': [
+ 'check_type', # 'ACTIVE'
+ 'item_type', # 'SERVICE' (or could be 'HOST')
+ 'event_type', # 'CHECK'
+ 'hostname', # 'localhost'
+ 'service_desc', # 'cpu load maui' (or could be None)
+ 'state', # 'WARNING'
+ 'state_type', # 'HARD'
+ 'attempts', # '0'
+ 'output', # 'NRPE v2.15'
+ ]
+ },
+ 'PASSIVE': {
+ # ex: "[1402515279] PASSIVE SERVICE CHECK: localhost;nsca_uptime;0;OK: uptime: 02:38h,
+ # boot: 2017-08-31 06:18:03 (UTC)|'uptime'=9508s;2100;90000"
+ 'pattern': r'^(PASSIVE) (HOST|SERVICE) (CHECK): '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^$]*)',
+ 'properties': [
+ 'check_type', # 'PASSIVE'
+ 'item_type', # 'SERVICE' (or could be 'HOST')
+ 'event_type', # 'CHECK'
+ 'hostname', # 'localhost'
+ 'service_desc', # 'cpu load maui' (or could be None)
+ 'state_id', # '0'
+ 'output', # 'K: uptime: 02:38h, boot: 2017-08-31 06:18:03 (UTC)
+ # |'uptime'=9508s;2100;90000'
+ ]
+ },
+ 'NOTIFICATION': {
+ # ex: "[1402515279] SERVICE NOTIFICATION:
+ # admin;localhost;check-ssh;CRITICAL;notify-service-by-email;Connection refused"
+ 'pattern': r'(HOST|SERVICE) (NOTIFICATION): '
+ r'([^\;]*);([^\;]*);(?:([^\;]*);)?([^\;]*);([^\;]*);([^\;]*)',
+ 'properties': [
+ 'item_type', # 'SERVICE' (or could be 'HOST')
+ 'event_type', # 'NOTIFICATION'
+ 'contact', # 'admin'
+ 'hostname', # 'localhost'
+ 'service_desc', # 'check-ssh' (or could be None)
+ 'state', # 'CRITICAL'
+ 'notification_method', # 'notify-service-by-email'
+ 'output', # 'Connection refused'
+ ]
+ },
+ 'ALERT': {
+ # ex: "[1329144231] SERVICE ALERT:
+ # dfw01-is02-006;cpu load maui;WARNING;HARD;4;WARNING - load average: 5.04, 4.67, 5.04"
+ 'pattern': r'^(HOST|SERVICE) (ALERT): '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^\;]*);([^\;]*);([^\;]*)',
+ 'properties': [
+ 'item_type', # 'SERVICE' (or could be 'HOST')
+ 'event_type', # 'ALERT'
+ 'hostname', # 'localhost'
+ 'service_desc', # 'cpu load maui' (or could be None)
+ 'state', # 'WARNING'
+ 'state_type', # 'HARD'
+ 'attempts', # '4'
+ 'output', # 'WARNING - load average: 5.04, 4.67, 5.04'
+ ]
+ },
+ 'EVENT': {
+ # ex: "[1329144231] HOST EVENT HANDLER: host-03;DOWN;HARD;0;g_host_event_handler"
+ 'pattern': r'^(HOST|SERVICE) (EVENT HANDLER): '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^\;]*);([^\;]*);([^\;]*)',
+ 'properties': [
+ 'item_type', # 'SERVICE' (or could be 'HOST')
+ 'event_type', # 'EVENT HANDLER'
+ 'hostname', # 'localhost'
+ 'service_desc', # 'cpu load maui' (or could be None)
+ 'state', # 'WARNING'
+ 'state_type', # 'HARD'
+ 'attempts', # '4'
+ 'output', # 'g_host_event_handler'
+ ]
+ },
+ 'COMMENT': {
+ # ex: "[1329144231] SERVICE COMMENT:
+ # dfw01-is02-006;cpu load maui;author;Comment text"
+ 'pattern': r'^(HOST|SERVICE) (COMMENT): '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^$]*)',
+ 'properties': [
+ 'item_type', # 'SERVICE' (or could be 'HOST')
+ 'event_type', # 'COMMENT'
+ 'hostname', # 'localhost'
+ 'service_desc', # 'cpu load maui' (or could be None)
+ 'author',
+ 'comment', # 'WARNING - load average: 5.04, 4.67, 5.04'
+ ]
+ },
+ 'ACKNOWLEDGE': {
+ # ex: "[1279250211] HOST ACKNOWLEDGE STARTED:
+ # maast64;Host has been acknowledged"
+ 'pattern': r'^(HOST|SERVICE) (ACKNOWLEDGE) ALERT: '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^\;]*)',
+ 'properties': [
+ 'item_type', # 'SERVICE' or 'HOST'
+ 'event_type', # 'ACKNOWLEDGE'
+ 'hostname', # The hostname
+ 'service_desc', # The service description or None
+ 'state', # 'STARTED' or 'EXPIRED' or 'CANCELLED'
+ 'output', # 'Host has been acknowledged'
+ ]
+ },
+ 'DOWNTIME': {
+ # ex: "[1279250211] HOST DOWNTIME ALERT:
+ # maast64;STARTED; Host has entered a period of scheduled downtime"
+ 'pattern': r'^(HOST|SERVICE) (DOWNTIME) ALERT: '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^\;]*)',
+ 'properties': [
+ 'item_type', # 'SERVICE' or 'HOST'
+ 'event_type', # 'DOWNTIME'
+ 'hostname', # The hostname
+ 'service_desc', # The service description or None
+ 'state', # 'STARTED' or 'STOPPED' or 'CANCELLED'
+ 'output', # 'Service appears to have started flapping (24% change >= 20.0% threshold)'
+ ]
+ },
+ 'FLAPPING': {
+ # service flapping ex: "[1375301662] SERVICE FLAPPING ALERT:
+ # testhost;check_ssh;STARTED;
+ # Service appears to have started flapping (24.2% change >= 20.0% threshold)"
+
+ # host flapping ex: "[1375301662] HOST FLAPPING ALERT:
+ # hostbw;STARTED; Host appears to have started flapping (20.1% change > 20.0% threshold)"
+ 'pattern': r'^(HOST|SERVICE) (FLAPPING) ALERT: '
+ r'([^\;]*);(?:([^\;]*);)?([^\;]*);([^\;]*)',
+ 'properties': [
+ 'item_type', # 'SERVICE' or 'HOST'
+ 'event_type', # 'FLAPPING'
+ 'hostname', # The hostname
+ 'service_desc', # The service description or None
+ 'state', # 'STOPPED' or 'STARTED'
+ 'output', # 'Service appears to have started flapping (24% change >= 20.0% threshold)'
+ ]
+ }
+}
+
+# SERVICE ACKNOWLEDGE ALERT: south_host_004;dummy_critical;STARTED; Service problem has been acknowledged
+
+class LogEvent(object): # pylint: disable=too-few-public-methods
+ """Class for parsing event logs
+ Populates self.data with the log type's properties
+ """
+
+ def __init__(self, log):
+ self.data = {}
+ self.syntax = False
+ self.valid = False
+ self.time = None
+ self.event_type = 'unknown'
+ self.pattern = 'unknown'
+
+ # Find the type of event
+ event_type_match = EVENT_TYPE_PATTERN.match(log)
+ if not event_type_match:
+ return
+ self.syntax = True
+
+ matched = event_type_match.group(1)
+ # print("Matched: %s" % matched)
+ matched = matched.split()
+ self.pattern = matched[0]
+ if self.pattern in ['HOST', 'SERVICE']:
+ self.pattern = matched[1]
+ # print("Pattern: %s" % self.pattern)
+
+ # parse it with it's pattern
+ if self.pattern not in EVENT_TYPES:
+ return
+
+ event_type = EVENT_TYPES[self.pattern]
+ properties_match = re.match(event_type['pattern'], log)
+ # print("Properties math: %s" % properties_match)
+ if not properties_match:
+ return
+
+ self.valid = True
+
+ # Populate self.data with the event's properties
+ for i, prop in enumerate(event_type['properties']):
+ self.data[prop] = properties_match.group(i + 1)
+
+ # # Convert the time to int
+ # self.data['time'] = int(self.data['time'])
+
+ # Convert event_type to int
+ if 'event_type' in self.data:
+ self.event_type = self.data['event_type']
+
+ # Convert attempts to int
+ if 'attempts' in self.data:
+ self.data['attempts'] = int(self.data['attempts'])
+
+ def __iter__(self):
+ return iter(self.data.items())
+
+ def __len__(self):
+ return len(self.data)
+
+ def __getitem__(self, key):
+ return self.data[key]
+
+ def __contains__(self, key):
+ return key in self.data
+
+ def __str__(self):
+ return str(self.data)
diff --git a/module/plugins/system/system.py b/module/plugins/system/system.py
index 91234029..a320abd9 100644
--- a/module/plugins/system/system.py
+++ b/module/plugins/system/system.py
@@ -30,6 +30,7 @@
import traceback
from copy import deepcopy
+from logevent import LogEvent
from shinken.log import logger
@@ -37,6 +38,383 @@
app = None
+def _get_alignak_livesynthesis():
+ """Get Alignak livesynthesis from the Arbiter API:
+ {
+ "alignak": "My Alignak",
+ "livesynthesis": {
+ "_overall": {
+ "_freshness": 1534237749,
+ "livesynthesis": {
+ "hosts_acknowledged": 0,
+ "hosts_down_hard": 0,
+ "hosts_down_soft": 0,
+ "hosts_flapping": 0,
+ "hosts_in_downtime": 0,
+ "hosts_not_monitored": 0,
+ "hosts_total": 13,
+ "hosts_unreachable_hard": 0,
+ "hosts_unreachable_soft": 0,
+ "hosts_up_hard": 13,
+ "hosts_up_soft": 0,
+ "services_acknowledged": 0,
+ "services_critical_hard": 6,
+ "services_critical_soft": 4,
+ "services_flapping": 0,
+ "services_in_downtime": 0,
+ "services_not_monitored": 0,
+ "services_ok_hard": 70,
+ "services_ok_soft": 0,
+ "services_total": 100,
+ "services_unknown_hard": 4,
+ "services_unknown_soft": 6,
+ "services_unreachable_hard": 0,
+ "services_unreachable_soft": 0,
+ "services_warning_hard": 5,
+ "services_warning_soft": 5
+ }
+ },
+ "scheduler-master": {
+ "_freshness": 1534237747,
+ "livesynthesis": {
+ "hosts_acknowledged": 0,
+ "hosts_down_hard": 0,
+ "hosts_down_soft": 0,
+ "hosts_flapping": 0,
+ "hosts_in_downtime": 0,
+ "hosts_not_monitored": 0,
+ "hosts_total": 13,
+ "hosts_unreachable_hard": 0,
+ "hosts_unreachable_soft": 0,
+ "hosts_up_hard": 13,
+ "hosts_up_soft": 0,
+ "services_acknowledged": 0,
+ "services_critical_hard": 6,
+ "services_critical_soft": 4,
+ "services_flapping": 0,
+ "services_in_downtime": 0,
+ "services_not_monitored": 0,
+ "services_ok_hard": 70,
+ "services_ok_soft": 0,
+ "services_total": 100,
+ "services_unknown_hard": 4,
+ "services_unknown_soft": 6,
+ "services_unreachable_hard": 0,
+ "services_unreachable_soft": 0,
+ "services_warning_hard": 5,
+ "services_warning_soft": 5
+ }
+ }
+ },
+ "name": "arbiter-master",
+ "running_id": "1534237614.73657398",
+ "start_time": 1534237614,
+ "type": "arbiter",
+ "version": "2.0.0rc2"
+ }
+ """
+ if not getattr(app, 'alignak_endpoint', None):
+ logger.info("[WebUI-system] Alignak is not configured. Redirecting to the home page.")
+ app.bottle.redirect(app.get_url("Dashboard"))
+
+ logger.debug("[WebUI-system] Get Alignak livesynthesis, endpoint: %s", app.alignak_endpoint)
+ try:
+ req = requests.Session()
+ raw_data = req.get("%s/livesynthesis" % app.alignak_endpoint)
+ data = json.loads(raw_data.content)
+ logger.debug("[WebUI-system] Result: %s", data)
+ except Exception as exp:
+ logger.error("[WebUI-system] alignak_livesynthesis, exception: %s", exp)
+ app.request.environ['MSG'] = "Alignak Error"
+ app.bottle.redirect(app.get_url("Dashboard"))
+
+ return data
+
+
+def _get_alignak_status():
+ """Get Alignak overall status from the Arbiter API:
+ {
+ "livestate": {
+ "long_output": "broker-master - daemon is alive and reachable.\npoller-master - daemon is alive and reachable.\nreactionner-master - daemon is not reachable.\nreceiver-master - daemon is alive and reachable.\nscheduler-master - daemon is alive and reachable.",
+ "output": "Some of my daemons are not reachable.",
+ "perf_data": "'modules'=2 'timeperiods'=4 'services'=100 'servicegroups'=1 'commands'=10 'hosts'=13 'hostgroups'=5 'contacts'=2 'contactgroups'=2 'notificationways'=2 'checkmodulations'=0 'macromodulations'=0 'servicedependencies'=40 'hostdependencies'=0 'arbiters'=1 'schedulers'=1 'reactionners'=1 'brokers'=1 'receivers'=1 'pollers'=1 'realms'=1 'resultmodulations'=0 'businessimpactmodulations'=0 'escalations'=0 'hostsextinfo'=0 'servicesextinfo'=0",
+ "state": "up",
+ "timestamp": 1542611507
+ },
+ "name": "My Alignak",
+ "services": [
+ {
+ "livestate": {
+ "long_output": "",
+ "output": "warning because some daemons are not reachable.",
+ "perf_data": "",
+ "state": "warning",
+ "timestamp": 1542611507
+ },
+ "name": "arbiter-master"
+ },
+ {
+ "livestate": {
+ "long_output": "Realm: All (True). Listening on: http://127.0.0.1:7772/",
+ "name": "broker_broker-master",
+ "output": "daemon is alive and reachable.",
+ "perf_data": "last_check=0.00",
+ "state": "ok",
+ "timestamp": 1542611507
+ },
+ "name": "broker-master"
+ },
+ {
+ "livestate": {
+ "long_output": "Realm: All (True). Listening on: http://127.0.0.1:7771/",
+ "name": "poller_poller-master",
+ "output": "daemon is alive and reachable.",
+ "perf_data": "last_check=0.00",
+ "state": "ok",
+ "timestamp": 1542611507
+ },
+ "name": "poller-master"
+ },
+ {
+ "livestate": {
+ "long_output": "Realm: All (True). Listening on: http://127.0.0.1:7769/",
+ "name": "reactionner_reactionner-master",
+ "output": "daemon is not reachable.",
+ "perf_data": "last_check=0.00",
+ "state": "warning",
+ "timestamp": 1542611507
+ },
+ "name": "reactionner-master"
+ },
+ {
+ "livestate": {
+ "long_output": "Realm: All (True). Listening on: http://127.0.0.1:7773/",
+ "name": "receiver_receiver-master",
+ "output": "daemon is alive and reachable.",
+ "perf_data": "last_check=0.00",
+ "state": "ok",
+ "timestamp": 1542611507
+ },
+ "name": "receiver-master"
+ },
+ {
+ "livestate": {
+ "long_output": "Realm: All (True). Listening on: http://127.0.0.1:7768/",
+ "name": "scheduler_scheduler-master",
+ "output": "daemon is alive and reachable.",
+ "perf_data": "last_check=0.00",
+ "state": "ok",
+ "timestamp": 1542611507
+ },
+ "name": "scheduler-master"
+ }
+ ],
+ "template": {
+ "_templates": [
+ "alignak",
+ "important"
+ ],
+ "active_checks_enabled": false,
+ "alias": "My Alignak",
+ "notes": "",
+ "passive_checks_enabled": true
+ },
+ "variables": {}
+ }
+ """
+ if not getattr(app, 'alignak_endpoint', None):
+ logger.info("[WebUI-system] Alignak is not configured. Redirecting to the home page.")
+ app.bottle.redirect(app.get_url("Dashboard"))
+
+ logger.debug("[WebUI-system] Get Alignak status, endpoint: %s", app.alignak_endpoint)
+ try:
+ req = requests.Session()
+ raw_data = req.get("%s/status" % app.alignak_endpoint)
+ data = json.loads(raw_data.content)
+ logger.debug("[WebUI-system] Result: %s", data)
+ except Exception as exp:
+ logger.error("[WebUI-system] alignak_status, exception: %s", exp)
+ app.request.environ['MSG'] = "Alignak Error"
+ app.bottle.redirect(app.get_url("Dashboard"))
+
+ return data
+
+
+def alignak_status():
+ """Alignak livestate view:
+ live state and live synthesis information from the arbiter"""
+
+ return {
+ 'ls': _get_alignak_livesynthesis(),
+ 'status': _get_alignak_status()
+ }
+
+
+def alignak_events():
+ """Get Alignak Arbiter events:
+ """
+ if not getattr(app, 'alignak_endpoint', None):
+ logger.info("[WebUI-system] Alignak is not configured. Redirecting to the home page.")
+ app.bottle.redirect(app.get_url("Dashboard"))
+
+ user = app.request.environ['USER']
+ _ = user.is_administrator() or app.redirect403()
+
+ midnight_timestamp = time.mktime(datetime.date.today().timetuple())
+ try:
+ range_start = int(app.request.query.get('range_start', midnight_timestamp))
+ except ValueError:
+ range_start = midnight_timestamp
+
+ try:
+ range_end = int(app.request.query.get('range_end', midnight_timestamp + 86399))
+ except ValueError:
+ range_end = midnight_timestamp + 86399
+ logger.debug("[WebUI-logs] get_global_history, range: %d - %d", range_start, range_end)
+
+ # Apply search filter if exists ...
+ search = app.request.query.get('search', "type:host")
+ if "type:host" not in search:
+ search = "type:host " + search
+ logger.debug("[WebUI-system] search parameters '%s'", search)
+
+ filters = ','.join(app.request.query.getall('filter')) or ""
+ logger.debug("[WebUI-system] filters: %s", filters)
+
+ # Fetch elements per page preference for user, default is 25
+ elts_per_page = app.prefs_module.get_ui_user_preference(user, 'elts_per_page', 25)
+
+ # We want to limit the number of elements
+ step = int(app.request.query.get('step', elts_per_page))
+ start = int(app.request.query.get('start', '0'))
+ end = int(app.request.query.get('end', start + step))
+
+ items = []
+ for log in app.alignak_events:
+ # Try to get a monitoring event
+ try:
+ logger.debug("Log: %s", log)
+ event = LogEvent(log['message'])
+ logger.debug("-> event: %s", event)
+ if not event.valid:
+ logger.warning("No monitoring event detected from: %s", log['message'])
+ continue
+
+ # -------------------------------------------
+ data = deepcopy(log)
+ if event.event_type == 'RETENTION':
+ type = "retention_save"
+ if event.data['state_type'].upper() == 'LOAD':
+ type = "retention_load"
+ data.update({
+ "type": type
+ })
+
+ if event.event_type == 'TIMEPERIOD':
+ data.update({
+ "type": "timeperiod_transition",
+ })
+
+ if event.event_type == 'EXTERNAL COMMAND':
+ data.update({
+ "type": "external_command",
+ "message": event.data['command']
+ })
+
+ if event.event_type == 'ALERT':
+ data.update({
+ "host_name": event.data['hostname'],
+ "service_name": event.data['service_desc'] or 'n/a',
+ "state": event.data['state'],
+ "state_type": event.data['state_type'],
+ "type": "alert",
+ })
+
+ if event.event_type == 'NOTIFICATION':
+ data.update({
+ "host_name": event.data['hostname'],
+ "service_name": event.data['service_desc'] or 'n/a',
+ "type": "notification",
+ })
+
+ if event.event_type == 'DOWNTIME':
+ downtime_type = "downtime_start"
+ if event.data['state'] == 'STOPPED':
+ downtime_type = "downtime_end"
+ if event.data['state'] == 'CANCELLED':
+ downtime_type = "downtime_cancelled"
+
+ data.update({
+ "host_name": event.data['hostname'],
+ "service_name": event.data['service_desc'] or 'n/a',
+ "user_name": "Alignak",
+ "type": downtime_type,
+ })
+
+ if event.event_type == 'ACKNOWLEDGE':
+ ack_type = "acknowledge_start"
+ if event.data['state'] == 'EXPIRED':
+ ack_type = "acknowledge_end"
+ if event.data['state'] == 'CANCELLED':
+ ack_type = "acknowledge_cancelled"
+
+ data.update({
+ "host_name": event.data['hostname'],
+ "service_name": event.data['service_desc'] or 'n/a',
+ "user_name": "Alignak",
+ "type": ack_type,
+ })
+
+ if event.event_type == 'FLAPPING':
+ flapping_type = "monitoring.flapping_start"
+ if event.data['state'] == 'STOPPED':
+ flapping_type = "monitoring.flapping_stop"
+
+ data.update({
+ "host_name": event.data['hostname'],
+ "service_name": event.data['service_desc'] or 'n/a',
+ "user_name": "Alignak",
+ "type": flapping_type,
+ })
+
+ if event.event_type == 'COMMENT':
+ data.update({
+ "host_name": event.data['hostname'],
+ "service_name": event.data['service_desc'] or 'n/a',
+ "user_name": event.data['author'] or 'Alignak',
+ "type": "comment",
+ })
+
+ if filters and data.get('type', 'unknown') not in filters:
+ continue
+
+ items.append(data)
+ except ValueError:
+ logger.warning("Unable to decode a monitoring event from: %s", log['message'])
+ logger.warning(traceback.format_exc())
+ continue
+
+ # If we overflow, came back as normal
+ total = len(items)
+ if start > total:
+ start = 0
+ end = step
+
+ navi = app.helper.get_navi(total, start, step=step)
+
+ logger.info("[WebUI-system] got %d matching items", len(items))
+
+ return {
+ 'navi': navi,
+ 'page': "alignak/events",
+ 'logs': items[start:end],
+ 'total': total,
+ "filters": filters,
+ 'range_start': range_start,
+ 'range_end': range_end
+ }
+
+
def system_parameters():
user = app.request.environ['USER']
_ = user.is_administrator() or app.redirect403()
@@ -153,5 +531,11 @@ def system_widget():
alignak_parameters: {
'name': 'AlignakParameters', 'route': '/alignak/parameters', 'view': 'alignak-parameters',
'static': True
- }
+ },
+ alignak_status: {
+ 'name': 'AlignakStatus', 'route': '/alignak/status', 'view': 'alignak_status'
+ },
+ alignak_events: {
+ 'name': 'AlignakEvents', 'route': '/alignak/events', 'view': 'alignak_events'
+ },
}
diff --git a/module/plugins/system/views/alignak_events.tpl b/module/plugins/system/views/alignak_events.tpl
new file mode 100644
index 00000000..318f3e82
--- /dev/null
+++ b/module/plugins/system/views/alignak_events.tpl
@@ -0,0 +1,129 @@
+%rebase("layout", title='Alignak events log', css=['system/css/multiselect.css'], js=['system/js/multiselect.js'], breadcrumb=[ ['Alignak events log', '/alignak/events'] ])
+
+%helper = app.helper
+
+
+
+
+
+
{{"%d total matching items" % total}}
+
+
+
+
+ %event_types = { "retention_load", "retention_save", "alert", "notification", "check_result", "webui_comment", "timeperiod_transition", "event_handler", "flapping_start", "flapping_stop", "downtime_start", "downtime_cancelled", "downtime_end", "acknowledge_start", "acknowledge_end" }
+
+
+
+
+ %if not logs:
+
+ %else:
+
+ %end
+
+
+
+
diff --git a/module/plugins/system/views/alignak_ls.tpl b/module/plugins/system/views/alignak_ls.tpl
new file mode 100644
index 00000000..d8ea8d86
--- /dev/null
+++ b/module/plugins/system/views/alignak_ls.tpl
@@ -0,0 +1,14 @@
+%rebase("layout", title='Alignak livesynthesis', css=['system/css/alignak.css'], js=['system/js/jquery.floatThead.min.js'], breadcrumb=[ ['Alignak livesynthesis', '/alignak/ls'] ])
+
+%helper = app.helper
+
+
+
+ %if not ls:
+
+ No live synthesis information is available.
+
+ %else:
+ %end
+
+
diff --git a/module/plugins/system/views/alignak_status.tpl b/module/plugins/system/views/alignak_status.tpl
new file mode 100644
index 00000000..3829ca57
--- /dev/null
+++ b/module/plugins/system/views/alignak_status.tpl
@@ -0,0 +1,78 @@
+%rebase("layout", title='Alignak livesynthesis', css=['system/css/alignak.css'], js=['system/js/jquery.floatThead.min.js'], breadcrumb=[ ['Alignak status', '/alignak/status'] ])
+
+%helper = app.helper
+
+
+
+ %if not status:
+
+ %else:
+ %livestate = status['livestate']
+ %state = livestate.get('state', 'unknown').lower()
+
+
+ {{!helper.get_fa_icon_state_and_label(cls="host", state=state)}}
+
+
+
{{status['name']}}
+
+
+
+
+
+
+
+ %columns = sorted(ls['livesynthesis']['_overall']['livesynthesis'].keys())
+
+
+ %end
+
+
diff --git a/module/views/header_element.tpl b/module/views/header_element.tpl
index fcf5147a..a5163bfe 100644
--- a/module/views/header_element.tpl
+++ b/module/views/header_element.tpl
@@ -224,6 +224,7 @@
%if user.is_administrator():
+ %if not app.alignak:
System
+ %else:
+ Alignak
+
+ %end
Configuration