Skip to content

Commit 49d8689

Browse files
committed
[refactor] Improve queries, remove redundant check in DeviceChecksumView
Check in DeviceCheckSumView was redundant as the same check is executed in trigger_whois_lookup task as well. Also we need estimated location feature to work if WHOIS record already exists so this handles that as well. Signed-off-by: DragnEmperor <[email protected]>
1 parent 641a3d9 commit 49d8689

File tree

6 files changed

+61
-24
lines changed

6 files changed

+61
-24
lines changed

docs/user/estimated-location.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,7 @@ the device.
5050
If there is **no matching location**, a new estimated location is created
5151
or the existing one is updated using coordinates from the WHOIS record,
5252
but only if the existing location is estimated.
53+
54+
If two devices share the same IP address and are assigned to the same
55+
location, and the last IP of one of the devices is updated, the system
56+
will create a new estimated location for that device.

openwisp_controller/config/controller/views.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,9 @@ def get(self, request, pk):
143143
self.update_device_cache(device)
144144
# When update fields are present then save() will run the WHOIS
145145
# lookup. But if there are no update fields, we still want to
146-
# trigger the WHOIS lookup if there is no record for the device's
147-
# last_ip.
148-
elif (
149-
app_settings.WHOIS_CONFIGURED
150-
and not device.whois_service.get_device_whois_info()
151-
):
146+
# trigger the WHOIS lookup which checks if WHOIS lookup is required
147+
# or not
148+
elif app_settings.WHOIS_CONFIGURED:
152149
device.whois_service.trigger_whois_lookup()
153150
checksum_requested.send(
154151
sender=device.__class__, instance=device, request=request

openwisp_controller/config/whois/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,6 @@ def trigger_whois_lookup(self):
149149
elif self._need_estimated_location_management(new_ip):
150150
transaction.on_commit(
151151
lambda: manage_estimated_locations.delay(
152-
device_pk=self.device.pk, ip_address=new_ip, add_existing=True
152+
device_pk=self.device.pk, ip_address=new_ip
153153
)
154154
)

openwisp_controller/config/whois/test_whois.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,30 @@ def test_whois_task_called(self, mocked_lookup_task):
347347
with self.subTest(
348348
"WHOIS lookup task not called when last_ip has related WhoIsInfo"
349349
):
350+
device.organization.config_settings.whois_enabled = True
351+
device.organization.config_settings.save()
350352
device.last_ip = "172.217.22.14"
351353
self._create_whois_info(ip_address=device.last_ip)
352354
device.save()
353355
mocked_lookup_task.assert_not_called()
354356
mocked_lookup_task.reset_mock()
355357

358+
with self.subTest(
359+
"WHOIS lookup task not called via DeviceChecksumView when "
360+
"last_ip has related WhoIsInfo"
361+
):
362+
WHOISInfo.objects.all().delete()
363+
self._create_whois_info(ip_address=device.last_ip)
364+
self._create_config(device=device)
365+
response = self.client.get(
366+
reverse("controller:device_checksum", args=[device.pk]),
367+
{"key": device.key},
368+
REMOTE_ADDR=device.last_ip,
369+
)
370+
self.assertEqual(response.status_code, 200)
371+
mocked_lookup_task.assert_not_called()
372+
mocked_lookup_task.reset_mock()
373+
356374
@mock.patch.object(app_settings, "WHOIS_CONFIGURED", True)
357375
@mock.patch("openwisp_controller.config.whois.tasks.fetch_whois_details.delay")
358376
def test_whois_multiple_orgs(self, mocked_task):

openwisp_controller/geo/estimated_location/tasks.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,22 @@
1010

1111

1212
@shared_task
13-
def manage_estimated_locations(device_pk, ip_address, add_existing=False):
13+
def manage_estimated_locations(device_pk, ip_address):
1414
"""
1515
Creates/updates estimated location for a device based on the latitude and
16-
longitude or attaches an existing location if `add_existing` is True.
16+
longitude or attaches an existing location.
1717
Existing location here means a location of another device whose last_ip matches
1818
the given ip_address.
19+
Does not alters the existing location if it is not estimated.
1920
20-
When `add_existing` is True:
21-
- If the current device has no location, attach the existing one; if it does,
22-
update it with the existing one only if it is estimated (fuzzy).
21+
- If the current device has no location or location is estimate, either update
22+
to an existing location; if it exists, else
2323
24-
When `add_existing` is False:
2524
- A new location is created if no location exists for current device, or
26-
existing one is updated using coords from WHOIS record if it is estimated (fuzzy).
25+
existing one is updated using coords from WHOIS record if it is estimated.
26+
27+
In case of multiple devices with same last_ip, the task will send a notification
28+
to the user to resolve the conflict manually.
2729
"""
2830
Device = load_model("config", "Device")
2931
Location = load_model("geo", "Location")
@@ -123,10 +125,14 @@ def _handle_attach_existing_location(
123125
)
124126

125127
whois_obj = WHOISInfo.objects.filter(ip_address=ip_address).first()
126-
device = Device.objects.get(pk=device_pk)
127-
device_location, _ = DeviceLocation.objects.select_related(
128-
"location"
129-
).get_or_create(content_object_id=device_pk)
128+
device = (
129+
Device.objects.select_related("devicelocation__location", "organization")
130+
.only("organization_id", "devicelocation")
131+
.get(pk=device_pk)
132+
)
133+
134+
if not (device_location := getattr(device, "devicelocation", None)):
135+
device_location = DeviceLocation(content_object=device)
130136

131137
attached_devices_exists = False
132138
if current_location := device_location.location:
@@ -136,12 +142,7 @@ def _handle_attach_existing_location(
136142
.exists()
137143
)
138144

139-
if add_existing and (not current_location or current_location.is_estimated):
145+
if not current_location or current_location.is_estimated:
140146
_handle_attach_existing_location(
141147
device, device_location, whois_obj, attached_devices_exists
142148
)
143-
144-
else:
145-
_update_or_create_estimated_location(
146-
device_location, whois_obj, attached_devices_exists
147-
)

openwisp_controller/geo/estimated_location/test_estimated_location.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
Device = load_model("config", "Device")
1818
Location = load_model("geo", "Location")
19+
WHOISInfo = load_model("config", "WHOISInfo")
1920
Notification = load_model("openwisp_notifications", "Notification")
2021
OrganizationConfigSettings = load_model("config", "OrganizationConfigSettings")
2122

@@ -143,6 +144,22 @@ def test_estimated_location_task_called(
143144
mocked_estimated_location_task.assert_called()
144145
mocked_estimated_location_task.reset_mock()
145146

147+
with self.subTest(
148+
"Estimated location task not called via DeviceChecksumView when "
149+
"last_ip has related WhoIsInfo"
150+
):
151+
WHOISInfo.objects.all().delete()
152+
self._create_whois_info(ip_address=device.last_ip)
153+
self._create_config(device=device)
154+
response = self.client.get(
155+
reverse("controller:device_checksum", args=[device.pk]),
156+
{"key": device.key},
157+
REMOTE_ADDR=device.last_ip,
158+
)
159+
self.assertEqual(response.status_code, 200)
160+
mocked_estimated_location_task.assert_called()
161+
mocked_estimated_location_task.reset_mock()
162+
146163
@mock.patch.object(config_app_settings, "WHOIS_CONFIGURED", True)
147164
@mock.patch(_ESTIMATED_LOCATION_INFO_LOGGER)
148165
@mock.patch(_WHOIS_GEOIP_CLIENT)

0 commit comments

Comments
 (0)