From 4a830030c0f75ae52cd324a856e0113bd8eeb466 Mon Sep 17 00:00:00 2001 From: Aryamanz29 Date: Mon, 6 Jun 2022 14:52:40 +0530 Subject: [PATCH] [feature] Implement Iperf check #385 - Added initial code for Iperf check class. - Added tests. Closes #385 --- openwisp_monitoring/check/apps.py | 8 +++ openwisp_monitoring/check/base/models.py | 24 ++++++++- openwisp_monitoring/check/classes/__init__.py | 1 + openwisp_monitoring/check/classes/iperf.py | 49 ++++++++++++++++++ openwisp_monitoring/check/settings.py | 11 ++++ openwisp_monitoring/check/tasks.py | 27 ++++++++++ .../check/tests/test_models.py | 50 +++++++++++++++---- openwisp_monitoring/check/tests/test_ping.py | 2 +- .../device/tests/test_transactions.py | 2 + openwisp_monitoring/tests/test_selenium.py | 2 + tests/openwisp2/settings.py | 2 + 11 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 openwisp_monitoring/check/classes/iperf.py diff --git a/openwisp_monitoring/check/apps.py b/openwisp_monitoring/check/apps.py index 15c9832f0..8f030cf4f 100644 --- a/openwisp_monitoring/check/apps.py +++ b/openwisp_monitoring/check/apps.py @@ -32,3 +32,11 @@ def _connect_signals(self): sender=load_model('config', 'Device'), dispatch_uid='auto_config_check', ) + if app_settings.AUTO_IPERF: + from .base.models import auto_iperf_check_receiver + + post_save.connect( + auto_iperf_check_receiver, + sender=load_model('config', 'Device'), + dispatch_uid='auto_iperf_check', + ) diff --git a/openwisp_monitoring/check/base/models.py b/openwisp_monitoring/check/base/models.py index a0bab9e66..e4abad47a 100644 --- a/openwisp_monitoring/check/base/models.py +++ b/openwisp_monitoring/check/base/models.py @@ -9,7 +9,11 @@ from jsonfield import JSONField from openwisp_monitoring.check import settings as app_settings -from openwisp_monitoring.check.tasks import auto_create_config_check, auto_create_ping +from openwisp_monitoring.check.tasks import ( + auto_create_config_check, + auto_create_iperf_check, + auto_create_ping, +) from openwisp_utils.base import TimeStampedEditableModel from ...utils import transaction_on_commit @@ -116,3 +120,21 @@ def auto_config_check_receiver(sender, instance, created, **kwargs): object_id=str(instance.pk), ) ) + + +def auto_iperf_check_receiver(sender, instance, created, **kwargs): + """ + Implements OPENWISP_MONITORING_AUTO_IPERF + The creation step is executed in the background + """ + # we need to skip this otherwise this task will be executed + # every time the configuration is requested via checksum + if not created: + return + transaction_on_commit( + lambda: auto_create_iperf_check.delay( + model=sender.__name__.lower(), + app_label=sender._meta.app_label, + object_id=str(instance.pk), + ) + ) diff --git a/openwisp_monitoring/check/classes/__init__.py b/openwisp_monitoring/check/classes/__init__.py index 33bf8293c..4a85b5243 100644 --- a/openwisp_monitoring/check/classes/__init__.py +++ b/openwisp_monitoring/check/classes/__init__.py @@ -1,2 +1,3 @@ from .config_applied import ConfigApplied # noqa +from .iperf import Iperf # noqa from .ping import Ping # noqa diff --git a/openwisp_monitoring/check/classes/iperf.py b/openwisp_monitoring/check/classes/iperf.py new file mode 100644 index 000000000..80698be23 --- /dev/null +++ b/openwisp_monitoring/check/classes/iperf.py @@ -0,0 +1,49 @@ +from swapper import load_model + +from .base import BaseCheck + +Chart = load_model('monitoring', 'Chart') +Metric = load_model('monitoring', 'Metric') +Device = load_model('config', 'Device') +DeviceData = load_model('device_monitoring', 'DeviceData') +Credentials = load_model('connection', 'Credentials') +AlertSettings = load_model('monitoring', 'AlertSettings') +DeviceConnection = load_model('connection', 'DeviceConnection') + + +class Iperf(BaseCheck): + def check(self, store=True): + pass + + def store_result(self, result): + """ + store result in the DB + """ + pass + + def _get_iperf_servers(self): + """ + Get iperf test servers + """ + pass + + def _get_iperf_result(self, mode=None): + """ + Get iperf test result + """ + pass + + def _get_metric(self): + """ + Gets or creates metric + """ + pass + + def _create_charts(self, metric): + """ + Creates iperf related charts (Bandwith/Jitter) + """ + pass + + def _create_alert_settings(self, metric): + pass diff --git a/openwisp_monitoring/check/settings.py b/openwisp_monitoring/check/settings.py index 4575c8eca..9ebab74cc 100644 --- a/openwisp_monitoring/check/settings.py +++ b/openwisp_monitoring/check/settings.py @@ -5,9 +5,20 @@ ( ('openwisp_monitoring.check.classes.Ping', 'Ping'), ('openwisp_monitoring.check.classes.ConfigApplied', 'Configuration Applied'), + ('openwisp_monitoring.check.classes.Iperf', 'Iperf'), ), ) AUTO_PING = get_settings_value('AUTO_PING', True) AUTO_CONFIG_CHECK = get_settings_value('AUTO_DEVICE_CONFIG_CHECK', True) MANAGEMENT_IP_ONLY = get_settings_value('MANAGEMENT_IP_ONLY', True) PING_CHECK_CONFIG = get_settings_value('PING_CHECK_CONFIG', {}) +# By default it should be disabled. +AUTO_IPERF = get_settings_value('AUTO_IPERF', False) +# IPERF_SERVERS = get_settings_value( +# 'IPERF_SERVERS', +# { +# # Running on my local +# 'be63c4e5-a68a-4650-bfe8-733837edb8be': ['172.19.0.1'], +# # '': [''] +# }, +# ) diff --git a/openwisp_monitoring/check/tasks.py b/openwisp_monitoring/check/tasks.py index 2ae62bc0d..9a509ea2b 100644 --- a/openwisp_monitoring/check/tasks.py +++ b/openwisp_monitoring/check/tasks.py @@ -100,3 +100,30 @@ def auto_create_config_check( ) check.full_clean() check.save() + + +@shared_task +def auto_create_iperf_check( + model, app_label, object_id, check_model=None, content_type_model=None +): + """ + Called by openwisp_monitoring.check.models.auto_iperf_check_receiver + """ + Check = check_model or get_check_model() + iperf_check_path = 'openwisp_monitoring.check.classes.Iperf' + has_check = Check.objects.filter( + object_id=object_id, content_type__model='device', check_type=iperf_check_path + ).exists() + # create new check only if necessary + if has_check: + return + content_type_model = content_type_model or ContentType + ct = content_type_model.objects.get(app_label=app_label, model=model) + check = Check( + name='Iperf', + check_type=iperf_check_path, + content_type=ct, + object_id=object_id, + ) + check.full_clean() + check.save() diff --git a/openwisp_monitoring/check/tests/test_models.py b/openwisp_monitoring/check/tests/test_models.py index abbf8ed13..3bb8e13f6 100644 --- a/openwisp_monitoring/check/tests/test_models.py +++ b/openwisp_monitoring/check/tests/test_models.py @@ -9,8 +9,8 @@ from ...device.tests import TestDeviceMonitoringMixin from .. import settings as app_settings -from ..classes import ConfigApplied, Ping -from ..tasks import auto_create_config_check, auto_create_ping +from ..classes import ConfigApplied, Iperf, Ping +from ..tasks import auto_create_config_check, auto_create_iperf_check, auto_create_ping Check = load_model('check', 'Check') Metric = load_model('monitoring', 'Metric') @@ -22,6 +22,7 @@ class TestModels(TestDeviceMonitoringMixin, TransactionTestCase): _PING = app_settings.CHECK_CLASSES[0][0] _CONFIG_APPLIED = app_settings.CHECK_CLASSES[1][0] + _IPERF = app_settings.CHECK_CLASSES[2][0] def test_check_str(self): c = Check(name='Test check') @@ -48,6 +49,12 @@ def test_check_class(self): check_type=self._CONFIG_APPLIED, ) self.assertEqual(c.check_class, ConfigApplied) + with self.subTest('Test Iperf check Class'): + c = Check( + name='Iperf class check', + check_type=self._IPERF, + ) + self.assertEqual(c.check_class, Iperf) def test_base_check_class(self): path = 'openwisp_monitoring.check.classes.base.BaseCheck' @@ -82,6 +89,18 @@ def test_check_instance(self): self.assertEqual(i.related_object, obj) self.assertEqual(i.params, c.params) + with self.subTest('Test Iperf check instance'): + c = Check( + name='Iperf class check', + check_type=self._IPERF, + content_object=obj, + params={}, + ) + i = c.check_instance + self.assertIsInstance(i, Iperf) + self.assertEqual(i.related_object, obj) + self.assertEqual(i.params, c.params) + def test_validation(self): with self.subTest('Test Ping check validation'): check = Check(name='Ping check', check_type=self._PING, params={}) @@ -105,7 +124,7 @@ def test_validation(self): def test_auto_check_creation(self): self.assertEqual(Check.objects.count(), 0) d = self._create_device(organization=self._create_org()) - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) with self.subTest('Test AUTO_PING'): c1 = Check.objects.filter(check_type=self._PING).first() self.assertEqual(c1.content_object, d) @@ -114,11 +133,15 @@ def test_auto_check_creation(self): c2 = Check.objects.filter(check_type=self._CONFIG_APPLIED).first() self.assertEqual(c2.content_object, d) self.assertEqual(self._CONFIG_APPLIED, c2.check_type) + with self.subTest('Test AUTO_IPERF'): + c3 = Check.objects.filter(check_type=self._IPERF).first() + self.assertEqual(c3.content_object, d) + self.assertEqual(self._IPERF, c3.check_type) def test_device_deleted(self): self.assertEqual(Check.objects.count(), 0) d = self._create_device(organization=self._create_org()) - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) d.delete() self.assertEqual(Check.objects.count(), 0) @@ -129,7 +152,7 @@ def test_config_modified_device_problem(self): self._create_config(status='modified', organization=self._create_org()) d = Device.objects.first() d.monitoring.update_status('ok') - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) self.assertEqual(Metric.objects.count(), 0) self.assertEqual(AlertSettings.objects.count(), 0) check = Check.objects.filter(check_type=self._CONFIG_APPLIED).first() @@ -159,7 +182,7 @@ def test_config_error(self): self._create_config(status='error', organization=self._create_org()) dm = Device.objects.first().monitoring dm.update_status('ok') - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) self.assertEqual(Metric.objects.count(), 0) self.assertEqual(AlertSettings.objects.count(), 0) check = Check.objects.filter(check_type=self._CONFIG_APPLIED).first() @@ -192,7 +215,7 @@ def test_config_error(self): @patch('openwisp_monitoring.check.settings.AUTO_PING', False) def test_config_check_critical_metric(self): self._create_config(status='modified', organization=self._create_org()) - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) d = Device.objects.first() dm = d.monitoring dm.update_status('ok') @@ -211,7 +234,7 @@ def test_config_check_critical_metric(self): def test_no_duplicate_check_created(self): self._create_config(organization=self._create_org()) - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) d = Device.objects.first() auto_create_config_check.delay( model=Device.__name__.lower(), @@ -223,13 +246,18 @@ def test_no_duplicate_check_created(self): app_label=Device._meta.app_label, object_id=str(d.pk), ) - self.assertEqual(Check.objects.count(), 2) + auto_create_iperf_check.delay( + model=Device.__name__.lower(), + app_label=Device._meta.app_label, + object_id=str(d.pk), + ) + self.assertEqual(Check.objects.count(), 3) def test_device_unreachable_no_config_check(self): self._create_config(status='modified', organization=self._create_org()) d = self.device_model.objects.first() d.monitoring.update_status('critical') - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) c2 = Check.objects.filter(check_type=self._CONFIG_APPLIED).first() c2.perform_check() self.assertEqual(Metric.objects.count(), 0) @@ -240,7 +268,7 @@ def test_device_unknown_no_config_check(self): self._create_config(status='modified', organization=self._create_org()) d = self.device_model.objects.first() d.monitoring.update_status('unknown') - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) c2 = Check.objects.filter(check_type=self._CONFIG_APPLIED).first() c2.perform_check() self.assertEqual(Metric.objects.count(), 0) diff --git a/openwisp_monitoring/check/tests/test_ping.py b/openwisp_monitoring/check/tests/test_ping.py index 11b9ee47b..23b94652d 100644 --- a/openwisp_monitoring/check/tests/test_ping.py +++ b/openwisp_monitoring/check/tests/test_ping.py @@ -239,7 +239,7 @@ def test_store_result(self, mocked_method): device.management_ip = '10.40.0.1' device.save() # check created automatically by autoping - self.assertEqual(Check.objects.count(), 2) + self.assertEqual(Check.objects.count(), 3) self.assertEqual(Metric.objects.count(), 0) self.assertEqual(Chart.objects.count(), 0) self.assertEqual(AlertSettings.objects.count(), 0) diff --git a/openwisp_monitoring/device/tests/test_transactions.py b/openwisp_monitoring/device/tests/test_transactions.py index 5fd3d9698..1cbdb5165 100644 --- a/openwisp_monitoring/device/tests/test_transactions.py +++ b/openwisp_monitoring/device/tests/test_transactions.py @@ -62,6 +62,8 @@ def test_trigger_device_recovery_task_regression( dm = self._create_device_monitoring() dm.device.management_ip = None dm.device.save() + # Delete iperf check to prevent unnecessary response timeout + Check.objects.filter(check_type__endswith='Iperf').delete() trigger_device_checks.delay(dm.device.pk) self.assertTrue(Check.objects.exists()) # we expect update_status() to be called once (by the check) diff --git a/openwisp_monitoring/tests/test_selenium.py b/openwisp_monitoring/tests/test_selenium.py index 78e02ca96..8de1c8b2b 100644 --- a/openwisp_monitoring/tests/test_selenium.py +++ b/openwisp_monitoring/tests/test_selenium.py @@ -89,6 +89,8 @@ def test_restoring_deleted_device(self): org = self._get_org() self._create_credentials(auto_add=True, organization=org) device = self._create_config(organization=org).device + # Delete iperf check to prevent unnecessary response timeout + Check.objects.filter(check_type__endswith='Iperf').delete() device_data = DeviceData.objects.get(id=device.id) device_checks = device_data.checks.all() for check in device_checks: diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py index 372eeabc6..6d042e5d7 100644 --- a/tests/openwisp2/settings.py +++ b/tests/openwisp2/settings.py @@ -195,6 +195,8 @@ OPENWISP_MONITORING_MAC_VENDOR_DETECTION = False OPENWISP_MONITORING_API_URLCONF = 'openwisp_monitoring.urls' OPENWISP_MONITORING_API_BASEURL = 'http://testserver' + # for testing AUTO_IPERF + OPENWISP_MONITORING_AUTO_IPERF = True # Temporarily added to identify slow tests TEST_RUNNER = 'openwisp_utils.tests.TimeLoggingTestRunner'