Skip to content

Commit 5a0ae7e

Browse files
committed
[changes] Perform lookup and location for last_ip updation only
Signed-off-by: DragnEmperor <[email protected]>
1 parent 854a9ee commit 5a0ae7e

File tree

9 files changed

+95
-29
lines changed

9 files changed

+95
-29
lines changed

docs/user/whois.rst

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,23 @@ A WHOIS lookup is triggered automatically when:
4040
However, the lookup will only run if **all** the following conditions are
4141
met:
4242

43+
- The device is either **newly created** or has a **changed last IP**.
4344
- The device's last IP address is **public**.
4445
- There is **no existing WHOIS record** for that IP.
4546
- WHOIS lookup is **enabled** for the device's organization.
4647

47-
Behavior with Shared IP Addresses
48-
---------------------------------
48+
Managing WHOIS Records
49+
----------------------
4950

50-
If multiple devices share the same public IP address and one of them
51-
switches to a different IP, the following occurs:
52-
53-
- A lookup is triggered for the **new IP**.
54-
- The WHOIS record for the **old IP** is deleted.
55-
- The next time a device still using the old IP fetches its checksum, a
56-
new lookup is triggered, ensuring up-to-date data.
51+
If a device updates its last IP address, lookup is triggered for the **new
52+
IP** and the **WHOIS record for the old IP** is deleted if no active
53+
devices are associated with that IP address.
5754

5855
.. note::
5956

6057
When a device with an associated WHOIS record is deleted, its WHOIS
61-
record is automatically removed.
58+
record is automatically removed only if no active devices are
59+
associated with that IP address.
6260

6361
.. _controller_setup_whois_lookup:
6462

openwisp_controller/config/base/device.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def save(self, *args, **kwargs):
287287
state_adding = self._state.adding
288288
super().save(*args, **kwargs)
289289
if app_settings.WHOIS_CONFIGURED:
290-
self._check_last_ip()
290+
self._check_last_ip(creating=state_adding)
291291
if state_adding and self.group and self.group.templates.exists():
292292
self.create_default_config()
293293
# The value of "self._state.adding" will always be "False"
@@ -510,9 +510,10 @@ def whois_service(self):
510510
"""
511511
return WHOISService(self)
512512

513-
def _check_last_ip(self):
513+
def _check_last_ip(self, creating=False):
514514
"""Trigger WHOIS lookup if last_ip is not deferred."""
515515
if self._initial_last_ip == models.DEFERRED:
516516
return
517-
self.whois_service.trigger_whois_lookup()
517+
if creating or self.last_ip != self._initial_last_ip:
518+
self.whois_service.process_ip_data_and_location()
518519
self._initial_last_ip = self.last_ip

openwisp_controller/config/base/whois.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.db import models, transaction
77
from django.utils.translation import gettext_lazy as _
88
from jsonfield import JSONField
9+
from swapper import load_model
910

1011
from openwisp_utils.base import TimeStampedEditableModel
1112

@@ -103,7 +104,17 @@ def device_whois_info_delete_handler(instance, **kwargs):
103104
Delete WHOIS information for a device when the last IP address is removed or
104105
when device is deleted.
105106
"""
106-
if instance._get_organization__config_settings().whois_enabled:
107+
Device = load_model("config", "Device")
108+
109+
last_ip = instance.last_ip
110+
existing_devices = Device.objects.filter(_is_deactivated=False).filter(
111+
last_ip=last_ip
112+
)
113+
if (
114+
last_ip
115+
and instance._get_organization__config_settings().whois_enabled
116+
and not existing_devices.exists()
117+
):
107118
transaction.on_commit(lambda: delete_whois_record.delay(instance.last_ip))
108119

109120
# this method is kept here instead of in OrganizationConfigSettings because

openwisp_controller/config/controller/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def get(self, request, pk):
146146
# trigger the WHOIS lookup which checks if WHOIS lookup is required
147147
# or not
148148
elif app_settings.WHOIS_CONFIGURED:
149-
device.whois_service.trigger_whois_lookup()
149+
device.whois_service.process_ip_data_and_location()
150150
checksum_requested.send(
151151
sender=device.__class__, instance=device, request=request
152152
)

openwisp_controller/config/whois/service.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,12 @@ def get_device_whois_info(self):
129129

130130
return self._get_whois_info_from_db(ip_address=ip_address).first()
131131

132-
def trigger_whois_lookup(self):
132+
def process_ip_data_and_location(self):
133133
"""
134-
Trigger WHOIS lookup based on the conditions of `_need_whois_lookup`.
135-
Task is triggered on commit to ensure redundant data is not created.
134+
Trigger WHOIS lookup based on the conditions of `_need_whois_lookup`
135+
and also manage estimated locations based on the conditions of
136+
`_need_estimated_location_management`.
137+
Tasks are triggered on commit to ensure redundant data is not created.
136138
"""
137139
new_ip = self.device.last_ip
138140
if self._need_whois_lookup(new_ip):

openwisp_controller/config/whois/tasks.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,12 @@ def fetch_whois_details(self, device_pk, initial_ip_address, new_ip_address):
128128
device_pk=device_pk, ip_address=new_ip_address
129129
)
130130

131-
# the following check ensures that for a case when device last_ip
132-
# is not changed and there is no related WHOIS record, we do not
133-
# delete the newly created record as both `initial_ip_address` and
134-
# `new_ip_address` would be same for such case.
135-
if initial_ip_address != new_ip_address:
136-
# If any active devices are linked to the following record,
137-
# then they will trigger this task and new record gets created
138-
# with latest data.
131+
# delete WHOIS record for initial IP if no devices are linked to it
132+
if (
133+
not Device.objects.filter(_is_deactivated=False)
134+
.filter(last_ip=initial_ip_address)
135+
.exists()
136+
):
139137
delete_whois_record(ip_address=initial_ip_address)
140138

141139

openwisp_controller/config/whois/test_whois.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ def _verify_whois_details(instance, ip_address):
491491

492492
with self.subTest(
493493
"Test WHOIS create & deletion of old record when last ip is updated"
494+
" when no other devices are linked to the old ip address"
494495
):
495496
old_ip_address = device.last_ip
496497
device.last_ip = "172.217.22.10"
@@ -508,12 +509,61 @@ def _verify_whois_details(instance, ip_address):
508509
WHOISInfo.objects.filter(ip_address=old_ip_address).count(), 0
509510
)
510511

511-
with self.subTest("Test WHOIS delete when device is deleted"):
512+
with self.subTest(
513+
"Test WHOIS create & deletion of old record when last ip is updated"
514+
" when other devices are linked to the old ip address"
515+
):
516+
old_ip_address = device.last_ip
517+
self._create_device(
518+
name="11:22:33:44:55:66",
519+
mac_address="11:22:33:44:55:66",
520+
last_ip="172.217.22.11",
521+
)
522+
device.last_ip = "172.217.22.11"
523+
device.save()
524+
self.assertEqual(mock_info.call_count, 1)
525+
mock_info.reset_mock()
526+
device.refresh_from_db()
527+
528+
_verify_whois_details(
529+
device.whois_service.get_device_whois_info(), device.last_ip
530+
)
531+
532+
# details related to old ip address should be not be deleted
533+
self.assertEqual(
534+
WHOISInfo.objects.filter(ip_address=old_ip_address).count(), 1
535+
)
536+
537+
with self.subTest(
538+
"Test WHOIS not deleted when device is deleted and"
539+
" other active devices are linked to the last_ip"
540+
):
512541
ip_address = device.last_ip
513542
device.delete(check_deactivated=False)
514543
self.assertEqual(mock_info.call_count, 0)
515544
mock_info.reset_mock()
516545

546+
# WHOIS related to the device's last_ip should be deleted
547+
self.assertEqual(WHOISInfo.objects.filter(ip_address=ip_address).count(), 1)
548+
549+
Device.objects.all().delete()
550+
with self.subTest(
551+
"Test WHOIS deleted when device is deleted and"
552+
" no other active devices are linked to the last_ip"
553+
):
554+
device1 = self._create_device(last_ip="172.217.22.11")
555+
device2 = self._create_device(
556+
name="11:22:33:44:55:66",
557+
mac_address="11:22:33:44:55:66",
558+
last_ip="172.217.22.11",
559+
)
560+
device2.deactivate()
561+
mock_info.reset_mock()
562+
ip_address = device1.last_ip
563+
device1.delete(check_deactivated=False)
564+
self.assertEqual(mock_info.call_count, 0)
565+
mock_info.reset_mock()
566+
517567
# WHOIS related to the device's last_ip should be deleted
518568
self.assertEqual(WHOISInfo.objects.filter(ip_address=ip_address).count(), 0)
519569

openwisp_controller/config/whois/tests_utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ def _task_called(self, mocked_task, task_name="WHOIS lookup"):
8080
mocked_get.assert_called_once()
8181
mocked_task.reset_mock()
8282

83+
with self.subTest(f"{task_name} task not called when last_ip not updated"):
84+
device.name = "default.test.Device2"
85+
device.save()
86+
mocked_task.assert_not_called()
87+
mocked_task.reset_mock()
88+
8389
with self.subTest(f"{task_name} task not called when last_ip is private"):
8490
device.last_ip = "10.0.0.1"
8591
device.save()

openwisp_controller/geo/estimated_location/tasks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ def manage_estimated_locations(device_pk, ip_address):
2121
- If the current device has no location or location is estimate, either update
2222
to an existing location; if it exists, else
2323
24-
- A new location is created if no location exists for current device, or
25-
existing one is updated using coords from WHOIS record if it is estimated.
24+
- A new location is created if current device has no location, or
25+
if it does; it is updated using coords from WHOIS record if it is estimated.
2626
2727
In case of multiple devices with same last_ip, the task will send a notification
2828
to the user to resolve the conflict manually.

0 commit comments

Comments
 (0)