@@ -25,40 +25,76 @@ def manage_estimated_locations(device_pk, ip_address, add_existing=False):
25
25
- A new location is created if no location exists for current device, or
26
26
existing one is updated using coords from WHOIS record if it is estimated (fuzzy).
27
27
"""
28
+ Device = load_model ("config" , "Device" )
29
+ Location = load_model ("geo" , "Location" )
30
+ WHOISInfo = load_model ("config" , "WHOISInfo" )
31
+ DeviceLocation = load_model ("geo" , "DeviceLocation" )
32
+
33
+ def _create_estimated_location (device_location , location_defaults ):
34
+ with transaction .atomic ():
35
+ location = Location (** location_defaults , is_estimated = True )
36
+ location .full_clean ()
37
+ location .save ()
38
+ device_location .location = location
39
+ device_location .full_clean ()
40
+ device_location .save ()
41
+ logger .info (
42
+ f"Estimated location saved successfully for { device_pk } "
43
+ f" for IP: { ip_address } "
44
+ )
28
45
29
- def _update_or_create_estimated_location (device_location , whois_obj ):
46
+ def _update_or_create_estimated_location (
47
+ device_location , whois_obj , attached_devices_exists = False
48
+ ):
30
49
# Used to update an existing location if it is estimated
31
50
# or create a new one if it doesn't exist
32
- location_defaults = {
33
- ** whois_obj ._get_defaults_for_estimated_location (),
34
- "organization_id" : device .organization_id ,
35
- }
36
- if current_location and current_location .is_estimated :
37
- for attr , value in location_defaults .items ():
38
- setattr (current_location , attr , value )
39
- current_location .full_clean ()
40
- current_location .save ()
41
- elif not current_location :
42
- with transaction .atomic ():
43
- location = Location (** location_defaults , is_estimated = True )
44
- location .full_clean ()
45
- location .save ()
46
- device_location .location = location
47
- device_location .full_clean ()
48
- device_location .save ()
51
+ if whois_obj and whois_obj .coordinates :
52
+ location_defaults = {
53
+ ** whois_obj ._get_defaults_for_estimated_location (),
54
+ "organization_id" : device .organization_id ,
55
+ }
56
+ if current_location and current_location .is_estimated :
57
+ if attached_devices_exists :
58
+ # If there are other devices attached to the current location,
59
+ # we do not update it, but create a new one.
60
+ _create_estimated_location (device_location , location_defaults )
61
+ return
62
+ updated = False
63
+ for attr , value in location_defaults .items ():
64
+ if getattr (current_location , attr ) != value :
65
+ setattr (current_location , attr , value )
66
+ updated = True
67
+ if updated :
68
+ current_location .full_clean ()
69
+ current_location .save ()
70
+ logger .info (
71
+ f"Estimated location saved successfully for { device_pk } "
72
+ f" for IP: { ip_address } "
73
+ )
74
+ elif not current_location :
75
+ # If there is no current location, we create a new one.
76
+ _create_estimated_location (device_location , location_defaults )
77
+ else :
78
+ logger .warning (
79
+ f"Coordinates not available for { device_pk } for IP: { ip_address } ."
80
+ " Estimated location cannot be determined."
81
+ )
82
+ return
49
83
50
- def _handle_attach_existing_location (device , device_location , whois_obj ):
84
+ def _handle_attach_existing_location (
85
+ device , device_location , whois_obj , attached_devices_exists = False
86
+ ):
51
87
# For handling the case when WHOIS already exists for device's new last_ip
52
88
# then we attach the location of the device with same last_ip if it exists.
53
- existing_devices_location = (
89
+ devices_with_location = (
54
90
Device .objects .select_related ("devicelocation" )
55
91
.filter (organization_id = device .organization_id )
56
92
.filter (last_ip = ip_address , devicelocation__location__isnull = False )
57
93
.exclude (pk = device_pk )
58
94
)
59
95
# If there are multiple devices with same last_ip then we need to inform
60
96
# the user to resolve the conflict manually.
61
- if existing_devices_location .count () > 1 :
97
+ if devices_with_location .count () > 1 :
62
98
send_whois_task_notification (
63
99
device_pk = device_pk , notify_type = "location_error"
64
100
)
@@ -67,44 +103,46 @@ def _handle_attach_existing_location(device, device_location, whois_obj):
67
103
f"last_ip { ip_address } . Please resolve the conflict manually."
68
104
)
69
105
return
106
+ first_device = devices_with_location .first ()
70
107
# If existing devices with same last_ip do not have any location
71
108
# then we create a new location based on WHOIS data.
72
- if existing_devices_location .count () == 0 :
73
- _update_or_create_estimated_location (device_location , whois_obj )
109
+ if not first_device :
110
+ _update_or_create_estimated_location (
111
+ device_location , whois_obj , attached_devices_exists
112
+ )
74
113
return
75
- existing_location = existing_devices_location . first () .devicelocation .location
114
+ existing_location = first_device .devicelocation .location
76
115
# We need to remove any existing estimated location of the device
77
- if current_location and current_location . pk != existing_location . pk :
116
+ if current_location and not attached_devices_exists :
78
117
current_location .delete ()
79
118
device_location .location = existing_location
80
119
device_location .full_clean ()
81
120
device_location .save ()
121
+ logger .info (
122
+ f"Estimated location saved successfully for { device_pk } "
123
+ f" for IP: { ip_address } "
124
+ )
82
125
83
- Device = load_model ("config" , "Device" )
84
- Location = load_model ("geo" , "Location" )
85
- WHOISInfo = load_model ("config" , "WHOISInfo" )
86
- DeviceLocation = load_model ("geo" , "DeviceLocation" )
87
-
88
- device = Device .objects .get (pk = device_pk )
89
126
whois_obj = WHOISInfo .objects .filter (ip_address = ip_address ).first ()
127
+ device = Device .objects .get (pk = device_pk )
90
128
device_location , _ = DeviceLocation .objects .select_related (
91
129
"location"
92
130
).get_or_create (content_object_id = device_pk )
93
- current_location = device_location .location
94
131
95
- if add_existing and (not current_location or current_location .is_estimated ):
96
- _handle_attach_existing_location (device , device_location , whois_obj )
132
+ attached_devices_exists = False
133
+ if current_location := device_location .location :
134
+ attached_devices_exists = (
135
+ Device .objects .filter (devicelocation__location_id = current_location .pk )
136
+ .exclude (pk = device_pk )
137
+ .exists ()
138
+ )
97
139
98
- elif whois_obj and whois_obj .coordinates :
99
- _update_or_create_estimated_location (device_location , whois_obj )
140
+ if add_existing and (not current_location or current_location .is_estimated ):
141
+ _handle_attach_existing_location (
142
+ device , device_location , whois_obj , attached_devices_exists
143
+ )
100
144
101
145
else :
102
- logger .warning (
103
- f"Coordinates not available for { device_pk } for IP: { ip_address } ."
104
- " Estimated location cannot be determined."
146
+ _update_or_create_estimated_location (
147
+ device_location , whois_obj , attached_devices_exists
105
148
)
106
- return
107
-
108
- logger .info (
109
- f"Estimated location saved successfully for { device_pk } for IP: { ip_address } "
110
- )
0 commit comments