Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: openwisp/openwisp-monitoring
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 93039cf1ba37e4246fef3931c9f999a1a5b48655
Choose a base ref
..
head repository: openwisp/openwisp-monitoring
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 91a648faa24feddc55fcdc5ecd13a1d986b52b80
Choose a head ref
48 changes: 17 additions & 31 deletions README.rst
Original file line number Diff line number Diff line change
@@ -116,6 +116,7 @@ In case, you wish to use ``Elasticsearch`` for timeseries data storage and retri
make use of the following settings

.. code-block:: python
TIMESERIES_DATABASE = {
'BACKEND': 'openwisp_monitoring.db.backends.elasticsearch',
'USER': 'openwisp',
@@ -270,40 +271,25 @@ Ping
.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/master/docs/rtt.png
:align: center

Traffic rx bytes
~~~~~~~~~~~~~~~~

+--------------------+-------------------------------+
| **measurement**: | ``<interface_name> rx_bytes`` |
+--------------------+-------------------------------+
| **type**: | ``int`` |
+--------------------+-------------------------------+
| **fields**: | ``rx_bytes`` |
+--------------------+-------------------------------+
| **configuration**: | ``traffic_rx_bytes`` |
+--------------------+-------------------------------+
| **charts**: | ``traffic`` |
+--------------------+-------------------------------+

Traffic tx bytes
~~~~~~~~~~~~~~~~

+--------------------+-------------------------------+
| **measurement**: | ``<interface_name> tx_bytes`` |
+--------------------+-------------------------------+
| **type**: | ``int`` |
+--------------------+-------------------------------+
| **fields**: | ``tx_bytes`` |
+--------------------+-------------------------------+
| **configuration**: | ``traffic_tx_bytes`` |
+--------------------+-------------------------------+
| **charts**: | ``traffic`` |
+--------------------+-------------------------------+
Traffic
~~~~~~~

+--------------------+----------------------------+
| **measurement**: | ``<interface_name>`` |
+--------------------+----------------------------+
| **type**: | ``int`` |
+--------------------+----------------------------+
| **fields**: | ``rx_bytes``, ``tx_bytes`` |
+--------------------+----------------------------+
| **configuration**: | ``traffic`` |
+--------------------+----------------------------+
| **charts**: | ``traffic`` |
+--------------------+----------------------------+

.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/master/docs/traffic.png
:align: center

WiFI Clients
WiFi Clients
~~~~~~~~~~~~

+--------------------+--------------------------+
@@ -865,7 +851,7 @@ An example usage has been shown below.
_('Average Max RTT'),
_('Average Min RTT'),
],
'unit': f' {_("ms")}',
'unit': _(' ms'),
'order': 220,
'query': chart_query['rtt'],
},
Original file line number Diff line number Diff line change
@@ -16,8 +16,8 @@
from openwisp_monitoring.device.utils import SHORT_RP, manage_short_retention_policy
from openwisp_monitoring.monitoring.tests import TestMonitoringMixin

from ... import timeseries_db
from ....exceptions import TimeseriesWriteException
from ... import timeseries_db

Chart = load_model('monitoring', 'Chart')
Notification = load_model('openwisp_notifications', 'Notification')
33 changes: 33 additions & 0 deletions openwisp_monitoring/device/admin.py
Original file line number Diff line number Diff line change
@@ -126,6 +126,23 @@ def get_extra_context(self, pk=None):
)
return ctx

def health_checks(self, obj):
metric_rows = []
for metric in DeviceData(pk=obj.pk).metrics.all():
# Skip metrics which don't affect device health
if metric.is_healthy is None:
continue
health = 'yes' if metric.is_healthy else 'no'
metric_rows.append(
f'<li><img src="/static/admin/img/icon-{health}.svg" '
f'alt="health"> {metric.name}</li>'
)
return format_html(
mark_safe(f'<ul class="health_checks">{"".join(metric_rows)}</ul>'),
)

health_checks.short_description = _('health checks')

def health_status(self, obj):
return format_html(
mark_safe('<span class="health-{0}">{1}</span>'),
@@ -145,6 +162,22 @@ def get_form(self, request, obj=None, **kwargs):
)
return super().get_form(request, obj, **kwargs)

def get_fields(self, request, obj=None):
fields = super().get_fields(request, obj)
if not obj or obj.monitoring.status in ['ok', 'unknown']:
return fields
fields = list(fields)
fields.insert(fields.index('health_status') + 1, 'health_checks')
return fields

def get_readonly_fields(self, request, obj=None):
readonly_fields = super().get_readonly_fields(request, obj)
if not obj or obj.monitoring.status in ['ok', 'unknown']:
return readonly_fields
readonly_fields = list(readonly_fields)
readonly_fields.append('health_checks')
return readonly_fields


def device_admin_get_inlines(self, request, obj):
# copy the list to avoid modifying the original data structure
32 changes: 18 additions & 14 deletions openwisp_monitoring/device/apps.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,10 @@
from django.db.models.signals import post_delete, post_save
from django.utils.translation import gettext_lazy as _
from openwisp_notifications.signals import notify
from openwisp_notifications.types import register_notification_type
from openwisp_notifications.types import (
register_notification_type,
unregister_notification_type,
)
from swapper import load_model

from openwisp_controller.config.signals import checksum_requested, config_modified
@@ -22,7 +25,7 @@ class DeviceMonitoringConfig(AppConfig):

def ready(self):
manage_short_retention_policy()
self.register_notifcation_types()
self.register_notification_types()
self.connect_is_working_changed()
self.connect_device_signals()
self.connect_config_modified()
@@ -128,7 +131,7 @@ def is_working_changed_receiver(
return
initial_status = device_monitoring.status
status = 'ok' if is_working else 'problem'
# do not send notificatons if recovery made after firmware upgrade
# do not send notifications if recovery made after firmware upgrade
if status == initial_status == 'ok':
device_monitoring.save()
return
@@ -147,14 +150,14 @@ def is_working_changed_receiver(
device_monitoring.update_status(status)
notify.send(**notification_opts)

def register_notifcation_types(self):
def register_notification_types(self):
register_notification_type(
'connection_is_working',
'connection_is_not_working',
{
'verbose_name': 'Device Alert',
'verb': 'working',
'level': 'info',
'email_subject': '[{site.name}] RECOVERY: Connection to device {notification.target}',
'verbose_name': 'Device Connection PROBLEM',
'verb': 'not working',
'level': 'error',
'email_subject': '[{site.name}] PROBLEM: Connection to device {notification.target}',
'message': (
'{notification.actor.credentials} connection to '
'device [{notification.target}]({notification.target_link}) '
@@ -163,19 +166,20 @@ def register_notifcation_types(self):
},
)
register_notification_type(
'connection_is_not_working',
'connection_is_working',
{
'verbose_name': 'Device Alert',
'verb': 'not working',
'level': 'error',
'email_subject': '[{site.name}] PROBLEM: Connection to device {notification.target}',
'verbose_name': 'Device Connection RECOVERY',
'verb': 'working',
'level': 'info',
'email_subject': '[{site.name}] RECOVERY: Connection to device {notification.target}',
'message': (
'{notification.actor.credentials} connection to '
'device [{notification.target}]({notification.target_link}) '
'is {notification.verb}. {notification.actor.failure_reason}'
),
},
)
unregister_notification_type('default')

@classmethod
def connect_config_modified(cls):
Original file line number Diff line number Diff line change
@@ -209,3 +209,7 @@ td.field-health_status,
#monitoring-metric-content_type-object_id-group{
padding-top: 0 !important;
}

.form-row.field-health_checks div.readonly {
margin-left: 0px !important;
}
25 changes: 25 additions & 0 deletions openwisp_monitoring/device/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -160,3 +160,28 @@ def test_check_inline_formset(self):
c = Check.objects.first()
self.assertEqual(c.name, 'Ping Check')
self.assertEqual(c.content_object, d)

def test_health_checks_list(self):
dd = self.create_test_adata()
url = reverse('admin:config_device_change', args=[dd.pk])
self._login_admin()
r = self.client.get(url)
self.assertNotContains(r, '<label>Health checks:</label>')
m = Metric.objects.filter(configuration='disk').first()
m.write(m.alertsettings.threshold + 0.1)
self.assertFalse(m.is_healthy)
self.assertEqual(dd.monitoring.status, 'problem')
r = self.client.get(url)
self.assertContains(r, '<label>Health checks:</label>')
# Clients and Traffic metrics
interface_metrics = dd.metrics.filter(is_healthy=None)
other_metrics = dd.metrics.all().exclude(is_healthy=None)
for metric in interface_metrics:
self.assertNotContains(r, f'{metric.name}</li>')
for metric in other_metrics:
health = 'yes' if metric.is_healthy else 'no'
self.assertContains(
r,
f'<li><img src="/static/admin/img/icon-{health}.svg" '
f'alt="health"> {metric.name}</li>',
)
45 changes: 19 additions & 26 deletions openwisp_monitoring/monitoring/configuration.py
Original file line number Diff line number Diff line change
@@ -47,12 +47,7 @@
'max': 100,
'min': 0,
'label': _('Reachable'),
'scale': [
[0, '#c13000'],
# [0.33, '#ef7d2d'],
[0.5, '#deed0e'],
[1, '#7db201'],
],
'scale': [[0, '#c13000'], [0.5, '#deed0e'], [1, '#7db201']],
'map': [
[100, '#7db201', _('Reachable')],
[33, '#deed0e', _('Partly reachable')],
@@ -86,7 +81,7 @@
_('Average Max RTT'),
_('Average Min RTT'),
],
'unit': _(" ms"),
'unit': _(' ms'),
'order': 220,
'query': chart_query['rtt'],
},
@@ -173,7 +168,7 @@
_('Total download traffic'),
_('Total upload traffic'),
],
'unit': _(" GB"),
'unit': _(' GB'),
'order': 240,
'query': chart_query['traffic'],
},
@@ -230,21 +225,20 @@
'message': _(
'The device [{notification.target}]({notification.target_link}) '
'{notification.verb} disk usage which has gone over '
'{notification.actor.alertsettings.threshold:.1%}.'
'{notification.actor.alertsettings.threshold:.0%}.'
),
},
'recovery': {
'verbose_name': 'Disk usage RECOVERY',
'verb': _('has returned within threshold'),
'verb': _('has returned to normal levels'),
'level': 'info',
'email_subject': _(
'[{site.name}] RECOVERY: {notification.target} disk usage '
'{notification.verb}'
),
'message': (
'The device [{notification.target}]({notification.target_link}) '
'disk usage {notification.verb} value of '
'{notification.actor.alertsettings.threshold:.1%}.'
'disk usage {notification.verb}.'
),
},
},
@@ -281,24 +275,24 @@
'verb': _('is experiencing a peak in'),
'level': 'warning',
'email_subject': _(
'[{site.name}] PROBLEM: {notification.target} {notification.verb} ram usage'
'[{site.name}] PROBLEM: {notification.target} {notification.verb} RAM usage'
),
'message': _(
'The device [{notification.target}]({notification.target_link}) '
'{notification.verb} ram usage which has gone '
'over {notification.actor.alertsettings.threshold:.1%}.'
'{notification.verb} RAM usage which has gone '
'over {notification.actor.alertsettings.threshold:.0%}.'
),
},
'recovery': {
'verbose_name': 'Memory usage RECOVERY',
'verb': _('has returned within threshold'),
'verb': _('has returned to normal levels'),
'level': 'info',
'email_subject': _(
'[{site.name}] RECOVERY: {notification.target} ram usage {notification.verb}'
'[{site.name}] RECOVERY: {notification.target} RAM usage {notification.verb}'
),
'message': (
'The device [{notification.target}]({notification.target_link}) ram usage '
'{notification.verb} value of {notification.actor.alertsettings.threshold:.1%}.'
'The device [{notification.target}]({notification.target_link}) RAM usage '
'{notification.verb}.'
),
},
},
@@ -331,25 +325,24 @@
'verb': _('is experiencing a peak in'),
'level': 'warning',
'email_subject': _(
'[{site.name}] PROBLEM: {notification.target} {notification.verb} cpu usage'
'[{site.name}] PROBLEM: {notification.target} {notification.verb} CPU usage'
),
'message': _(
'The device [{notification.target}]({notification.target_link}) '
'{notification.verb} cpu usage which has gone '
'over {notification.actor.alertsettings.threshold:.1%}.'
'{notification.verb} CPU usage which has gone '
'over {notification.actor.alertsettings.threshold:.0%}.'
),
},
'recovery': {
'verbose_name': 'CPU usage RECOVERY',
'verb': _('has returned within threshold'),
'verb': _('has returned to normal levels'),
'level': 'info',
'email_subject': _(
'[{site.name}] RECOVERY: {notification.target} cpu usage {notification.verb}'
'[{site.name}] RECOVERY: {notification.target} CPU usage {notification.verb}'
),
'message': (
'The device [{notification.target}]({notification.target_link}) '
'cpu usage {notification.verb} value of '
'{notification.actor.alertsettings.threshold:.1%}.'
'CPU usage {notification.verb}.'
),
},
},
Loading