Skip to content

Conversation

DragnEmperor
Copy link
Member

@DragnEmperor DragnEmperor commented Aug 26, 2025

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #1058

Description of Changes

Added functionality to update WHOIS records which are older than a threshold which is a configurable setting.
Since the main conditions are different from existing conditions of whois lookup, have defined new method for handling update.
Updated documentation and test cases.

@DragnEmperor DragnEmperor self-assigned this Aug 26, 2025
@DragnEmperor DragnEmperor added enhancement gsoc Part of a Google Summer of Code project labels Aug 26, 2025
@DragnEmperor DragnEmperor changed the title [enhancement] Added updating older WHOIS records [enhancement] Updating stale WHOIS records Aug 27, 2025
@DragnEmperor DragnEmperor force-pushed the issues/1034-fuzzy-location-creation branch from 0a1c1cc to 2c1dda6 Compare August 29, 2025 04:57
Overview
--------

The Estimated Location feature automatically creates or updates a device’s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we need not to repeat the topic name. Same for other cases as well

Suggested change
The Estimated Location feature automatically creates or updates a device’s
This feature automatically creates or updates a device’s

Comment on lines 128 to 130
This will be triggered for the same scenarios defined in `trigger
conditions <whois_trigger_conditions_>`_ but among the conditions **only
WHOIS enablement is required**.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement is not very clear

)
return

def _handle_attach_existing_location(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this function has a lot of responsibilities, should we refactor this?



class Command(BaseCommand):
help = "Clear the last IP address, if set, of active devices of all organizations."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
help = "Clear the last IP address, if set, of active devices of all organizations."
help = "Clears the last IP address (if set) for every active devices of all organizations."

"Are you sure you want to do this?\n\n"
"Type 'yes' to continue, or 'no' to cancel: "
)
if input("".join(message)) != "yes":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make it case-insensitive?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can but since we have explicitly provided the choices, it shouldn't be confusing

raise CommandError("Operation cancelled by user.")

devices = Device.objects.filter(_is_deactivated=False).only("last_ip")
# Filter devices that have no WHOIS information for their last IP
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Filter devices that have no WHOIS information for their last IP
# Filter out devices that have no WHOIS information for their last IP

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It excludes the devices "having" WHOIS information. Will rephrase this for clarity

Comment on lines 141 to 231
if not self.is_valid_public_ip_address(new_ip):
return False

if not self.is_whois_enabled:
return False

return self.is_estimated_location_enabled
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if not self.is_valid_public_ip_address(new_ip):
return False
if not self.is_whois_enabled:
return False
return self.is_estimated_location_enabled
return (
self.is_valid_public_ip_address(new_ip) and
self.is_whois_enabled and
self.is_estimated_location_enabled
)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am following the same structure that we have done for other functions dealing with conditions within this class.
I think we decided to split the conditions in other functions to make them more readable.

Comment on lines 79 to 85
# Host is based on the db that is used to fetch the details.
# As we are using GeoLite2, 'geolite.info' host is used.
# Refer: https://geoip2.readthedocs.io/en/latest/#sync-web-service-example
ip_client = geoip2_webservice.Client(
account_id=app_settings.WHOIS_GEOIP_ACCOUNT,
license_key=app_settings.WHOIS_GEOIP_KEY,
host="geolite.info",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This should be abstracted out for extensibility

**app_settings.API_TASK_RETRY_OPTIONS,
)
def fetch_whois_details(self, device_pk, initial_ip_address, new_ip_address):
def fetch_whois_details(self, device_pk, initial_ip_address):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is quite big to follow, we should look to break it in smaller functions, if possible

@DragnEmperor DragnEmperor force-pushed the issues/1058-update-whois branch 2 times, most recently from ddcf823 to 55a77c2 Compare August 29, 2025 23:09
Copy link
Member

@codesankalp codesankalp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice there are quite a few else conditions now just to handle updates. If the task is only about refreshing stale information, why not run it as a scheduled task in Celery? Would that cause any issues?

This way, we can avoid calling the update method in the WhoIs service for every checksum call or other device update operation.

A WHOIS lookup is triggered automatically when:

- A new device is registered.
- A new device is registered or its last IP address is changed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can add it as a new pointer below

Comment on lines 121 to 212
- The WHOIS information of new ip is present and is not older than
14 days.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use the configured variable name

@DragnEmperor
Copy link
Member Author

I notice there are quite a few else conditions now just to handle updates. If the task is only about refreshing stale information, why not run it as a scheduled task in Celery? Would that cause any issues?

This way, we can avoid calling the update method in the WhoIs service for every checksum call or other device update operation.

Hi Sankalp, initially the plan was to create a task but as discussed on the weekly call, it was decided that it should run only when required to avoid too many requests.

@DragnEmperor DragnEmperor force-pushed the issues/1034-fuzzy-location-creation branch 2 times, most recently from 7c909e4 to f934ae3 Compare September 9, 2025 16:12
Base automatically changed from issues/1034-fuzzy-location-creation to gsoc25-whois September 20, 2025 16:21
@nemesifier nemesifier changed the title [enhancement] Updating stale WHOIS records [feature] Updating stale WHOIS records Sep 20, 2025
@DragnEmperor DragnEmperor force-pushed the issues/1058-update-whois branch from df469a6 to 155ea0b Compare September 21, 2025 10:41
Managing Older Estimated Locations
----------------------------------

Whenever location related fields in WHOIS record are updated as per
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Whenever location related fields in WHOIS record are updated as per
Whenever location related fields in WHOIS records are updated as per

self.device = device

@staticmethod
def geoip_client():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about?

Suggested change
def geoip_client():
def get_geoip_client():

For consistency.

raise exc_type(message)
except requests.RequestException as e:
raise e

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this blank line

Suggested change


try:
data = ip_client.city(ip_address=ip_address)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this blank line

Suggested change

the relevant information.
"""
ip_client = self.geoip_client()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this blank line

Suggested change

ip_address = self.device.last_ip
if not self.is_valid_public_ip_address(ip_address):
return

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this blank line

Suggested change

Existing location here means a location of another device whose last_ip matches
the given ip_address.
Does not alters the existing location if it is not estimated.
def _create_estimated_location(device_location, location_defaults, ip_address):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make this function create or update the location, this would make the code more readable.

We have similar constructs in our code base, please see:

https://github.com/search?q=org%3Aopenwisp+get_or_create&type=code

Moreover, another pattern that we're striving to follow is to have most logic concentrated in either models or classes like the one you did for the whois service, and then have admin views, API views and celery tasks use the methods provided by in models.

device_pk=device.pk,
notify_type="estimated_location_updated",
actor=current_location,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this logic would go in the create or update function/method.

Copy link
Member

@codesankalp codesankalp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it and it is working as expected.

Comment on lines 109 to 110
# If there are multiple devices with same last_ip then we need to inform
# the user to resolve the conflict manually.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this comment? logger error message seems to be clear.

Comment on lines 31 to 39
if existing_device_location:
existing_location = existing_device_location.location
# We need to remove existing estimated location of the device
# if it is not shared
if attached_devices_exists is False:
current_location.delete()
device_location.location = existing_location
device_location.full_clean()
device_location.save()
logger.info(
f"Estimated location saved successfully for {device.pk}"
f" for IP: {ip_address}"
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a test asserting the number of queries?

Copy link
Member Author

@DragnEmperor DragnEmperor Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have added this in the latest commit for creation/updating related tests for estimate location

Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding assertNumQueries, it's always good to keep the query count in check because new contributors can inadvertently change ORM calls and cause query count to go banana.

I have a few comments below.

whois_instance = WHOISInfo(**whois_details)
whois_instance.full_clean()
whois_instance.save()
whois_instance.save(force_insert=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one curiosity, why is this necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Django hits an UPDATE query using the primary key ip_address to update any existing records, if any. Since we have checks to ensure we update existing record, if it exists, I think we can proceed with force_insert.

# We need to remove existing estimated location of the device
# if it is not shared
if attached_devices_exists is False:
current_location.delete()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the comment above needs to be moved too.

@DragnEmperor DragnEmperor force-pushed the issues/1058-update-whois branch 2 times, most recently from cf35f57 to 5eca0e0 Compare October 15, 2025 02:57
Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DragnEmperor the test failing due to email verification not being sent can be fixed with this: 260289a.

Added required checks for ensuring WHOIS records
older than a certain Threshold (configurable) will
be updated along with the coordinates.
Formatted existing code to be consistent with current
coding standards.
Extend django-loci's location change form template

Closes  #1058

Signed-off-by: DragnEmperor <[email protected]>
@DragnEmperor DragnEmperor force-pushed the issues/1058-update-whois branch from 005500f to 3346bfc Compare October 17, 2025 03:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement gsoc Part of a Google Summer of Code project

Development

Successfully merging this pull request may close these issues.

4 participants