From e0e85827d6393dc7d6970215ce8966f066eb1e8b Mon Sep 17 00:00:00 2001 From: Annika Wickert Date: Fri, 14 Jan 2022 22:52:21 +0100 Subject: [PATCH] Make the client more resilient (#25) * Continue if controller response was empty or had other failures --- unifi_respondd/respondd_client.py | 38 ++++++++++++++++++------- unifi_respondd/unifi_client.py | 46 +++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/unifi_respondd/respondd_client.py b/unifi_respondd/respondd_client.py index a6a19b2..4057afc 100644 --- a/unifi_respondd/respondd_client.py +++ b/unifi_respondd/respondd_client.py @@ -62,14 +62,17 @@ class SoftwareInfo: firmware: FirmwareInfo + @dataclasses.dataclass class InterfacesInfo: other: List[str] + @dataclasses.dataclass class IntInfo: interfaces: InterfacesInfo + @dataclasses.dataclass class NetworkInfo: """This class contains the network information of an AP. @@ -79,10 +82,12 @@ class NetworkInfo: mac: str mesh: Dict[str, IntInfo] + @dataclasses.dataclass class SystemInfo: domain_code: str + @dataclass_json @dataclasses.dataclass class NodeInfo: @@ -185,21 +190,25 @@ class StatisticsInfo: gateway6: str gateway_nexthop: str + @dataclasses.dataclass class NeighbourDetails: tq: int lastseen: float + @dataclasses.dataclass class Neighbours: neighbours: Dict[str, NeighbourDetails] + @dataclass_json @dataclasses.dataclass class NeighboursInfo: node_id: str batadv: Dict[str, Neighbours] + class ResponddClient: """This class receives a request from the respondd server and returns the response.""" @@ -248,8 +257,13 @@ def getNodeInfos(self): location=LocationInfo(latitude=ap.latitude, longitude=ap.longitude), hardware=HardwareInfo(model=ap.model), owner=OwnerInfo(contact=ap.contact), - network=NetworkInfo(mac=ap.mac,mesh={"bat0": IntInfo(interfaces=InterfacesInfo(other=[ap.mac]))}), - system=SystemInfo(domain_code=ap.domain_code) + network=NetworkInfo( + mac=ap.mac, + mesh={ + "bat0": IntInfo(interfaces=InterfacesInfo(other=[ap.mac])) + }, + ), + system=SystemInfo(domain_code=ap.domain_code), ) ) return nodes @@ -295,12 +309,15 @@ def getNeighbours(self): neighbours.append( NeighboursInfo( node_id=ap.mac.replace(":", ""), - batadv={ap.mac: Neighbours( - neighbours={ap.neighbour_mac: NeighbourDetails( - tq=255, - lastseen=0.45 - )} - )} + batadv={ + ap.mac: Neighbours( + neighbours={ + ap.neighbour_mac: NeighbourDetails( + tq=255, lastseen=0.45 + ) + } + ) + }, ) ) return neighbours @@ -315,7 +332,6 @@ def listenMulticast(self): def sendUnicast(self): logger.info("Using unicast method") - timeSleep = int(60 - (self._timeStop - self._timeStart) % 60) if self._config.verbose: logger.debug("will now sleep " + str(timeSleep) + " seconds") @@ -346,6 +362,8 @@ def start(self): self.sendUnicast() self._timeStart = time.time() self._aps = unifi_client.get_infos() + if self._aps is None: + continue if msgSplit[0] == "GET": # multi_request for request in msgSplit[1:]: responseStruct[request] = self.buildStruct(request) @@ -375,7 +393,7 @@ def buildStruct(self, responseType): responseClass = self._statistics elif responseType == "nodeinfo": responseClass = self._nodeinfos - elif responseType == 'neighbours': + elif responseType == "neighbours": responseClass = self._neighbours else: logger.warning("unknown command: " + responseType) diff --git a/unifi_respondd/unifi_client.py b/unifi_respondd/unifi_client.py index 7e488ec..9066067 100644 --- a/unifi_respondd/unifi_client.py +++ b/unifi_respondd/unifi_client.py @@ -1,18 +1,20 @@ #!/usr/bin/env python3 -from json import load from geopy.point import Point from pyunifi.controller import Controller from typing import List from geopy.geocoders import Nominatim from unifi_respondd import config from requests import get as rget +from unifi_respondd import logger import time import dataclasses import re + ffnodes = None + @dataclasses.dataclass class Accesspoint: """This class contains the information of an AP. @@ -96,25 +98,31 @@ def get_location_by_address(address, app): except: return get_location_by_address(address) + def scrape(url): """returns remote json""" try: return rget(url).json() except Exception as ex: - print('Error: %s' %(ex)) + logger.error("Error: %s" % (ex)) + def get_infos(): """This function gathers all the information and returns a list of Accesspoint objects.""" cfg = config.Config.from_dict(config.load_config()) ffnodes = scrape(cfg.nodelist) - c = Controller( - host=cfg.controller_url, - username=cfg.username, - password=cfg.password, - port=cfg.controller_port, - version=cfg.version, - ssl_verify=cfg.ssl_verify, - ) + try: + c = Controller( + host=cfg.controller_url, + username=cfg.username, + password=cfg.password, + port=cfg.controller_port, + version=cfg.version, + ssl_verify=cfg.ssl_verify, + ) + except Exception as ex: + logger.error("Error: %s" % (ex)) + return geolookup = Nominatim(user_agent="ffmuc_respondd") aps = Accesspoints(accesspoints=[]) for site in c.get_sites(): @@ -164,8 +172,16 @@ def get_infos(): pass try: neighbour_mac = cfg.offloader_mac.get(site["desc"], None) - offloader_id = cfg.offloader_mac.get(site["desc"], "").replace(':', '') - offloader = list(filter(lambda x:x["mac"]==cfg.offloader_mac.get(site["desc"], ""),ffnodes["nodes"]))[0] + offloader_id = cfg.offloader_mac.get(site["desc"], "").replace( + ":", "" + ) + offloader = list( + filter( + lambda x: x["mac"] + == cfg.offloader_mac.get(site["desc"], ""), + ffnodes["nodes"], + ) + )[0] except: neighbour_mac = None offloader_id = None @@ -173,7 +189,7 @@ def get_infos(): pass uplink = ap.get("uplink", None) if uplink is not None and uplink.get("ap_mac", None) is not None: - neighbour_mac = uplink.get("ap_mac") + neighbour_mac = uplink.get("ap_mac") aps.accesspoints.append( Accesspoint( name=ap.get("name", None), @@ -200,7 +216,9 @@ def get_infos(): gateway6=offloader.get("gateway6", None), gateway_nexthop=offloader_id, neighbour_mac=neighbour_mac, - domain_code=offloader.get("domain", "ffmuc_unifi_respondd_fallback"), + domain_code=offloader.get( + "domain", "ffmuc_unifi_respondd_fallback" + ), ) ) return aps