Skip to content

Commit 53f0acc

Browse files
committed
hot fix and logger for apclientcount task + update readme and .evn
1 parent d034013 commit 53f0acc

File tree

4 files changed

+421
-19
lines changed

4 files changed

+421
-19
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ DB_USER=postgres
9797
DB_PASSWORD=your_password
9898
DB_PORT=3306
9999
100+
APCLIENT_DB_URL=postgresql://postgres:your_password@localhost:3306/apclientcount
101+
100102
# DNA Center API Configuration
101103
DNA_API_URL=https://your-dnac-host/dna/intent/api/v1/
102104
DNA_USERNAME=your_username

ap_monitor/app/dna_api.py

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ def __init__(self, auth_url=AUTH_URL, auth_headers=AUTH_HEADERS):
5656
self.auth_headers = auth_headers
5757
self.token = None
5858
self.token_expiry = None
59+
logger.info(f"Initializing AuthManager with URL: {auth_url}")
60+
logger.info(f"Auth headers (excluding credentials): {dict(filter(lambda x: x[0] != 'Authorization', auth_headers.items()))}")
5961

6062
def get_token(self):
6163
"""Get a valid authentication token, refreshing if necessary."""
@@ -68,9 +70,15 @@ def get_token(self):
6870
if response.status == 200:
6971
response_data = json.load(response)
7072
self.token = response_data.get("Token")
73+
if not self.token:
74+
logger.error("No token in response data")
75+
logger.error(f"Response data: {response_data}")
76+
raise Exception("No token in response data")
7177
self.token_expiry = current_time + timedelta(minutes=55)
7278
logger.info("Authentication token successfully refreshed")
79+
logger.debug(f"Token expiry set to: {self.token_expiry}")
7380
else:
81+
logger.error(f"Failed to obtain access token. Status: {response.status}")
7482
raise Exception(f"Failed to obtain access token: {response.status}")
7583
except HTTPError as e:
7684
logger.error(f"HTTP Error while obtaining access token: {e.code} - {e.reason}")
@@ -144,6 +152,9 @@ def fetch_ap_data(auth_manager, rounded_unix_timestamp, retries=3):
144152
i = 0
145153
length = 250 # Initial value to enter the loop
146154

155+
logger.info(f"Starting AP data fetch with timestamp: {rounded_unix_timestamp}")
156+
logger.info(f"Using site ID: {KEELE_CAMPUS_SITE_ID}")
157+
147158
while length == 250: # Continue until we get less than 250 devices (pagination)
148159
params = {
149160
"deviceRole": "AP",
@@ -160,36 +171,63 @@ def fetch_ap_data(auth_manager, rounded_unix_timestamp, retries=3):
160171
attempt = 0
161172
while attempt < retries:
162173
try:
174+
logger.info(f"Making API request {i + 1} with params: {params}")
163175
with urlopen(req, context=ssl_context, timeout=60) as response:
164176
device_info = json.load(response)
177+
logger.info(f"API Response for request {i + 1}: {json.dumps(device_info, indent=2)}")
178+
179+
# Log response statistics
180+
response_data = device_info.get("response", [])
181+
logger.info(f"Response {i + 1} contains {len(response_data)} devices")
182+
if response_data:
183+
sample_device = response_data[0]
184+
logger.info(f"Sample device data: {json.dumps(sample_device, indent=2)}")
185+
165186
break # Exit retry loop if successful
166187
except (HTTPError, URLError) as e:
167188
attempt += 1
168-
logger.warning(f"{type(e).__name__} (attempt {attempt}): {e}")
189+
logger.warning(f"{type(e).__name__} (attempt {attempt}/{retries}): {e}")
169190
if attempt < retries:
170-
time.sleep(2 ** attempt) # Exponential backoff
191+
wait_time = 2 ** attempt
192+
logger.info(f"Waiting {wait_time} seconds before retry...")
193+
time.sleep(wait_time) # Exponential backoff
171194
else:
172195
logger.error(f"Failed due to {type(e).__name__} after {retries} attempts: {e}")
173196
raise
174197
except Exception as e:
175198
attempt += 1
176-
logger.warning(f"General error (attempt {attempt}): {e}")
199+
logger.warning(f"General error (attempt {attempt}/{retries}): {e}")
177200
if attempt < retries:
178-
time.sleep(2 ** attempt)
201+
wait_time = 2 ** attempt
202+
logger.info(f"Waiting {wait_time} seconds before retry...")
203+
time.sleep(wait_time)
179204
else:
180205
logger.error(f"Failed due to general error after {retries} attempts: {e}")
181206
raise
182207

183-
response_length = len(device_info["response"])
208+
response_length = len(device_info.get("response", []))
184209
length = response_length # Update length for loop condition
185-
data.extend(device_info["response"])
210+
data.extend(device_info.get("response", []))
186211

187212
i += 1
188213
if response_length == 250: # If we got the maximum number, there might be more
214+
logger.info("Received maximum number of devices, checking for more...")
189215
time.sleep(2) # Avoid API rate limits
190216

191217
# Remove duplicates by uuid
192218
unique_devices = list({device["uuid"]: device for device in data}.values())
219+
logger.info(f"Total devices before deduplication: {len(data)}")
220+
logger.info(f"Total unique devices after deduplication: {len(unique_devices)}")
221+
222+
if len(unique_devices) == 0:
223+
logger.warning("No APs found in the API response. This might indicate an issue with the API parameters or authentication.")
224+
logger.warning(f"API URL: {DEVICE_HEALTH_URL}")
225+
logger.warning(f"Site ID: {KEELE_CAMPUS_SITE_ID}")
226+
logger.warning(f"Timestamp: {rounded_unix_timestamp}")
227+
else:
228+
logger.info("Successfully fetched AP data")
229+
logger.info(f"First device sample: {json.dumps(unique_devices[0], indent=2)}")
230+
193231
return unique_devices
194232

195233
def get_ap_data(auth_manager=None, retries=3):
@@ -254,13 +292,22 @@ def insert_apclientcount_data(device_info_list, timestamp, session=None):
254292
radioId_map = {r.radioname: r.radioid for r in session.query(RadioType).all()}
255293
for device in device_info_list:
256294
ap_name = device['name']
257-
location = device.get('location', '')
258-
location_parts = location.split('/')
259-
if len(location_parts) < 5:
260-
logger.warning(f"Skipping device {ap_name} due to invalid location format: {location}")
295+
# Try to get location from multiple fields
296+
location = device.get('location')
297+
if not location or len(location.split('/')) < 5:
298+
snmp_location = device.get('snmpLocation')
299+
if snmp_location and snmp_location.lower() != 'default location' and snmp_location.strip():
300+
location = snmp_location
301+
else:
302+
location_name = device.get('locationName')
303+
if location_name and location_name.strip().lower() != 'null':
304+
location = location_name
305+
location_parts = location.split('/') if location else []
306+
if len(location_parts) < 2:
307+
logger.warning(f"Skipping device {ap_name} due to missing or invalid location fields. location: {location}")
261308
continue
262-
building_name = location_parts[3]
263-
floor_name = location_parts[4]
309+
building_name = location_parts[-2] if len(location_parts) >= 2 else 'Unknown'
310+
floor_name = location_parts[-1] if len(location_parts) >= 1 else 'Unknown'
264311
# Building
265312
building = session.query(ApBuilding).filter_by(buildingname=building_name).first()
266313
if not building:

ap_monitor/app/main.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,25 @@ def update_ap_data_task(db: Session = None):
140140
logger.info(f"Fetched {len(aps)} APs from DNAC API")
141141

142142
for ap in aps:
143-
location = ap.get('location', '')
144-
location_parts = location.split('/')
145-
if len(location_parts) < 5:
146-
logger.warning(f"Skipping device {ap.get('name')} due to invalid location format: {location}")
143+
# Try to get location from multiple fields
144+
location = ap.get('location')
145+
if not location or len(location.split('/')) < 5:
146+
# Fallback to snmpLocation if available and not default
147+
snmp_location = ap.get('snmpLocation')
148+
if snmp_location and snmp_location.lower() != 'default location' and snmp_location.strip():
149+
location = snmp_location
150+
else:
151+
# Fallback to locationName if available and not null
152+
location_name = ap.get('locationName')
153+
if location_name and location_name.strip().lower() != 'null':
154+
location = location_name
155+
location_parts = location.split('/') if location else []
156+
if len(location_parts) < 2:
157+
logger.warning(f"Skipping device {ap.get('name')} due to missing or invalid location fields. location: {location}")
147158
continue
148-
149-
building_name = location_parts[3]
150-
floor_name = location_parts[4]
159+
# Use last two parts as building and floor if possible
160+
building_name = location_parts[-2] if len(location_parts) >= 2 else 'Unknown'
161+
floor_name = location_parts[-1] if len(location_parts) >= 1 else 'Unknown'
151162
logger.debug(f"Processing AP: {ap.get('name')} in Building: {building_name}, Floor: {floor_name}")
152163

153164
# Find or create building

0 commit comments

Comments
 (0)