Skip to content

Commit 8a868fc

Browse files
committed
[feature] Add a check which inspects device configuration status periodically #54
1 parent 4d7e5e3 commit 8a868fc

File tree

17 files changed

+370
-36
lines changed

17 files changed

+370
-36
lines changed

README.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,15 @@ command is used to collect these metrics.
213213
You may choose to disable auto creation of this check by setting
214214
`OPENWISP_MONITORING_AUTO_PING <#OPENWISP_MONITORING_AUTO_PING>`_ to ``False``.
215215

216+
``Configuration Applied``
217+
~~~~~~~~~~~~~~~~~~~~~~~~~
218+
219+
It checks the configuration status of the device periodically and changes
220+
the device health status to ``PROBLEM``, if config status stays modified
221+
for more than `OPENWISP_MONITORING_DEVICE_CONFIG_CHECK_MAX_TIME <#OPENWISP_MONITORING_DEVICE_CONFIG_CHECK_MAX_TIME>`_.
222+
You may choose to disable auto creation of this check by using the
223+
setting `OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK <#OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK>`_.
224+
216225
Settings
217226
--------
218227

@@ -242,6 +251,33 @@ in terms of disk space.
242251

243252
Whether ping checks are created automatically for devices.
244253

254+
``OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK``
255+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
256+
257+
+--------------+-------------+
258+
| **type**: | ``bool`` |
259+
+--------------+-------------+
260+
| **default**: | ``True`` |
261+
+--------------+-------------+
262+
263+
This setting allows you to choose whether `config_applied <#configuration-applied>`_ checks should be
264+
created automatically for newly registered devices. It's enabled by default.
265+
266+
``OPENWISP_MONITORING_DEVICE_CONFIG_CHECK_MAX_TIME``
267+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
268+
269+
+--------------+-----------+
270+
| **type**: | ``int`` |
271+
+--------------+-----------+
272+
| **default**: | ``5`` |
273+
+--------------+-----------+
274+
275+
Defines the maximum amount of minutes the device configuration can stay in the *modified*
276+
state before its health status is changed to ``PROBLEM`` and an alert is sent.
277+
278+
**Note**: The setting will be ignored if ``OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK``
279+
is ``False``.
280+
245281
``OPENWISP_MONITORING_AUTO_CHARTS``
246282
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
247283

openwisp_monitoring/check/apps.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,12 @@ def _connect_signals(self):
2222
sender=load_model('config', 'Device'),
2323
dispatch_uid='auto_ping',
2424
)
25+
26+
if app_settings.AUTO_CONFIG_CHECK:
27+
from .base.models import auto_config_check_receiver
28+
29+
post_save.connect(
30+
auto_config_check_receiver,
31+
sender=load_model('config', 'Device'),
32+
dispatch_uid='auto_config_check',
33+
)

openwisp_monitoring/check/base/models.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from django.utils.translation import gettext_lazy as _
99
from jsonfield import JSONField
1010
from openwisp_monitoring.check import settings as app_settings
11-
from openwisp_monitoring.check.tasks import auto_create_ping
11+
from openwisp_monitoring.check.tasks import auto_create_config_check, auto_create_ping
1212

1313
from openwisp_utils.base import TimeStampedEditableModel
1414

@@ -91,3 +91,22 @@ def auto_ping_receiver(sender, instance, created, **kwargs):
9191
object_id=str(instance.pk),
9292
)
9393
)
94+
95+
96+
def auto_config_check_receiver(sender, instance, created, **kwargs):
97+
"""
98+
Implements OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK
99+
The creation step is executed in the background
100+
"""
101+
# we need to skip this otherwise this task will be executed
102+
# every time the configuration is requested via checksum
103+
if not created:
104+
return
105+
with transaction.atomic():
106+
transaction.on_commit(
107+
lambda: auto_create_config_check.delay(
108+
model=sender.__name__.lower(),
109+
app_label=sender._meta.app_label,
110+
object_id=str(instance.pk),
111+
)
112+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
from .config_applied import ConfigApplied # noqa
12
from .ping import Ping # noqa

openwisp_monitoring/check/classes/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def validate_params(self):
3030
def check(self, store=True):
3131
raise NotImplementedError
3232

33-
def _get_or_create_metric(self):
33+
def _get_or_create_metric(self, configuration=None):
3434
"""
3535
Gets or creates metric
3636
"""
@@ -44,7 +44,7 @@ def _get_or_create_metric(self):
4444
options = dict(
4545
object_id=obj_id,
4646
content_type=ct,
47-
configuration=self.__class__.__name__.lower(),
47+
configuration=configuration or self.__class__.__name__.lower(),
4848
)
4949
metric, created = Metric._get_or_create(**options)
5050
return metric, created
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from swapper import load_model
2+
3+
from ...device.utils import SHORT_RP
4+
from ..settings import CONFIG_CHECK_MAX_TIME
5+
from .base import BaseCheck
6+
7+
AlertSettings = load_model('monitoring', 'AlertSettings')
8+
9+
10+
class ConfigApplied(BaseCheck):
11+
def check(self, store=True):
12+
# If the device is down do not run config applied checks
13+
if self.related_object.monitoring.status == 'critical':
14+
return
15+
if not hasattr(self.related_object, 'config'):
16+
return
17+
result = 1 if self.related_object.config.status == 'applied' else 0
18+
if store:
19+
self.get_metric().write(
20+
result, retention_policy=SHORT_RP,
21+
)
22+
return result
23+
24+
def get_metric(self):
25+
metric, created = self._get_or_create_metric(configuration='config_applied')
26+
if created:
27+
self._create_alert_setting(metric)
28+
return metric
29+
30+
def _create_alert_setting(self, metric):
31+
alert_s = AlertSettings(
32+
metric=metric, operator='<', value=1, seconds=CONFIG_CHECK_MAX_TIME * 60
33+
)
34+
alert_s.full_clean()
35+
alert_s.save()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from django.db import migrations
2+
from openwisp_monitoring.check.settings import AUTO_CONFIG_CHECK
3+
from openwisp_monitoring.check.tasks import auto_create_config_check
4+
5+
6+
def add_config_applied_checks(apps, schema_editor):
7+
if not AUTO_CONFIG_CHECK:
8+
return
9+
Device = apps.get_model('config', 'Device')
10+
for device in Device.objects.all():
11+
auto_create_config_check.delay(
12+
model=Device.__name__.lower(),
13+
app_label=Device._meta.app_label,
14+
object_id=str(device.pk),
15+
)
16+
17+
18+
def remove_config_applied_checks(apps, schema_editor):
19+
Check = apps.get_model('config', 'Device')
20+
Check.objects.filter(name='Configuration Applied').delete()
21+
22+
23+
class Migration(migrations.Migration):
24+
25+
dependencies = [
26+
('check', '0003_create_ping'),
27+
]
28+
29+
operations = [
30+
migrations.RunPython(
31+
add_config_applied_checks, reverse_code=remove_config_applied_checks
32+
),
33+
]

openwisp_monitoring/check/settings.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,17 @@
33
CHECK_CLASSES = getattr(
44
settings,
55
'OPENWISP_MONITORING_CHECK_CLASSES',
6-
(('openwisp_monitoring.check.classes.Ping', 'Ping'),),
6+
(
7+
('openwisp_monitoring.check.classes.Ping', 'Ping'),
8+
('openwisp_monitoring.check.classes.ConfigApplied', 'Configuration Applied'),
9+
),
710
)
811
AUTO_PING = getattr(settings, 'OPENWISP_MONITORING_AUTO_PING', True)
12+
AUTO_CONFIG_CHECK = getattr(
13+
settings, 'OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK', True
14+
)
15+
# Input in minutes
16+
CONFIG_CHECK_MAX_TIME = getattr(
17+
settings, 'OPENWISP_MONITORING_DEVICE_CONFIG_CHECK_MAX_TIME', 5
18+
)
919
MANAGEMENT_IP_ONLY = getattr(settings, 'OPENWISP_MONITORING_MANAGEMENT_IP_ONLY', True)

openwisp_monitoring/check/tasks.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,27 @@ def auto_create_ping(model, app_label, object_id):
5252
check = Check(name='Ping', check=ping_path, content_type=ct, object_id=object_id)
5353
check.full_clean()
5454
check.save()
55+
56+
57+
@shared_task
58+
def auto_create_config_check(model, app_label, object_id):
59+
"""
60+
Called by openwisp_monitoring.check.models.auto_config_check_receiver
61+
"""
62+
Check = load_model('check', 'Check')
63+
config_check_path = 'openwisp_monitoring.check.classes.ConfigApplied'
64+
has_check = Check.objects.filter(
65+
object_id=object_id, content_type__model='device', check=config_check_path,
66+
).exists()
67+
# create new check only if necessary
68+
if has_check:
69+
return
70+
ct = ContentType.objects.get(app_label=app_label, model=model)
71+
check = Check(
72+
name='Configuration Applied',
73+
check=config_check_path,
74+
content_type=ct,
75+
object_id=object_id,
76+
)
77+
check.full_clean()
78+
check.save()

0 commit comments

Comments
 (0)