1
0
mirror of https://github.com/craigerl/aprsd.git synced 2024-11-24 08:58:49 -05:00

Update Client to report login status

This patch updates the aprsd client base class to report login succes
and any error string associated with a login failure.  Also exposes
the login status so anyone using the client to check for login failure.

Update webchat, listen, server commands to check for initial login
failures and exit if the login failed.  No reason to continue on
if the login fails.
This commit is contained in:
Hemna 2024-11-20 15:55:02 -05:00
parent 9f7d169c18
commit 06c1fb985e
11 changed files with 104 additions and 30 deletions

View File

@ -23,13 +23,24 @@ class APRSISClient(base.APRSClient):
max_timeout = {"hours": 0.0, "minutes": 2, "seconds": 0} max_timeout = {"hours": 0.0, "minutes": 2, "seconds": 0}
self.max_delta = datetime.timedelta(**max_timeout) self.max_delta = datetime.timedelta(**max_timeout)
def stats(self) -> dict: def stats(self, serializable=False) -> dict:
stats = {} stats = {}
if self.is_configured(): if self.is_configured():
if self._client:
keepalive = self._client.aprsd_keepalive
server_string = self._client.server_string
if serializable:
keepalive = keepalive.isoformat()
else:
keepalive = "None"
server_string = "None"
stats = { stats = {
"server_string": self._client.server_string, "connected": self.is_connected,
"sever_keepalive": self._client.aprsd_keepalive,
"filter": self.filter, "filter": self.filter,
"keepalive": keepalive,
"login_status": self.login_status,
"server_string": server_string,
"transport": self.transport(),
} }
return stats return stats
@ -99,22 +110,31 @@ class APRSISClient(base.APRSClient):
self.connected = False self.connected = False
backoff = 1 backoff = 1
aprs_client = None aprs_client = None
retries = 3
retry_count = 0
while not self.connected: while not self.connected:
retry_count += 1
if retry_count >= retries:
break
try: try:
LOG.info(f"Creating aprslib client({host}:{port}) and logging in {user}.") LOG.info(f"Creating aprslib client({host}:{port}) and logging in {user}.")
aprs_client = aprsis.Aprsdis(user, passwd=password, host=host, port=port) aprs_client = aprsis.Aprsdis(user, passwd=password, host=host, port=port)
# Force the log to be the same # Force the log to be the same
aprs_client.logger = LOG aprs_client.logger = LOG
aprs_client.connect() aprs_client.connect()
self.connected = True self.connected = self.login_status["success"] = True
self.login_status["message"] = aprs_client.server_string
backoff = 1 backoff = 1
except LoginError as e: except LoginError as e:
LOG.error(f"Failed to login to APRS-IS Server '{e}'") LOG.error(f"Failed to login to APRS-IS Server '{e}'")
self.connected = False self.connected = self.login_status["success"] = False
self.login_status["message"] = e.message
LOG.error(e.message)
time.sleep(backoff) time.sleep(backoff)
except Exception as e: except Exception as e:
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ") LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
self.connected = False self.connected = self.login_status["success"] = False
self.login_status["message"] = e.message
time.sleep(backoff) time.sleep(backoff)
# Don't allow the backoff to go to inifinity. # Don't allow the backoff to go to inifinity.
if backoff > 5: if backoff > 5:
@ -135,5 +155,7 @@ class APRSISClient(base.APRSClient):
except Exception as e: except Exception as e:
LOG.error(e) LOG.error(e)
LOG.info(e.__cause__) LOG.info(e.__cause__)
raise e
else: else:
LOG.warning("client is None, might be resetting.") LOG.warning("client is None, might be resetting.")
self.connected = False

View File

@ -19,6 +19,10 @@ class APRSClient:
_client = None _client = None
connected = False connected = False
login_status = {
"success": False,
"message": None,
}
filter = None filter = None
lock = threading.Lock() lock = threading.Lock()
@ -38,6 +42,18 @@ class APRSClient:
dict: Statistics about the connection and packet handling dict: Statistics about the connection and packet handling
""" """
@property
def is_connected(self):
return self.connected
@property
def login_success(self):
return self.login_status.get("success", False)
@property
def login_failure(self):
return self.login_status["message"]
def set_filter(self, filter): def set_filter(self, filter):
self.filter = filter self.filter = filter
if self._client: if self._client:

View File

@ -27,6 +27,9 @@ class Aprsdis(aprslib.IS):
# date for last time we heard from the server # date for last time we heard from the server
aprsd_keepalive = datetime.datetime.now() aprsd_keepalive = datetime.datetime.now()
# Which server we are connected to?
server_string = "None"
# timeout in seconds # timeout in seconds
select_timeout = 1 select_timeout = 1
lock = threading.Lock() lock = threading.Lock()

View File

@ -14,8 +14,11 @@ LOG = logging.getLogger("APRSD")
class APRSDFakeClient(base.APRSClient, metaclass=trace.TraceWrapperMetaclass): class APRSDFakeClient(base.APRSClient, metaclass=trace.TraceWrapperMetaclass):
def stats(self) -> dict: def stats(self, serializable=False) -> dict:
return {} return {
"transport": "Fake",
"connected": True,
}
@staticmethod @staticmethod
def is_enabled(): def is_enabled():

View File

@ -17,12 +17,18 @@ class KISSClient(base.APRSClient):
_client = None _client = None
def stats(self) -> dict: def stats(self, serializable=False) -> dict:
stats = {} stats = {}
if self.is_configured(): if self.is_configured():
return { stats = {
"connected": self.is_connected,
"transport": self.transport(), "transport": self.transport(),
} }
if self.transport() == client.TRANSPORT_TCPKISS:
stats["host"] = CONF.kiss_tcp.host
stats["port"] = CONF.kiss_tcp.port
elif self.transport() == client.TRANSPORT_SERIALKISS:
stats["device"] = CONF.kiss_serial.device
return stats return stats
@staticmethod @staticmethod

View File

@ -17,22 +17,4 @@ class APRSClientStats:
@wrapt.synchronized(lock) @wrapt.synchronized(lock)
def stats(self, serializable=False): def stats(self, serializable=False):
cl = client.client_factory.create() return client.client_factory.create().stats(serializable=serializable)
stats = {
"transport": cl.transport(),
"filter": cl.filter,
"connected": cl.connected,
}
if cl.transport() == client.TRANSPORT_APRSIS:
stats["server_string"] = cl.client.server_string
keepalive = cl.client.aprsd_keepalive
if serializable:
keepalive = keepalive.isoformat()
stats["server_keepalive"] = keepalive
elif cl.transport() == client.TRANSPORT_TCPKISS:
stats["host"] = CONF.kiss_tcp.host
stats["port"] = CONF.kiss_tcp.port
elif cl.transport() == client.TRANSPORT_SERIALKISS:
stats["device"] = CONF.kiss_serial.device
return stats

View File

@ -256,6 +256,12 @@ def listen(
LOG.info("Creating client connection") LOG.info("Creating client connection")
aprs_client = client_factory.create() aprs_client = client_factory.create()
LOG.info(aprs_client) LOG.info(aprs_client)
if not aprs_client.login_success:
# We failed to login, will just quit!
msg = f"Login Failure: {aprs_client.login_failure}"
LOG.error(msg)
print(msg)
sys.exit(-1)
LOG.debug(f"Filter by '{filter}'") LOG.debug(f"Filter by '{filter}'")
aprs_client.set_filter(filter) aprs_client.set_filter(filter)

View File

@ -58,6 +58,14 @@ def server(ctx, flush):
LOG.info("Creating client connection") LOG.info("Creating client connection")
aprs_client = client_factory.create() aprs_client = client_factory.create()
LOG.info(aprs_client) LOG.info(aprs_client)
if not aprs_client.login_success:
# We failed to login, will just quit!
msg = f"Login Failure: {aprs_client.login_failure}"
LOG.error(msg)
print(msg)
sys.exit(-1)
# Check to make sure the login worked.
# Create the initial PM singleton and Register plugins # Create the initial PM singleton and Register plugins
# We register plugins first here so we can register each # We register plugins first here so we can register each

View File

@ -24,7 +24,9 @@ from aprsd import utils as aprsd_utils
from aprsd.client import client_factory, kiss from aprsd.client import client_factory, kiss
from aprsd.main import cli from aprsd.main import cli
from aprsd.threads import aprsd as aprsd_threads from aprsd.threads import aprsd as aprsd_threads
from aprsd.threads import keep_alive, rx, tx from aprsd.threads import keep_alive, rx
from aprsd.threads import stats as stats_thread
from aprsd.threads import tx
from aprsd.utils import trace from aprsd.utils import trace
@ -614,10 +616,24 @@ def webchat(ctx, flush, port):
LOG.error("APRS client is not properly configured in config file.") LOG.error("APRS client is not properly configured in config file.")
sys.exit(-1) sys.exit(-1)
# Creates the client object
LOG.info("Creating client connection")
aprs_client = client_factory.create()
LOG.info(aprs_client)
if not aprs_client.login_success:
# We failed to login, will just quit!
msg = f"Login Failure: {aprs_client.login_failure}"
LOG.error(msg)
print(msg)
sys.exit(-1)
keepalive = keep_alive.KeepAliveThread() keepalive = keep_alive.KeepAliveThread()
LOG.info("Start KeepAliveThread") LOG.info("Start KeepAliveThread")
keepalive.start() keepalive.start()
stats_store_thread = stats_thread.APRSDStatsStoreThread()
stats_store_thread.start()
socketio = init_flask(loglevel, quiet) socketio = init_flask(loglevel, quiet)
rx_thread = rx.APRSDPluginRXThread( rx_thread = rx.APRSDPluginRXThread(
packet_queue=threads.packet_queue, packet_queue=threads.packet_queue,

View File

@ -5,6 +5,7 @@ import tracemalloc
from loguru import logger from loguru import logger
from oslo_config import cfg from oslo_config import cfg
import timeago
from aprsd import packets, utils from aprsd import packets, utils
from aprsd.client import client_factory from aprsd.client import client_factory
@ -98,6 +99,9 @@ class KeepAliveThread(APRSDThread):
# check the APRS connection # check the APRS connection
cl = client_factory.create() cl = client_factory.create()
cl_stats = cl.stats()
keepalive = timeago.format(cl_stats.get("keepalive", None))
LOGU.opt(colors=True).info(f"<green>Client keepalive {keepalive}</green>")
# Reset the connection if it's dead and this isn't our # Reset the connection if it's dead and this isn't our
# First time through the loop. # First time through the loop.
# The first time through the loop can happen at startup where # The first time through the loop can happen at startup where

View File

@ -18,6 +18,8 @@ LOG = logging.getLogger("APRSD")
class APRSDRXThread(APRSDThread): class APRSDRXThread(APRSDThread):
_client = None
def __init__(self, packet_queue): def __init__(self, packet_queue):
super().__init__("RX_PKT") super().__init__("RX_PKT")
self.packet_queue = packet_queue self.packet_queue = packet_queue
@ -33,6 +35,12 @@ class APRSDRXThread(APRSDThread):
self._client = client_factory.create() self._client = client_factory.create()
time.sleep(1) time.sleep(1)
return True return True
if not self._client.is_connected:
self._client = client_factory.create()
time.sleep(1)
return True
# setup the consumer of messages and block until a messages # setup the consumer of messages and block until a messages
try: try:
# This will register a packet consumer with aprslib # This will register a packet consumer with aprslib