diff --git a/openwisp_monitoring/check/base/models.py b/openwisp_monitoring/check/base/models.py index 0d040a7b1..2f2e05b2a 100644 --- a/openwisp_monitoring/check/base/models.py +++ b/openwisp_monitoring/check/base/models.py @@ -98,6 +98,11 @@ def perform_check(self, store=True): """ initiates check instance and calls its check method """ + if ( + hasattr(self.content_object, 'organization_id') + and self.content_object.organization.is_active is False + ): + return return self.check_instance.check(store=True) def perform_check_delayed(self, duration=0): diff --git a/openwisp_monitoring/check/tests/test_models.py b/openwisp_monitoring/check/tests/test_models.py index 0f71fa94d..c0e5aef8c 100644 --- a/openwisp_monitoring/check/tests/test_models.py +++ b/openwisp_monitoring/check/tests/test_models.py @@ -282,6 +282,16 @@ def test_device_unknown_no_config_check(self): self.assertEqual(Notification.objects.count(), 0) self.assertIsNone(c2.perform_check()) + def test_device_organization_disabled_check_not_performed(self): + self._create_config( + status='modified', organization=self._create_org(is_active=False) + ) + self.assertEqual(Check.objects.count(), 3) + check = Check.objects.filter(check_type=self._CONFIG_APPLIED).first() + with patch(f'{self._CONFIG_APPLIED}.check') as mocked_check: + check.perform_check() + mocked_check.assert_not_called() + def test_config_check_problem_with_interval(self): self._create_admin() d = self._create_device(organization=self._create_org()) diff --git a/openwisp_monitoring/device/admin.py b/openwisp_monitoring/device/admin.py index 3c244a3a1..c242dbea1 100644 --- a/openwisp_monitoring/device/admin.py +++ b/openwisp_monitoring/device/admin.py @@ -324,7 +324,7 @@ def get_inlines(self, request, obj=None): for inline in inlines: if not hasattr(inline, 'sortable_options'): inline.sortable_options = {'disabled': True} - if not obj or obj._state.adding: + if not obj or obj._state.adding or obj.organization.is_active is False: inlines.remove(CheckInline) inlines.remove(MetricInline) return inlines diff --git a/openwisp_monitoring/device/api/views.py b/openwisp_monitoring/device/api/views.py index 2dd956256..697318dc6 100644 --- a/openwisp_monitoring/device/api/views.py +++ b/openwisp_monitoring/device/api/views.py @@ -111,10 +111,14 @@ class DeviceMetricView( """ model = DeviceData - queryset = DeviceData.objects.only( - 'id', - 'key', - ).all() + queryset = ( + DeviceData.objects.filter(organization__is_active=True) + .only( + 'id', + 'key', + ) + .all() + ) serializer_class = serializers.Serializer permission_classes = [DevicePermission] schema = schema diff --git a/openwisp_monitoring/device/apps.py b/openwisp_monitoring/device/apps.py index fb6685ff6..739709878 100644 --- a/openwisp_monitoring/device/apps.py +++ b/openwisp_monitoring/device/apps.py @@ -58,6 +58,7 @@ def connect_device_signals(self): DeviceLocation = load_model('geo', 'DeviceLocation') Metric = load_model('monitoring', 'Metric') Chart = load_model('monitoring', 'Chart') + Organization = load_model('openwisp_users', 'Organization') post_save.connect( self.device_post_save_receiver, @@ -120,6 +121,11 @@ def connect_device_signals(self): sender=DeviceLocation, dispatch_uid='post_delete_devicelocation_invalidate_devicedata_cache', ) + post_save.connect( + self.organization_post_save_receiver, + sender=Organization, + dispatch_uid='post_save_organization_disabled_monitoring', + ) @classmethod def device_post_save_receiver(cls, instance, created, **kwargs): @@ -134,6 +140,13 @@ def device_post_delete_receiver(cls, instance, **kwargs): instance.checks.all().delete() instance.metrics.all().delete() + @classmethod + def organization_post_save_receiver(cls, instance, *args, **kwargs): + if instance.is_active is False: + from .tasks import handle_disabled_organization + + handle_disabled_organization.delay(str(instance.id)) + def device_recovery_detection(self): if not app_settings.DEVICE_RECOVERY_DETECTION: return diff --git a/openwisp_monitoring/device/base/models.py b/openwisp_monitoring/device/base/models.py index 2d8eba8b9..b3e3f5d4c 100644 --- a/openwisp_monitoring/device/base/models.py +++ b/openwisp_monitoring/device/base/models.py @@ -435,6 +435,25 @@ def is_metric_critical(metric): return True return False + @classmethod + def handle_disabled_organization(cls, organization_id): + """ + Clears the management IP of all devices belonging to a + disabled organization and set their monitoring status to 'unknown'. + + Parameters: + organization_id (int): The ID of the disabled organization. + + Returns: + None + """ + load_model('config', 'Device').objects.filter( + organization_id=organization_id + ).update(management_ip='') + cls.objects.filter(device__organization_id=organization_id).update( + status='unknown' + ) + class AbstractWifiClient(TimeStampedEditableModel): id = None diff --git a/openwisp_monitoring/device/tasks.py b/openwisp_monitoring/device/tasks.py index 6799acd3c..03ab8577a 100644 --- a/openwisp_monitoring/device/tasks.py +++ b/openwisp_monitoring/device/tasks.py @@ -63,3 +63,9 @@ def write_device_metrics(pk, data, time=None, current=False): except DeviceData.DoesNotExist: return device_data.writer.write(data, time, current) + + +@shared_task(base=OpenwispCeleryTask) +def handle_disabled_organization(organization_id): + DeviceMonitoring = load_model('device_monitoring', 'DeviceMonitoring') + DeviceMonitoring.handle_disabled_organization(organization_id) diff --git a/openwisp_monitoring/device/tests/test_admin.py b/openwisp_monitoring/device/tests/test_admin.py index 0cdf4e02e..8699f856e 100644 --- a/openwisp_monitoring/device/tests/test_admin.py +++ b/openwisp_monitoring/device/tests/test_admin.py @@ -321,6 +321,25 @@ def test_device_add_view(self): self.assertContains(r, '