Skip to content

Commit

Permalink
Update Client to report login status
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hemna committed Nov 20, 2024
1 parent 9f7d169 commit 06c1fb9
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 30 deletions.
34 changes: 28 additions & 6 deletions aprsd/client/aprsis.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,24 @@ def __init__(self):
max_timeout = {"hours": 0.0, "minutes": 2, "seconds": 0}
self.max_delta = datetime.timedelta(**max_timeout)

def stats(self) -> dict:
def stats(self, serializable=False) -> dict:
stats = {}
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 = {
"server_string": self._client.server_string,
"sever_keepalive": self._client.aprsd_keepalive,
"connected": self.is_connected,
"filter": self.filter,
"keepalive": keepalive,
"login_status": self.login_status,
"server_string": server_string,
"transport": self.transport(),
}

return stats
Expand Down Expand Up @@ -99,22 +110,31 @@ def setup_connection(self):
self.connected = False
backoff = 1
aprs_client = None
retries = 3
retry_count = 0
while not self.connected:
retry_count += 1
if retry_count >= retries:
break
try:
LOG.info(f"Creating aprslib client({host}:{port}) and logging in {user}.")
aprs_client = aprsis.Aprsdis(user, passwd=password, host=host, port=port)
# Force the log to be the same
aprs_client.logger = LOG
aprs_client.connect()
self.connected = True
self.connected = self.login_status["success"] = True
self.login_status["message"] = aprs_client.server_string
backoff = 1
except LoginError as 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)
except Exception as 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)
# Don't allow the backoff to go to inifinity.
if backoff > 5:
Expand All @@ -135,5 +155,7 @@ def consumer(self, callback, blocking=False, immortal=False, raw=False):
except Exception as e:
LOG.error(e)
LOG.info(e.__cause__)
raise e
else:
LOG.warning("client is None, might be resetting.")
self.connected = False
16 changes: 16 additions & 0 deletions aprsd/client/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ class APRSClient:
_client = None

connected = False
login_status = {
"success": False,
"message": None,
}
filter = None
lock = threading.Lock()

Expand All @@ -38,6 +42,18 @@ def stats(self) -> dict:
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):
self.filter = filter
if self._client:
Expand Down
3 changes: 3 additions & 0 deletions aprsd/client/drivers/aprsis.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class Aprsdis(aprslib.IS):
# date for last time we heard from the server
aprsd_keepalive = datetime.datetime.now()

# Which server we are connected to?
server_string = "None"

# timeout in seconds
select_timeout = 1
lock = threading.Lock()
Expand Down
7 changes: 5 additions & 2 deletions aprsd/client/fake.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@

class APRSDFakeClient(base.APRSClient, metaclass=trace.TraceWrapperMetaclass):

def stats(self) -> dict:
return {}
def stats(self, serializable=False) -> dict:
return {
"transport": "Fake",
"connected": True,
}

@staticmethod
def is_enabled():
Expand Down
10 changes: 8 additions & 2 deletions aprsd/client/kiss.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ class KISSClient(base.APRSClient):

_client = None

def stats(self) -> dict:
def stats(self, serializable=False) -> dict:
stats = {}
if self.is_configured():
return {
stats = {
"connected": self.is_connected,
"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

@staticmethod
Expand Down
20 changes: 1 addition & 19 deletions aprsd/client/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,4 @@ class APRSClientStats:

@wrapt.synchronized(lock)
def stats(self, serializable=False):
cl = client.client_factory.create()
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
return client.client_factory.create().stats(serializable=serializable)
6 changes: 6 additions & 0 deletions aprsd/cmds/listen.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,12 @@ def listen(
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)

LOG.debug(f"Filter by '{filter}'")
aprs_client.set_filter(filter)
Expand Down
8 changes: 8 additions & 0 deletions aprsd/cmds/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ def server(ctx, flush):
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)

# Check to make sure the login worked.

# Create the initial PM singleton and Register plugins
# We register plugins first here so we can register each
Expand Down
18 changes: 17 additions & 1 deletion aprsd/cmds/webchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
from aprsd.client import client_factory, kiss
from aprsd.main import cli
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


Expand Down Expand Up @@ -614,10 +616,24 @@ def webchat(ctx, flush, port):
LOG.error("APRS client is not properly configured in config file.")
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()
LOG.info("Start KeepAliveThread")
keepalive.start()

stats_store_thread = stats_thread.APRSDStatsStoreThread()
stats_store_thread.start()

socketio = init_flask(loglevel, quiet)
rx_thread = rx.APRSDPluginRXThread(
packet_queue=threads.packet_queue,
Expand Down
4 changes: 4 additions & 0 deletions aprsd/threads/keep_alive.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from loguru import logger
from oslo_config import cfg
import timeago

from aprsd import packets, utils
from aprsd.client import client_factory
Expand Down Expand Up @@ -98,6 +99,9 @@ def loop(self):

# check the APRS connection
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
# First time through the loop.
# The first time through the loop can happen at startup where
Expand Down
8 changes: 8 additions & 0 deletions aprsd/threads/rx.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@


class APRSDRXThread(APRSDThread):
_client = None

def __init__(self, packet_queue):
super().__init__("RX_PKT")
self.packet_queue = packet_queue
Expand All @@ -33,6 +35,12 @@ def loop(self):
self._client = client_factory.create()
time.sleep(1)
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
try:
# This will register a packet consumer with aprslib
Expand Down

0 comments on commit 06c1fb9

Please sign in to comment.