Skip to content

Commit 2504f10

Browse files
[fix] Skip WHOIS and estimated location for deactivated devices #1325
- add guard in WHOISService to skip processing for deactivated devices - add guard in EstimatedLocationService to prevent task scheduling - fix N+1 query by including _is_deactivated in clear_last_ip_command queryset - add regression tests ensuring tasks are skipped when device is deactivated - ensure deterministic tests by enabling whois on organization settings Fixes #1325
1 parent 8d528e7 commit 2504f10

4 files changed

Lines changed: 46 additions & 7 deletions

File tree

openwisp_controller/config/whois/commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def clear_last_ip_command(stdout, stderr, interactive=True):
3939
)
4040
# include the FK field 'organization' in .only() so the related
4141
# `organization__config_settings` traversal is not deferred
42-
.only("last_ip", "organization", "key")
42+
.only("last_ip", "organization", "key", "_is_deactivated")
4343
)
4444
# Filter out devices that have WHOIS information for their last IP
4545
devices = devices.exclude(last_ip=None).exclude(

openwisp_controller/config/whois/tests/tests.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@
2121
from selenium.webdriver.common.by import By
2222
from swapper import load_model
2323

24-
from openwisp_controller.config import settings as app_settings
2524
from openwisp_controller.config.signals import whois_fetched, whois_lookup_skipped
26-
from openwisp_controller.config.whois.service import WHOISService
2725
from openwisp_utils.tests import SeleniumTestMixin, catch_signal
2826

2927
from ....tests.utils import TestAdminMixin
@@ -349,7 +347,7 @@ def test_last_ip_management_command_queries(self):
349347
)
350348
args = ["--noinput"]
351349

352-
with self.assertNumQueries(7):
350+
with self.assertNumQueries(4):
353351
call_command("clear_last_ip", *args, stdout=out, stderr=StringIO())
354352

355353
@mock.patch.object(app_settings, "WHOIS_CONFIGURED", True)
@@ -1203,7 +1201,14 @@ def setUp(self):
12031201
@mock.patch.object(app_settings, "WHOIS_CONFIGURED", True)
12041202
@mock.patch("openwisp_controller.config.whois.service.fetch_whois_details.delay")
12051203
def test_process_ip_skips_when_deactivated(self, mock_task):
1206-
self.device._is_deactivated = True
1204+
# Public IP that would normally trigger a lookup, so the only reason
1205+
# the task is not enqueued is the deactivation guard under test.
1206+
self.device._initial_last_ip = None
1207+
self.device.last_ip = "8.8.8.8"
1208+
self.device.save()
1209+
mock_task.reset_mock()
1210+
1211+
self.device.deactivate()
12071212

12081213
service = WHOISService(self.device)
12091214
service.process_ip_data_and_location()
@@ -1213,10 +1218,41 @@ def test_process_ip_skips_when_deactivated(self, mock_task):
12131218
@mock.patch.object(app_settings, "WHOIS_CONFIGURED", True)
12141219
@mock.patch("openwisp_controller.config.whois.service.fetch_whois_details.delay")
12151220
def test_process_ip_runs_when_active(self, mock_task):
1216-
self.device._is_deactivated = False
1221+
self.device._initial_last_ip = None
12171222
self.device.last_ip = "8.8.8.8"
1223+
self.device.save()
1224+
1225+
mock_task.reset_mock()
12181226

12191227
service = WHOISService(self.device)
12201228
service.process_ip_data_and_location()
12211229

1222-
self.assertEqual(mock_task.call_count, 1)
1230+
# transaction.on_commit executes immediately in TransactionTestCase,
1231+
# so the task is triggered synchronously here
1232+
mock_task.assert_called_once_with(
1233+
device_pk=self.device.pk,
1234+
initial_ip_address=self.device._initial_last_ip,
1235+
)
1236+
1237+
@mock.patch.object(app_settings, "WHOIS_CONFIGURED", True)
1238+
@mock.patch("openwisp_controller.config.whois.service.fetch_whois_details.delay")
1239+
def test_update_whois_skips_when_deactivated(self, mock_task):
1240+
ip = "8.8.8.8"
1241+
self.device._initial_last_ip = None
1242+
self.device.last_ip = ip
1243+
self.device.save()
1244+
1245+
threshold = app_settings.WHOIS_REFRESH_THRESHOLD_DAYS + 1
1246+
expired_time = timezone.now() - timedelta(days=threshold)
1247+
1248+
whois_obj = self._create_whois_info(ip_address=ip)
1249+
WHOISInfo.objects.filter(pk=whois_obj.pk).update(modified=expired_time)
1250+
1251+
self.device.deactivate()
1252+
1253+
mock_task.reset_mock()
1254+
1255+
service = WHOISService(self.device)
1256+
service.update_whois_info()
1257+
1258+
mock_task.assert_not_called()

openwisp_controller/connection/tests/test_tasks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def test_launch_command_exception(self, *args):
142142
class TestTransactionTasks(
143143
TestRegistrationMixin, CreateConnectionsMixin, TransactionTestCase
144144
):
145+
@mock.patch("openwisp_controller.connection.tasks.launch_command.delay")
145146
@mock.patch.object(tasks.update_config, "delay")
146147
def test_update_config_hostname_changed_on_reregister(self, mocked_update_config):
147148
device = self._create_device_config()

openwisp_controller/geo/estimated_location/service.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def is_estimated_location_enabled(self):
6363
return self.check_estimated_location_enabled(self.device.organization_id)
6464

6565
def trigger_estimated_location_task(self, ip_address):
66+
if self.device.is_deactivated():
67+
return
6668
try:
6769
current_app.send_task(
6870
"whois_estimated_location_task",

0 commit comments

Comments
 (0)