mirror of
https://github.com/craigerl/aprsd.git
synced 2024-11-21 23:55:17 -05:00
Convert config to oslo_config
This patch is the initial conversion of the custom config and config file yaml format to oslo_config's configuration mechanism. The resulting config format is now an ini type file. The default location is ~/.config/aprsd/aprsd.conf This is a backwards incompatible change. You will have to rebuild the config file and edit it. Also any aprsd plugins can now define config options in code and add an setup.cfg entry_point definition oslo_config.opts = foo.conf = foo.conf:list_opts
This commit is contained in:
parent
ce3b29f990
commit
e13ca0061a
28
ChangeLog
28
ChangeLog
@ -1,9 +1,37 @@
|
||||
CHANGES
|
||||
=======
|
||||
|
||||
* Added rain formatting unit tests to WeatherPacket
|
||||
* Fix Rain reporting in WeatherPacket send
|
||||
* Removed Packet.send()
|
||||
* Removed watchlist plugins
|
||||
* Fix PluginManager.get\_plugins
|
||||
* Cleaned up PluginManager
|
||||
* Cleaned up PluginManager
|
||||
* Update routing for weatherpacket
|
||||
* Fix some WeatherPacket formatting
|
||||
* Fix pep8 violation
|
||||
* Add packet filtering for aprsd listen
|
||||
* Added WeatherPacket encoding
|
||||
* Updated webchat and listen for queue based RX
|
||||
* reworked collecting and reporting stats
|
||||
* Removed unused threading code
|
||||
* Change RX packet processing to enqueu
|
||||
* Make tracking objectstores work w/o initializing
|
||||
* Cleaned up packet transmit class attributes
|
||||
* Fix packets timestamp to int
|
||||
* More messaging -> packets cleanup
|
||||
* Cleaned out all references to messaging
|
||||
* Added contructing a GPSPacket for sending
|
||||
* cleanup webchat
|
||||
* Reworked all packet processing
|
||||
* Updated plugins and plugin interfaces for Packet
|
||||
* Started using dataclasses to describe packets
|
||||
|
||||
v2.6.1
|
||||
------
|
||||
|
||||
* v2.6.1
|
||||
* Fixed position report for webchat beacon
|
||||
* Try and fix broken 32bit qemu builds on 64bit system
|
||||
* Add unit tests for webchat
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
# python included libs
|
||||
import datetime
|
||||
from importlib.metadata import entry_points
|
||||
from importlib.metadata import version as metadata_version
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
@ -29,12 +31,11 @@ import time
|
||||
|
||||
import click
|
||||
import click_completion
|
||||
from oslo_config import cfg, generator
|
||||
|
||||
# local imports here
|
||||
import aprsd
|
||||
from aprsd import cli_helper
|
||||
from aprsd import config as aprsd_config
|
||||
from aprsd import packets, stats, threads, utils
|
||||
from aprsd import cli_helper, packets, stats, threads, utils
|
||||
|
||||
|
||||
# setup the global logger
|
||||
@ -111,8 +112,32 @@ def check_version(ctx):
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def sample_config(ctx):
|
||||
"""This dumps the config to stdout."""
|
||||
click.echo(aprsd_config.dump_default_cfg())
|
||||
"""Generate a sample Config file from aprsd and all installed plugins."""
|
||||
|
||||
def get_namespaces():
|
||||
args = []
|
||||
|
||||
selected = entry_points(group="oslo.config.opts")
|
||||
for entry in selected:
|
||||
if "aprsd" in entry.name:
|
||||
args.append("--namespace")
|
||||
args.append(entry.name)
|
||||
|
||||
return args
|
||||
|
||||
args = get_namespaces()
|
||||
config_version = metadata_version("oslo.config")
|
||||
logging.basicConfig(level=logging.WARN)
|
||||
conf = cfg.ConfigOpts()
|
||||
generator.register_cli_opts(conf)
|
||||
try:
|
||||
conf(args, version=config_version)
|
||||
except cfg.RequiredOptError:
|
||||
conf.print_help()
|
||||
if not sys.argv[1:]:
|
||||
raise SystemExit
|
||||
raise
|
||||
generator.generate(conf)
|
||||
|
||||
|
||||
@cli.command()
|
||||
|
@ -1,13 +1,22 @@
|
||||
from functools import update_wrapper
|
||||
from pathlib import Path
|
||||
import typing as t
|
||||
|
||||
import click
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import config as aprsd_config
|
||||
import aprsd
|
||||
from aprsd.logging import log
|
||||
from aprsd.utils import trace
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
home = str(Path.home())
|
||||
DEFAULT_CONFIG_DIR = f"{home}/.config/aprsd/"
|
||||
DEFAULT_SAVE_FILE = f"{home}/.config/aprsd/aprsd.p"
|
||||
DEFAULT_CONFIG_FILE = f"{home}/.config/aprsd/aprsd.conf"
|
||||
|
||||
|
||||
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
|
||||
|
||||
common_options = [
|
||||
@ -27,7 +36,7 @@ common_options = [
|
||||
"--config",
|
||||
"config_file",
|
||||
show_default=True,
|
||||
default=aprsd_config.DEFAULT_CONFIG_FILE,
|
||||
default=DEFAULT_CONFIG_FILE,
|
||||
help="The aprsd config file to use for options.",
|
||||
),
|
||||
click.option(
|
||||
@ -51,16 +60,22 @@ def process_standard_options(f: F) -> F:
|
||||
def new_func(*args, **kwargs):
|
||||
ctx = args[0]
|
||||
ctx.ensure_object(dict)
|
||||
if kwargs["config_file"]:
|
||||
default_config_files = [kwargs["config_file"]]
|
||||
else:
|
||||
default_config_files = None
|
||||
CONF(
|
||||
[], project="aprsd", version=aprsd.__version__,
|
||||
default_config_files=default_config_files,
|
||||
)
|
||||
ctx.obj["loglevel"] = kwargs["loglevel"]
|
||||
ctx.obj["config_file"] = kwargs["config_file"]
|
||||
ctx.obj["quiet"] = kwargs["quiet"]
|
||||
ctx.obj["config"] = aprsd_config.parse_config(kwargs["config_file"])
|
||||
log.setup_logging(
|
||||
ctx.obj["config"],
|
||||
ctx.obj["loglevel"],
|
||||
ctx.obj["quiet"],
|
||||
)
|
||||
if ctx.obj["config"]["aprsd"].get("trace", False):
|
||||
if CONF.trace_enabled:
|
||||
trace.setup_tracing(["method", "api"])
|
||||
|
||||
del kwargs["loglevel"]
|
||||
|
114
aprsd/client.py
114
aprsd/client.py
@ -4,14 +4,15 @@ import time
|
||||
|
||||
import aprslib
|
||||
from aprslib.exceptions import LoginError
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import config as aprsd_config
|
||||
from aprsd import exception
|
||||
from aprsd.clients import aprsis, kiss
|
||||
from aprsd.packets import core, packet_list
|
||||
from aprsd.utils import trace
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
TRANSPORT_APRSIS = "aprsis"
|
||||
TRANSPORT_TCPKISS = "tcpkiss"
|
||||
@ -28,7 +29,6 @@ class Client:
|
||||
|
||||
_instance = None
|
||||
_client = None
|
||||
config = None
|
||||
|
||||
connected = False
|
||||
server_string = None
|
||||
@ -41,11 +41,6 @@ class Client:
|
||||
# Put any initialization here.
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, config=None):
|
||||
"""Initialize the object instance."""
|
||||
if config:
|
||||
self.config = config
|
||||
|
||||
def set_filter(self, filter):
|
||||
self.filter = filter
|
||||
if self._client:
|
||||
@ -74,12 +69,12 @@ class Client:
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def is_enabled(config):
|
||||
def is_enabled():
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def transport(config):
|
||||
def transport():
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
@ -90,26 +85,38 @@ class Client:
|
||||
class APRSISClient(Client):
|
||||
|
||||
@staticmethod
|
||||
def is_enabled(config):
|
||||
def is_enabled():
|
||||
# Defaults to True if the enabled flag is non existent
|
||||
try:
|
||||
return config["aprs"].get("enabled", True)
|
||||
return CONF.aprs_network.enabled
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_configured(config):
|
||||
if APRSISClient.is_enabled(config):
|
||||
def is_configured():
|
||||
if APRSISClient.is_enabled():
|
||||
# Ensure that the config vars are correctly set
|
||||
config.check_option("aprs.login")
|
||||
config.check_option("aprs.password")
|
||||
config.check_option("aprs.host")
|
||||
return True
|
||||
if not CONF.aprs_network.login:
|
||||
LOG.error("Config aprs_network.login not set.")
|
||||
raise exception.MissingConfigOptionException(
|
||||
"aprs_network.login is not set.",
|
||||
)
|
||||
if not CONF.aprs_network.password:
|
||||
LOG.error("Config aprs_network.password not set.")
|
||||
raise exception.MissingConfigOptionException(
|
||||
"aprs_network.password is not set.",
|
||||
)
|
||||
if not CONF.aprs_network.host:
|
||||
LOG.error("Config aprs_network.host not set.")
|
||||
raise exception.MissingConfigOptionException(
|
||||
"aprs_network.host is not set.",
|
||||
)
|
||||
|
||||
return True
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def transport(config):
|
||||
def transport():
|
||||
return TRANSPORT_APRSIS
|
||||
|
||||
def decode_packet(self, *args, **kwargs):
|
||||
@ -118,10 +125,10 @@ class APRSISClient(Client):
|
||||
|
||||
@trace.trace
|
||||
def setup_connection(self):
|
||||
user = self.config["aprs"]["login"]
|
||||
password = self.config["aprs"]["password"]
|
||||
host = self.config["aprs"].get("host", "rotate.aprs.net")
|
||||
port = self.config["aprs"].get("port", 14580)
|
||||
user = CONF.aprs_network.login
|
||||
password = CONF.aprs_network.password
|
||||
host = CONF.aprs_network.host
|
||||
port = CONF.aprs_network.port
|
||||
connected = False
|
||||
backoff = 1
|
||||
aprs_client = None
|
||||
@ -151,45 +158,43 @@ class APRSISClient(Client):
|
||||
class KISSClient(Client):
|
||||
|
||||
@staticmethod
|
||||
def is_enabled(config):
|
||||
def is_enabled():
|
||||
"""Return if tcp or serial KISS is enabled."""
|
||||
if "kiss" not in config:
|
||||
return False
|
||||
|
||||
if config.get("kiss.serial.enabled", default=False):
|
||||
if CONF.kiss_serial.enabled:
|
||||
return True
|
||||
|
||||
if config.get("kiss.tcp.enabled", default=False):
|
||||
if CONF.kiss_tcp.enabled:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_configured(config):
|
||||
def is_configured():
|
||||
# Ensure that the config vars are correctly set
|
||||
if KISSClient.is_enabled(config):
|
||||
config.check_option(
|
||||
"aprsd.callsign",
|
||||
default_fail=aprsd_config.DEFAULT_CONFIG_DICT["aprsd"]["callsign"],
|
||||
)
|
||||
transport = KISSClient.transport(config)
|
||||
if KISSClient.is_enabled():
|
||||
transport = KISSClient.transport()
|
||||
if transport == TRANSPORT_SERIALKISS:
|
||||
config.check_option("kiss.serial")
|
||||
config.check_option("kiss.serial.device")
|
||||
if not CONF.kiss_serial.device:
|
||||
LOG.error("KISS serial enabled, but no device is set.")
|
||||
raise exception.MissingConfigOptionException(
|
||||
"kiss_serial.device is not set.",
|
||||
)
|
||||
elif transport == TRANSPORT_TCPKISS:
|
||||
config.check_option("kiss.tcp")
|
||||
config.check_option("kiss.tcp.host")
|
||||
config.check_option("kiss.tcp.port")
|
||||
if not CONF.kiss_tcp.host:
|
||||
LOG.error("KISS TCP enabled, but no host is set.")
|
||||
raise exception.MissingConfigOptionException(
|
||||
"kiss_tcp.host is not set.",
|
||||
)
|
||||
|
||||
return True
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def transport(config):
|
||||
if config.get("kiss.serial.enabled", default=False):
|
||||
def transport():
|
||||
if CONF.kiss_serial.enabled:
|
||||
return TRANSPORT_SERIALKISS
|
||||
|
||||
if config.get("kiss.tcp.enabled", default=False):
|
||||
if CONF.kiss_tcp.enabled:
|
||||
return TRANSPORT_TCPKISS
|
||||
|
||||
def decode_packet(self, *args, **kwargs):
|
||||
@ -208,7 +213,7 @@ class KISSClient(Client):
|
||||
|
||||
@trace.trace
|
||||
def setup_connection(self):
|
||||
client = kiss.KISS3Client(self.config)
|
||||
client = kiss.KISS3Client()
|
||||
return client
|
||||
|
||||
|
||||
@ -222,8 +227,7 @@ class ClientFactory:
|
||||
# Put any initialization here.
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
def __init__(self):
|
||||
self._builders = {}
|
||||
|
||||
def register(self, key, builder):
|
||||
@ -231,23 +235,23 @@ class ClientFactory:
|
||||
|
||||
def create(self, key=None):
|
||||
if not key:
|
||||
if APRSISClient.is_enabled(self.config):
|
||||
if APRSISClient.is_enabled():
|
||||
key = TRANSPORT_APRSIS
|
||||
elif KISSClient.is_enabled(self.config):
|
||||
key = KISSClient.transport(self.config)
|
||||
elif KISSClient.is_enabled():
|
||||
key = KISSClient.transport()
|
||||
|
||||
LOG.debug(f"GET client '{key}'")
|
||||
builder = self._builders.get(key)
|
||||
if not builder:
|
||||
raise ValueError(key)
|
||||
return builder(self.config)
|
||||
return builder()
|
||||
|
||||
def is_client_enabled(self):
|
||||
"""Make sure at least one client is enabled."""
|
||||
enabled = False
|
||||
for key in self._builders.keys():
|
||||
try:
|
||||
enabled |= self._builders[key].is_enabled(self.config)
|
||||
enabled |= self._builders[key].is_enabled()
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@ -257,7 +261,7 @@ class ClientFactory:
|
||||
enabled = False
|
||||
for key in self._builders.keys():
|
||||
try:
|
||||
enabled |= self._builders[key].is_configured(self.config)
|
||||
enabled |= self._builders[key].is_configured()
|
||||
except KeyError:
|
||||
pass
|
||||
except exception.MissingConfigOptionException as ex:
|
||||
@ -270,11 +274,11 @@ class ClientFactory:
|
||||
return enabled
|
||||
|
||||
@staticmethod
|
||||
def setup(config):
|
||||
def setup():
|
||||
"""Create and register all possible client objects."""
|
||||
global factory
|
||||
|
||||
factory = ClientFactory(config)
|
||||
factory = ClientFactory()
|
||||
factory.register(TRANSPORT_APRSIS, APRSISClient)
|
||||
factory.register(TRANSPORT_TCPKISS, KISSClient)
|
||||
factory.register(TRANSPORT_SERIALKISS, KISSClient)
|
||||
|
@ -10,11 +10,12 @@ import sys
|
||||
import time
|
||||
|
||||
import click
|
||||
from oslo_config import cfg
|
||||
from rich.console import Console
|
||||
|
||||
# local imports here
|
||||
import aprsd
|
||||
from aprsd import cli_helper, client, packets, stats, threads, utils
|
||||
from aprsd import cli_helper, client, packets, stats, threads
|
||||
from aprsd.aprsd import cli
|
||||
from aprsd.threads import rx
|
||||
|
||||
@ -22,6 +23,7 @@ from aprsd.threads import rx
|
||||
# setup the global logger
|
||||
# logging.basicConfig(level=logging.DEBUG) # level=10
|
||||
LOG = logging.getLogger("APRSD")
|
||||
CONF = cfg.CONF
|
||||
console = Console()
|
||||
|
||||
|
||||
@ -38,8 +40,8 @@ def signal_handler(sig, frame):
|
||||
|
||||
|
||||
class APRSDListenThread(rx.APRSDRXThread):
|
||||
def __init__(self, config, packet_queue, packet_filter=None):
|
||||
super().__init__(config, packet_queue)
|
||||
def __init__(self, packet_queue, packet_filter=None):
|
||||
super().__init__(packet_queue)
|
||||
self.packet_filter = packet_filter
|
||||
|
||||
def process_packet(self, *args, **kwargs):
|
||||
@ -118,7 +120,6 @@ def listen(
|
||||
"""
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
config = ctx.obj["config"]
|
||||
|
||||
if not aprs_login:
|
||||
click.echo(ctx.get_help())
|
||||
@ -132,27 +133,19 @@ def listen(
|
||||
ctx.fail("Must set --aprs-password or APRS_PASSWORD")
|
||||
ctx.exit()
|
||||
|
||||
config["aprs"]["login"] = aprs_login
|
||||
config["aprs"]["password"] = aprs_password
|
||||
# CONF.aprs_network.login = aprs_login
|
||||
# config["aprs"]["password"] = aprs_password
|
||||
|
||||
LOG.info(f"APRSD Listen Started version: {aprsd.__version__}")
|
||||
|
||||
flat_config = utils.flatten_dict(config)
|
||||
LOG.info("Using CONFIG values:")
|
||||
for x in flat_config:
|
||||
if "password" in x or "aprsd.web.users.admin" in x:
|
||||
LOG.info(f"{x} = XXXXXXXXXXXXXXXXXXX")
|
||||
else:
|
||||
LOG.info(f"{x} = {flat_config[x]}")
|
||||
|
||||
stats.APRSDStats(config)
|
||||
CONF.log_opt_values(LOG, logging.DEBUG)
|
||||
|
||||
# Try and load saved MsgTrack list
|
||||
LOG.debug("Loading saved MsgTrack object.")
|
||||
|
||||
# Initialize the client factory and create
|
||||
# The correct client object ready for use
|
||||
client.ClientFactory.setup(config)
|
||||
client.ClientFactory.setup()
|
||||
# Make sure we have 1 client transport enabled
|
||||
if not client.factory.is_client_enabled():
|
||||
LOG.error("No Clients are enabled in config.")
|
||||
@ -166,12 +159,11 @@ def listen(
|
||||
LOG.debug(f"Filter by '{filter}'")
|
||||
aprs_client.set_filter(filter)
|
||||
|
||||
keepalive = threads.KeepAliveThread(config=config)
|
||||
keepalive = threads.KeepAliveThread()
|
||||
keepalive.start()
|
||||
|
||||
LOG.debug("Create APRSDListenThread")
|
||||
listen_thread = APRSDListenThread(
|
||||
config=config,
|
||||
packet_queue=threads.packet_queue,
|
||||
packet_filter=packet_filter,
|
||||
)
|
||||
|
@ -3,16 +3,16 @@ import signal
|
||||
import sys
|
||||
|
||||
import click
|
||||
from oslo_config import cfg
|
||||
|
||||
import aprsd
|
||||
from aprsd import (
|
||||
cli_helper, client, flask, packets, plugin, stats, threads, utils,
|
||||
)
|
||||
from aprsd import aprsd as aprsd_main
|
||||
from aprsd import cli_helper, client, flask, packets, plugin, threads, utils
|
||||
from aprsd.aprsd import cli
|
||||
from aprsd.threads import rx
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -32,10 +32,8 @@ LOG = logging.getLogger("APRSD")
|
||||
@cli_helper.process_standard_options
|
||||
def server(ctx, flush):
|
||||
"""Start the aprsd server gateway process."""
|
||||
ctx.obj["config_file"]
|
||||
loglevel = ctx.obj["loglevel"]
|
||||
quiet = ctx.obj["quiet"]
|
||||
config = ctx.obj["config"]
|
||||
|
||||
signal.signal(signal.SIGINT, aprsd_main.signal_handler)
|
||||
signal.signal(signal.SIGTERM, aprsd_main.signal_handler)
|
||||
@ -50,19 +48,11 @@ def server(ctx, flush):
|
||||
LOG.info(msg)
|
||||
LOG.info(f"APRSD Started version: {aprsd.__version__}")
|
||||
|
||||
flat_config = utils.flatten_dict(config)
|
||||
LOG.info("Using CONFIG values:")
|
||||
for x in flat_config:
|
||||
if "password" in x or "aprsd.web.users.admin" in x:
|
||||
LOG.info(f"{x} = XXXXXXXXXXXXXXXXXXX")
|
||||
else:
|
||||
LOG.info(f"{x} = {flat_config[x]}")
|
||||
|
||||
stats.APRSDStats(config)
|
||||
CONF.log_opt_values(LOG, logging.DEBUG)
|
||||
|
||||
# Initialize the client factory and create
|
||||
# The correct client object ready for use
|
||||
client.ClientFactory.setup(config)
|
||||
client.ClientFactory.setup()
|
||||
# Make sure we have 1 client transport enabled
|
||||
if not client.factory.is_client_enabled():
|
||||
LOG.error("No Clients are enabled in config.")
|
||||
@ -77,30 +67,28 @@ def server(ctx, flush):
|
||||
client.factory.create().client
|
||||
|
||||
# Now load the msgTrack from disk if any
|
||||
packets.PacketList(config=config)
|
||||
packets.PacketList()
|
||||
if flush:
|
||||
LOG.debug("Deleting saved MsgTrack.")
|
||||
packets.PacketTrack(config=config).flush()
|
||||
packets.WatchList(config=config)
|
||||
packets.SeenList(config=config)
|
||||
packets.PacketTrack().flush()
|
||||
packets.WatchList()
|
||||
packets.SeenList()
|
||||
else:
|
||||
# Try and load saved MsgTrack list
|
||||
LOG.debug("Loading saved MsgTrack object.")
|
||||
packets.PacketTrack(config=config).load()
|
||||
packets.WatchList(config=config).load()
|
||||
packets.SeenList(config=config).load()
|
||||
packets.PacketTrack().load()
|
||||
packets.WatchList().load()
|
||||
packets.SeenList().load()
|
||||
|
||||
# Create the initial PM singleton and Register plugins
|
||||
LOG.info("Loading Plugin Manager and registering plugins")
|
||||
plugin_manager = plugin.PluginManager(config)
|
||||
plugin_manager = plugin.PluginManager()
|
||||
plugin_manager.setup_plugins()
|
||||
|
||||
rx_thread = rx.APRSDPluginRXThread(
|
||||
packet_queue=threads.packet_queue,
|
||||
config=config,
|
||||
)
|
||||
process_thread = rx.APRSDPluginProcessPacketThread(
|
||||
config=config,
|
||||
packet_queue=threads.packet_queue,
|
||||
)
|
||||
rx_thread.start()
|
||||
@ -108,19 +96,19 @@ def server(ctx, flush):
|
||||
|
||||
packets.PacketTrack().restart()
|
||||
|
||||
keepalive = threads.KeepAliveThread(config=config)
|
||||
keepalive = threads.KeepAliveThread()
|
||||
keepalive.start()
|
||||
|
||||
web_enabled = config.get("aprsd.web.enabled", default=False)
|
||||
web_enabled = CONF.admin.web_enabled
|
||||
|
||||
if web_enabled:
|
||||
aprsd_main.flask_enabled = True
|
||||
(socketio, app) = flask.init_flask(config, loglevel, quiet)
|
||||
(socketio, app) = flask.init_flask(loglevel, quiet)
|
||||
socketio.run(
|
||||
app,
|
||||
allow_unsafe_werkzeug=True,
|
||||
host=config["aprsd"]["web"]["host"],
|
||||
port=config["aprsd"]["web"]["port"],
|
||||
host=CONF.admin.web_ip,
|
||||
port=CONF.admin.web_port,
|
||||
)
|
||||
|
||||
# If there are items in the msgTracker, then save them
|
||||
|
@ -4,10 +4,13 @@ from logging.handlers import RotatingFileHandler
|
||||
import queue
|
||||
import sys
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import config as aprsd_config
|
||||
from aprsd.logging import rich as aprsd_logging
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
logging_queue = queue.Queue()
|
||||
|
||||
@ -15,13 +18,15 @@ logging_queue = queue.Queue()
|
||||
# Setup the logging faciility
|
||||
# to disable logging to stdout, but still log to file
|
||||
# use the --quiet option on the cmdln
|
||||
def setup_logging(config, loglevel, quiet):
|
||||
def setup_logging(loglevel, quiet):
|
||||
log_level = aprsd_config.LOG_LEVELS[loglevel]
|
||||
LOG.setLevel(log_level)
|
||||
date_format = config["aprsd"].get("dateformat", aprsd_config.DEFAULT_DATE_FORMAT)
|
||||
date_format = CONF.logging.get("date_format", aprsd_config.DEFAULT_DATE_FORMAT)
|
||||
rh = None
|
||||
fh = None
|
||||
|
||||
rich_logging = False
|
||||
if config["aprsd"].get("rich_logging", False) and not quiet:
|
||||
if CONF.logging.get("rich_logging", False) and not quiet:
|
||||
log_format = "%(message)s"
|
||||
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
||||
rh = aprsd_logging.APRSDRichHandler(
|
||||
@ -32,8 +37,8 @@ def setup_logging(config, loglevel, quiet):
|
||||
LOG.addHandler(rh)
|
||||
rich_logging = True
|
||||
|
||||
log_file = config["aprsd"].get("logfile", None)
|
||||
log_format = config["aprsd"].get("logformat", aprsd_config.DEFAULT_LOG_FORMAT)
|
||||
log_file = CONF.logging.logfile
|
||||
log_format = CONF.logging.logformat
|
||||
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
||||
|
||||
if log_file:
|
||||
@ -42,12 +47,16 @@ def setup_logging(config, loglevel, quiet):
|
||||
LOG.addHandler(fh)
|
||||
|
||||
imap_logger = None
|
||||
if config.get("aprsd.email.enabled", default=False) and config.get("aprsd.email.imap.debug", default=False):
|
||||
if CONF.email_plugin.enabled and CONF.email_plugin.debug:
|
||||
imap_logger = logging.getLogger("imapclient.imaplib")
|
||||
imap_logger.setLevel(log_level)
|
||||
imap_logger.addHandler(fh)
|
||||
if rh:
|
||||
imap_logger.addHandler(rh)
|
||||
if fh:
|
||||
imap_logger.addHandler(fh)
|
||||
|
||||
if config.get("aprsd.web.enabled", default=False):
|
||||
|
||||
if CONF.admin.get("web_enabled", default=False):
|
||||
qh = logging.handlers.QueueHandler(logging_queue)
|
||||
q_log_formatter = logging.Formatter(
|
||||
fmt=aprsd_config.QUEUE_LOG_FORMAT,
|
||||
|
@ -1,12 +1,14 @@
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from oslo_config import cfg
|
||||
import wrapt
|
||||
|
||||
from aprsd import stats, utils
|
||||
from aprsd.packets import seen_list
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -15,7 +17,6 @@ class PacketList:
|
||||
|
||||
_instance = None
|
||||
lock = threading.Lock()
|
||||
config = None
|
||||
|
||||
packet_list: utils.RingBuffer = utils.RingBuffer(1000)
|
||||
|
||||
@ -25,17 +26,8 @@ class PacketList:
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
if "config" in kwargs:
|
||||
cls._instance.config = kwargs["config"]
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, config=None):
|
||||
if config:
|
||||
self.config = config
|
||||
|
||||
def _is_initialized(self):
|
||||
return self.config is not None
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def __iter__(self):
|
||||
return iter(self.packet_list)
|
||||
|
@ -2,11 +2,13 @@ import datetime
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from oslo_config import cfg
|
||||
import wrapt
|
||||
|
||||
from aprsd.utils import objectstore
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -16,21 +18,14 @@ class SeenList(objectstore.ObjectStoreMixin):
|
||||
_instance = None
|
||||
lock = threading.Lock()
|
||||
data: dict = {}
|
||||
config = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
if "config" in kwargs:
|
||||
if "config" in kwargs:
|
||||
cls._instance.config = kwargs["config"]
|
||||
cls._instance._init_store()
|
||||
cls._instance._init_store()
|
||||
cls._instance.data = {}
|
||||
return cls._instance
|
||||
|
||||
def is_initialized(self):
|
||||
return self.config is not None
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def update_seen(self, packet):
|
||||
callsign = None
|
||||
|
@ -1,12 +1,16 @@
|
||||
import datetime
|
||||
import threading
|
||||
|
||||
from oslo_config import cfg
|
||||
import wrapt
|
||||
|
||||
from aprsd.threads import tx
|
||||
from aprsd.utils import objectstore
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class PacketTrack(objectstore.ObjectStoreMixin):
|
||||
"""Class to keep track of outstanding text messages.
|
||||
|
||||
@ -23,7 +27,6 @@ class PacketTrack(objectstore.ObjectStoreMixin):
|
||||
_instance = None
|
||||
_start_time = None
|
||||
lock = threading.Lock()
|
||||
config = None
|
||||
|
||||
data: dict = {}
|
||||
total_tracked: int = 0
|
||||
@ -32,14 +35,9 @@ class PacketTrack(objectstore.ObjectStoreMixin):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
cls._instance._start_time = datetime.datetime.now()
|
||||
if "config" in kwargs:
|
||||
cls._instance.config = kwargs["config"]
|
||||
cls._instance._init_store()
|
||||
return cls._instance
|
||||
|
||||
def is_initialized(self):
|
||||
return self.config is not None
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def __getitem__(self, name):
|
||||
return self.data[name]
|
||||
|
@ -2,12 +2,14 @@ import datetime
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from oslo_config import cfg
|
||||
import wrapt
|
||||
|
||||
from aprsd import utils
|
||||
from aprsd.utils import objectstore
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -17,24 +19,22 @@ class WatchList(objectstore.ObjectStoreMixin):
|
||||
_instance = None
|
||||
lock = threading.Lock()
|
||||
data = {}
|
||||
config = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
if "config" in kwargs:
|
||||
cls._instance.config = kwargs["config"]
|
||||
cls._instance._init_store()
|
||||
cls._instance._init_store()
|
||||
cls._instance.data = {}
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, config=None):
|
||||
if config:
|
||||
self.config = config
|
||||
ring_size = CONF.watch_list.packet_keep_count
|
||||
|
||||
ring_size = config["aprsd"]["watch_list"].get("packet_keep_count", 10)
|
||||
if not self.is_enabled():
|
||||
LOG.info("Watch List is disabled.")
|
||||
|
||||
for callsign in config["aprsd"]["watch_list"].get("callsigns", []):
|
||||
if CONF.watch_list.callsigns:
|
||||
for callsign in CONF.watch_list.callsigns:
|
||||
call = callsign.replace("*", "")
|
||||
# FIXME(waboring) - we should fetch the last time we saw
|
||||
# a beacon from a callsign or some other mechanism to find
|
||||
@ -47,14 +47,8 @@ class WatchList(objectstore.ObjectStoreMixin):
|
||||
),
|
||||
}
|
||||
|
||||
def is_initialized(self):
|
||||
return self.config is not None
|
||||
|
||||
def is_enabled(self):
|
||||
if self.config and "watch_list" in self.config["aprsd"]:
|
||||
return self.config["aprsd"]["watch_list"].get("enabled", False)
|
||||
else:
|
||||
return False
|
||||
return CONF.watch_list.enabled
|
||||
|
||||
def callsign_in_watchlist(self, callsign):
|
||||
return callsign in self.data
|
||||
@ -78,9 +72,8 @@ class WatchList(objectstore.ObjectStoreMixin):
|
||||
return str(now - self.last_seen(callsign))
|
||||
|
||||
def max_delta(self, seconds=None):
|
||||
watch_list_conf = self.config["aprsd"]["watch_list"]
|
||||
if not seconds:
|
||||
seconds = watch_list_conf["alert_time_seconds"]
|
||||
seconds = CONF.watch_list.alert_time_seconds
|
||||
max_timeout = {"seconds": seconds}
|
||||
return datetime.timedelta(**max_timeout)
|
||||
|
||||
|
@ -7,6 +7,7 @@ import re
|
||||
import textwrap
|
||||
import threading
|
||||
|
||||
from oslo_config import cfg
|
||||
import pluggy
|
||||
|
||||
import aprsd
|
||||
@ -15,6 +16,7 @@ from aprsd.packets import watch_list
|
||||
|
||||
|
||||
# setup the global logger
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
CORE_MESSAGE_PLUGINS = [
|
||||
@ -211,6 +213,10 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
||||
|
||||
@hookimpl
|
||||
def filter(self, packet: packets.core.MessagePacket):
|
||||
if not self.enabled:
|
||||
LOG.info(f"{self.__class__.__name__} is not enabled.")
|
||||
return None
|
||||
|
||||
result = None
|
||||
|
||||
message = packet.get("message_text", None)
|
||||
@ -220,7 +226,7 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
||||
# Only process messages destined for us
|
||||
# and is an APRS message format and has a message.
|
||||
if (
|
||||
tocall == self.config["aprs"]["login"]
|
||||
tocall == CONF.callsign
|
||||
and msg_format == "message"
|
||||
and message
|
||||
):
|
||||
@ -249,12 +255,11 @@ class APRSFIKEYMixin:
|
||||
"""Mixin class to enable checking the existence of the aprs.fi apiKey."""
|
||||
|
||||
def ensure_aprs_fi_key(self):
|
||||
try:
|
||||
self.config.check_option(["services", "aprs.fi", "apiKey"])
|
||||
self.enabled = True
|
||||
except Exception as ex:
|
||||
LOG.error(f"Failed to find config aprs.fi:apikey {ex}")
|
||||
if not CONF.aprs_fi.apiKey:
|
||||
LOG.error("Config aprs_fi.apiKey is not set")
|
||||
self.enabled = False
|
||||
else:
|
||||
self.enabled = True
|
||||
|
||||
|
||||
class HelpPlugin(APRSDRegexCommandPluginBase):
|
||||
@ -410,21 +415,28 @@ class PluginManager:
|
||||
)
|
||||
if plugin_obj:
|
||||
if isinstance(plugin_obj, APRSDWatchListPluginBase):
|
||||
LOG.info(
|
||||
"Registering WatchList plugin '{}'({})".format(
|
||||
plugin_name,
|
||||
plugin_obj.version,
|
||||
),
|
||||
)
|
||||
self._watchlist_pm.register(plugin_obj)
|
||||
if plugin_obj.enabled:
|
||||
LOG.info(
|
||||
"Registering WatchList plugin '{}'({})".format(
|
||||
plugin_name,
|
||||
plugin_obj.version,
|
||||
),
|
||||
)
|
||||
self._watchlist_pm.register(plugin_obj)
|
||||
else:
|
||||
LOG.warning(f"Plugin {plugin_obj.__class__.__name__} is disabled")
|
||||
else:
|
||||
LOG.info(
|
||||
"Registering plugin '{}'({})".format(
|
||||
plugin_name,
|
||||
plugin_obj.version,
|
||||
),
|
||||
)
|
||||
self._pluggy_pm.register(plugin_obj)
|
||||
if plugin_obj.enabled:
|
||||
LOG.info(
|
||||
"Registering plugin '{}'({}) -- {}".format(
|
||||
plugin_name,
|
||||
plugin_obj.version,
|
||||
plugin_obj.command_regex,
|
||||
),
|
||||
)
|
||||
self._pluggy_pm.register(plugin_obj)
|
||||
else:
|
||||
LOG.warning(f"Plugin {plugin_obj.__class__.__name__} is disabled")
|
||||
except Exception as ex:
|
||||
LOG.error(f"Couldn't load plugin '{plugin_name}'")
|
||||
LOG.exception(ex)
|
||||
@ -443,7 +455,7 @@ class PluginManager:
|
||||
_help = HelpPlugin(self.config)
|
||||
self._pluggy_pm.register(_help)
|
||||
|
||||
enabled_plugins = self.config["aprsd"].get("enabled_plugins", None)
|
||||
enabled_plugins = CONF.enabled_plugins
|
||||
if enabled_plugins:
|
||||
for p_name in enabled_plugins:
|
||||
self._load_plugin(p_name)
|
||||
|
@ -26,13 +26,18 @@ def get_aprs_fi(api_key, callsign):
|
||||
|
||||
def get_weather_gov_for_gps(lat, lon):
|
||||
LOG.debug(f"Fetch station at {lat}, {lon}")
|
||||
headers = requests.utils.default_headers()
|
||||
headers.update(
|
||||
{"User-Agent": "(aprsd, waboring@hemna.com)"},
|
||||
)
|
||||
try:
|
||||
url2 = (
|
||||
"https://forecast.weather.gov/MapClick.php?lat=%s"
|
||||
"&lon=%s&FcstType=json" % (lat, lon)
|
||||
#"https://forecast.weather.gov/MapClick.php?lat=%s"
|
||||
#"&lon=%s&FcstType=json" % (lat, lon)
|
||||
f"https://api.weather.gov/points/{lat},{lon}"
|
||||
)
|
||||
LOG.debug(f"Fetching weather '{url2}'")
|
||||
response = requests.get(url2)
|
||||
response = requests.get(url2, headers=headers)
|
||||
except Exception as e:
|
||||
LOG.error(e)
|
||||
raise Exception("Failed to get weather")
|
||||
|
@ -9,13 +9,16 @@ import threading
|
||||
import time
|
||||
|
||||
import imapclient
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import packets, plugin, stats, threads
|
||||
from aprsd.threads import tx
|
||||
from aprsd.utils import trace
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
shortcuts_dict = None
|
||||
|
||||
|
||||
class EmailInfo:
|
||||
@ -71,18 +74,18 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
|
||||
def setup(self):
|
||||
"""Ensure that email is enabled and start the thread."""
|
||||
|
||||
email_enabled = self.config["aprsd"]["email"].get("enabled", False)
|
||||
if email_enabled:
|
||||
if CONF.email_plugin.enabled:
|
||||
self.enabled = True
|
||||
shortcuts = _build_shortcuts_dict()
|
||||
LOG.info(f"Email shortcuts {shortcuts}")
|
||||
|
||||
else:
|
||||
LOG.info("Email services not enabled.")
|
||||
self.enabled = False
|
||||
|
||||
def create_threads(self):
|
||||
if self.enabled:
|
||||
return APRSDEmailThread(
|
||||
config=self.config,
|
||||
)
|
||||
return APRSDEmailThread()
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet: packets.MessagePacket):
|
||||
@ -97,18 +100,18 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
ack = packet.get("msgNo", "0")
|
||||
|
||||
reply = None
|
||||
if not self.config["aprsd"]["email"].get("enabled", False):
|
||||
if not CONF.email_plugin.enabled:
|
||||
LOG.debug("Email is not enabled in config file ignoring.")
|
||||
return "Email not enabled."
|
||||
|
||||
searchstring = "^" + self.config["ham"]["callsign"] + ".*"
|
||||
searchstring = "^" + CONF.email_plugin.callsign + ".*"
|
||||
# only I can do email
|
||||
if re.search(searchstring, fromcall):
|
||||
# digits only, first one is number of emails to resend
|
||||
r = re.search("^-([0-9])[0-9]*$", message)
|
||||
if r is not None:
|
||||
LOG.debug("RESEND EMAIL")
|
||||
resend_email(self.config, r.group(1), fromcall)
|
||||
resend_email(r.group(1), fromcall)
|
||||
reply = packets.NULL_MESSAGE
|
||||
# -user@address.com body of email
|
||||
elif re.search(r"^-([A-Za-z0-9_\-\.@]+) (.*)", message):
|
||||
@ -118,7 +121,7 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
to_addr = a.group(1)
|
||||
content = a.group(2)
|
||||
|
||||
email_address = get_email_from_shortcut(self.config, to_addr)
|
||||
email_address = get_email_from_shortcut(to_addr)
|
||||
if not email_address:
|
||||
reply = "Bad email address"
|
||||
return reply
|
||||
@ -128,7 +131,7 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
content = (
|
||||
"Click for my location: http://aprs.fi/{}" ""
|
||||
).format(
|
||||
self.config["ham"]["callsign"],
|
||||
CONF.email_plugin.callsign,
|
||||
)
|
||||
too_soon = 0
|
||||
now = time.time()
|
||||
@ -141,7 +144,7 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
too_soon = 1
|
||||
if not too_soon or ack == 0:
|
||||
LOG.info(f"Send email '{content}'")
|
||||
send_result = send_email(self.config, to_addr, content)
|
||||
send_result = send_email(to_addr, content)
|
||||
reply = packets.NULL_MESSAGE
|
||||
if send_result != 0:
|
||||
reply = f"-{to_addr} failed"
|
||||
@ -169,9 +172,9 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
return reply
|
||||
|
||||
|
||||
def _imap_connect(config):
|
||||
imap_port = config["aprsd"]["email"]["imap"].get("port", 143)
|
||||
use_ssl = config["aprsd"]["email"]["imap"].get("use_ssl", False)
|
||||
def _imap_connect():
|
||||
imap_port = CONF.email_plugin.imap_port
|
||||
use_ssl = CONF.email_plugin.imap_use_ssl
|
||||
# host = CONFIG["aprsd"]["email"]["imap"]["host"]
|
||||
# msg = "{}{}:{}".format("TLS " if use_ssl else "", host, imap_port)
|
||||
# LOG.debug("Connect to IMAP host {} with user '{}'".
|
||||
@ -179,7 +182,7 @@ def _imap_connect(config):
|
||||
|
||||
try:
|
||||
server = imapclient.IMAPClient(
|
||||
config["aprsd"]["email"]["imap"]["host"],
|
||||
CONF.email_plugin.imap_host,
|
||||
port=imap_port,
|
||||
use_uid=True,
|
||||
ssl=use_ssl,
|
||||
@ -191,8 +194,8 @@ def _imap_connect(config):
|
||||
|
||||
try:
|
||||
server.login(
|
||||
config["aprsd"]["email"]["imap"]["login"],
|
||||
config["aprsd"]["email"]["imap"]["password"],
|
||||
CONF.email_plugin.imap_login,
|
||||
CONF.email_plugin.imap_password,
|
||||
)
|
||||
except (imaplib.IMAP4.error, Exception) as e:
|
||||
msg = getattr(e, "message", repr(e))
|
||||
@ -208,15 +211,15 @@ def _imap_connect(config):
|
||||
return server
|
||||
|
||||
|
||||
def _smtp_connect(config):
|
||||
host = config["aprsd"]["email"]["smtp"]["host"]
|
||||
smtp_port = config["aprsd"]["email"]["smtp"]["port"]
|
||||
use_ssl = config["aprsd"]["email"]["smtp"].get("use_ssl", False)
|
||||
def _smtp_connect():
|
||||
host = CONF.email_plugin.smtp_host
|
||||
smtp_port = CONF.email_plugin.smtp_port
|
||||
use_ssl = CONF.email_plugin.smtp_use_ssl
|
||||
msg = "{}{}:{}".format("SSL " if use_ssl else "", host, smtp_port)
|
||||
LOG.debug(
|
||||
"Connect to SMTP host {} with user '{}'".format(
|
||||
msg,
|
||||
config["aprsd"]["email"]["imap"]["login"],
|
||||
CONF.email_plugin.smtp_login,
|
||||
),
|
||||
)
|
||||
|
||||
@ -239,15 +242,15 @@ def _smtp_connect(config):
|
||||
|
||||
LOG.debug(f"Connected to smtp host {msg}")
|
||||
|
||||
debug = config["aprsd"]["email"]["smtp"].get("debug", False)
|
||||
debug = CONF.email_plugin.debug
|
||||
if debug:
|
||||
server.set_debuglevel(5)
|
||||
server.sendmail = trace.trace(server.sendmail)
|
||||
|
||||
try:
|
||||
server.login(
|
||||
config["aprsd"]["email"]["smtp"]["login"],
|
||||
config["aprsd"]["email"]["smtp"]["password"],
|
||||
CONF.email_plugin.smtp_login,
|
||||
CONF.email_plugin.smtp_password,
|
||||
)
|
||||
except Exception:
|
||||
LOG.error("Couldn't connect to SMTP Server")
|
||||
@ -257,22 +260,40 @@ def _smtp_connect(config):
|
||||
return server
|
||||
|
||||
|
||||
def get_email_from_shortcut(config, addr):
|
||||
if config["aprsd"]["email"].get("shortcuts", False):
|
||||
return config["aprsd"]["email"]["shortcuts"].get(addr, addr)
|
||||
def _build_shortcuts_dict():
|
||||
global shortcuts_dict
|
||||
if not shortcuts_dict:
|
||||
if CONF.email_plugin.email_shortcuts:
|
||||
shortcuts_dict = {}
|
||||
tmp = CONF.email_plugin.email_shortcuts
|
||||
for combo in tmp:
|
||||
entry = combo.split("=")
|
||||
shortcuts_dict[entry[0]] = entry[1]
|
||||
else:
|
||||
shortcuts_dict = {}
|
||||
|
||||
LOG.info(f"Shortcuts Dict {shortcuts_dict}")
|
||||
return shortcuts_dict
|
||||
|
||||
|
||||
def get_email_from_shortcut(addr):
|
||||
if CONF.email_plugin.email_shortcuts:
|
||||
shortcuts = _build_shortcuts_dict()
|
||||
LOG.info(f"Shortcut lookup {addr} returns {shortcuts.get(addr, addr)}")
|
||||
return shortcuts.get(addr, addr)
|
||||
else:
|
||||
return addr
|
||||
|
||||
|
||||
def validate_email_config(config, disable_validation=False):
|
||||
def validate_email_config(disable_validation=False):
|
||||
"""function to simply ensure we can connect to email services.
|
||||
|
||||
This helps with failing early during startup.
|
||||
"""
|
||||
LOG.info("Checking IMAP configuration")
|
||||
imap_server = _imap_connect(config)
|
||||
imap_server = _imap_connect()
|
||||
LOG.info("Checking SMTP configuration")
|
||||
smtp_server = _smtp_connect(config)
|
||||
smtp_server = _smtp_connect()
|
||||
|
||||
if imap_server and smtp_server:
|
||||
return True
|
||||
@ -376,16 +397,16 @@ def parse_email(msgid, data, server):
|
||||
|
||||
|
||||
@trace.trace
|
||||
def send_email(config, to_addr, content):
|
||||
shortcuts = config["aprsd"]["email"]["shortcuts"]
|
||||
email_address = get_email_from_shortcut(config, to_addr)
|
||||
def send_email(to_addr, content):
|
||||
shortcuts = _build_shortcuts_dict()
|
||||
email_address = get_email_from_shortcut(to_addr)
|
||||
LOG.info("Sending Email_________________")
|
||||
|
||||
if to_addr in shortcuts:
|
||||
LOG.info(f"To : {to_addr}")
|
||||
to_addr = email_address
|
||||
LOG.info(f" ({to_addr})")
|
||||
subject = config["ham"]["callsign"]
|
||||
subject = CONF.email_plugin.callsign
|
||||
# content = content + "\n\n(NOTE: reply with one line)"
|
||||
LOG.info(f"Subject : {subject}")
|
||||
LOG.info(f"Body : {content}")
|
||||
@ -395,13 +416,13 @@ def send_email(config, to_addr, content):
|
||||
|
||||
msg = MIMEText(content)
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = config["aprsd"]["email"]["smtp"]["login"]
|
||||
msg["From"] = CONF.email_plugin.smtp_login
|
||||
msg["To"] = to_addr
|
||||
server = _smtp_connect(config)
|
||||
server = _smtp_connect()
|
||||
if server:
|
||||
try:
|
||||
server.sendmail(
|
||||
config["aprsd"]["email"]["smtp"]["login"],
|
||||
CONF.email_plugin.smtp_login,
|
||||
[to_addr],
|
||||
msg.as_string(),
|
||||
)
|
||||
@ -415,19 +436,19 @@ def send_email(config, to_addr, content):
|
||||
|
||||
|
||||
@trace.trace
|
||||
def resend_email(config, count, fromcall):
|
||||
def resend_email(count, fromcall):
|
||||
date = datetime.datetime.now()
|
||||
month = date.strftime("%B")[:3] # Nov, Mar, Apr
|
||||
day = date.day
|
||||
year = date.year
|
||||
today = f"{day}-{month}-{year}"
|
||||
|
||||
shortcuts = config["aprsd"]["email"]["shortcuts"]
|
||||
shortcuts = _build_shortcuts_dict()
|
||||
# swap key/value
|
||||
shortcuts_inverted = {v: k for k, v in shortcuts.items()}
|
||||
|
||||
try:
|
||||
server = _imap_connect(config)
|
||||
server = _imap_connect()
|
||||
except Exception:
|
||||
LOG.exception("Failed to Connect to IMAP. Cannot resend email ")
|
||||
return
|
||||
@ -467,7 +488,7 @@ def resend_email(config, count, fromcall):
|
||||
reply = "-" + from_addr + " * " + body.decode(errors="ignore")
|
||||
tx.send(
|
||||
packets.MessagePacket(
|
||||
from_call=config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=fromcall,
|
||||
message_text=reply,
|
||||
),
|
||||
@ -490,7 +511,7 @@ def resend_email(config, count, fromcall):
|
||||
)
|
||||
tx.send(
|
||||
packets.MessagePacket(
|
||||
from_call=config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=fromcall,
|
||||
message_text=reply,
|
||||
),
|
||||
@ -504,9 +525,8 @@ def resend_email(config, count, fromcall):
|
||||
|
||||
|
||||
class APRSDEmailThread(threads.APRSDThread):
|
||||
def __init__(self, config):
|
||||
def __init__(self):
|
||||
super().__init__("EmailThread")
|
||||
self.config = config
|
||||
self.past = datetime.datetime.now()
|
||||
|
||||
def loop(self):
|
||||
@ -527,7 +547,7 @@ class APRSDEmailThread(threads.APRSDThread):
|
||||
f"check_email_delay is {EmailInfo().delay} seconds ",
|
||||
)
|
||||
|
||||
shortcuts = self.config["aprsd"]["email"]["shortcuts"]
|
||||
shortcuts = _build_shortcuts_dict()
|
||||
# swap key/value
|
||||
shortcuts_inverted = {v: k for k, v in shortcuts.items()}
|
||||
|
||||
@ -538,7 +558,7 @@ class APRSDEmailThread(threads.APRSDThread):
|
||||
today = f"{day}-{month}-{year}"
|
||||
|
||||
try:
|
||||
server = _imap_connect(self.config)
|
||||
server = _imap_connect()
|
||||
except Exception:
|
||||
LOG.exception("IMAP Failed to connect")
|
||||
return True
|
||||
@ -611,8 +631,8 @@ class APRSDEmailThread(threads.APRSDThread):
|
||||
# config ham.callsign
|
||||
tx.send(
|
||||
packets.MessagePacket(
|
||||
from_call=self.config["aprsd"]["callsign"],
|
||||
to_call=self.config["ham"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=CONF.email_plugin.callsign,
|
||||
message_text=reply,
|
||||
),
|
||||
)
|
||||
|
@ -2,10 +2,13 @@ import logging
|
||||
import re
|
||||
import time
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import packets, plugin, plugin_utils
|
||||
from aprsd.utils import trace
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -26,7 +29,7 @@ class LocationPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
|
||||
message = packet.get("message_text", None)
|
||||
# ack = packet.get("msgNo", "0")
|
||||
|
||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
||||
api_key = CONF.aprs_fi.apiKey
|
||||
|
||||
# optional second argument is a callsign to search
|
||||
a = re.search(r"^.*\s+(.*)", message)
|
||||
|
@ -1,8 +1,11 @@
|
||||
import logging
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import packets, plugin
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -20,7 +23,7 @@ class NotifySeenPlugin(plugin.APRSDWatchListPluginBase):
|
||||
def process(self, packet: packets.MessagePacket):
|
||||
LOG.info("NotifySeenPlugin")
|
||||
|
||||
notify_callsign = self.config["aprsd"]["watch_list"]["alert_callsign"]
|
||||
notify_callsign = CONF.watch_list.alert_callsign
|
||||
fromcall = packet.from_call
|
||||
|
||||
wl = packets.WatchList()
|
||||
@ -38,7 +41,7 @@ class NotifySeenPlugin(plugin.APRSDWatchListPluginBase):
|
||||
packet_type = packet.__class__.__name__
|
||||
# we shouldn't notify the alert user that they are online.
|
||||
pkt = packets.MessagePacket(
|
||||
from_call=self.config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=notify_callsign,
|
||||
message_text=(
|
||||
f"{fromcall} was just seen by type:'{packet_type}'"
|
||||
|
@ -2,11 +2,14 @@ import datetime
|
||||
import logging
|
||||
import re
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import packets, plugin
|
||||
from aprsd.packets import tracker
|
||||
from aprsd.utils import trace
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -17,6 +20,13 @@ class QueryPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
command_name = "query"
|
||||
short_description = "APRSD Owner command to query messages in the MsgTrack"
|
||||
|
||||
def setup(self):
|
||||
"""Do any plugin setup here."""
|
||||
if not CONF.query_plugin.callsign:
|
||||
LOG.error("Config query_plugin.callsign not set. Disabling plugin")
|
||||
self.enabled = False
|
||||
self.enabled = True
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet: packets.MessagePacket):
|
||||
LOG.info("Query COMMAND")
|
||||
@ -32,7 +42,7 @@ class QueryPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
now.strftime("%H:%M:%S"),
|
||||
)
|
||||
|
||||
searchstring = "^" + self.config["ham"]["callsign"] + ".*"
|
||||
searchstring = "^" + CONF.query_plugin.callsign + ".*"
|
||||
# only I can do admin commands
|
||||
if re.search(searchstring, fromcall):
|
||||
|
||||
|
@ -2,12 +2,14 @@ import logging
|
||||
import re
|
||||
import time
|
||||
|
||||
from oslo_config import cfg
|
||||
import pytz
|
||||
|
||||
from aprsd import packets, plugin, plugin_utils
|
||||
from aprsd.utils import fuzzy, trace
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -74,7 +76,7 @@ class TimeOWMPlugin(TimePlugin, plugin.APRSFIKEYMixin):
|
||||
# if no second argument, search for calling station
|
||||
searchcall = fromcall
|
||||
|
||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
||||
api_key = CONF.aprs_fi.apiKey
|
||||
try:
|
||||
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
||||
except Exception as ex:
|
||||
|
@ -2,12 +2,14 @@ import json
|
||||
import logging
|
||||
import re
|
||||
|
||||
from oslo_config import cfg
|
||||
import requests
|
||||
|
||||
from aprsd import plugin, plugin_utils
|
||||
from aprsd.utils import trace
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -34,10 +36,10 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin)
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
LOG.info("Weather Plugin")
|
||||
fromcall = packet.get("from")
|
||||
fromcall = packet.from_call
|
||||
# message = packet.get("message_text", None)
|
||||
# ack = packet.get("msgNo", "0")
|
||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
||||
api_key = CONF.aprs_fi.apiKey
|
||||
try:
|
||||
aprs_data = plugin_utils.get_aprs_fi(api_key, fromcall)
|
||||
except Exception as ex:
|
||||
@ -58,17 +60,23 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin)
|
||||
LOG.error(f"Couldn't fetch forecast.weather.gov '{ex}'")
|
||||
return "Unable to get weather"
|
||||
|
||||
reply = (
|
||||
"%sF(%sF/%sF) %s. %s, %s."
|
||||
% (
|
||||
wx_data["currentobservation"]["Temp"],
|
||||
wx_data["data"]["temperature"][0],
|
||||
wx_data["data"]["temperature"][1],
|
||||
wx_data["data"]["weather"][0],
|
||||
wx_data["time"]["startPeriodName"][1],
|
||||
wx_data["data"]["weather"][1],
|
||||
)
|
||||
).rstrip()
|
||||
LOG.info(f"WX data {wx_data}")
|
||||
|
||||
if wx_data["success"] == False:
|
||||
# Failed to fetch the weather
|
||||
reply = "Failed to fetch weather for location"
|
||||
else:
|
||||
reply = (
|
||||
"%sF(%sF/%sF) %s. %s, %s."
|
||||
% (
|
||||
wx_data["currentobservation"]["Temp"],
|
||||
wx_data["data"]["temperature"][0],
|
||||
wx_data["data"]["temperature"][1],
|
||||
wx_data["data"]["weather"][0],
|
||||
wx_data["time"]["startPeriodName"][1],
|
||||
wx_data["data"]["weather"][1],
|
||||
)
|
||||
).rstrip()
|
||||
LOG.debug(f"reply: '{reply}' ")
|
||||
return reply
|
||||
|
||||
@ -119,13 +127,7 @@ class USMetarPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
|
||||
# if no second argument, search for calling station
|
||||
fromcall = fromcall
|
||||
|
||||
try:
|
||||
self.config.exists(["services", "aprs.fi", "apiKey"])
|
||||
except Exception as ex:
|
||||
LOG.error(f"Failed to find config aprs.fi:apikey {ex}")
|
||||
return "No aprs.fi apikey found"
|
||||
|
||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
||||
api_key = CONF.aprs_fi.apiKey
|
||||
|
||||
try:
|
||||
aprs_data = plugin_utils.get_aprs_fi(api_key, fromcall)
|
||||
@ -187,6 +189,13 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
command_name = "OpenWeatherMap"
|
||||
short_description = "OpenWeatherMap weather of GPS Beacon location"
|
||||
|
||||
def setup(self):
|
||||
if not CONF.owm_weather_plugin.apiKey:
|
||||
LOG.error("Config.owm_weather_plugin.apiKey is not set. Disabling")
|
||||
self.enabled = False
|
||||
else:
|
||||
self.enabled = True
|
||||
|
||||
def help(self):
|
||||
_help = [
|
||||
"openweathermap: Send {} to get weather "
|
||||
@ -209,13 +218,8 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
else:
|
||||
searchcall = fromcall
|
||||
|
||||
try:
|
||||
self.config.exists(["services", "aprs.fi", "apiKey"])
|
||||
except Exception as ex:
|
||||
LOG.error(f"Failed to find config aprs.fi:apikey {ex}")
|
||||
return "No aprs.fi apikey found"
|
||||
api_key = CONF.aprs_fi.apiKey
|
||||
|
||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
||||
try:
|
||||
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
||||
except Exception as ex:
|
||||
@ -230,21 +234,8 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
lat = aprs_data["entries"][0]["lat"]
|
||||
lon = aprs_data["entries"][0]["lng"]
|
||||
|
||||
try:
|
||||
self.config.exists(["services", "openweathermap", "apiKey"])
|
||||
except Exception as ex:
|
||||
LOG.error(f"Failed to find config openweathermap:apiKey {ex}")
|
||||
return "No openweathermap apiKey found"
|
||||
|
||||
try:
|
||||
self.config.exists(["aprsd", "units"])
|
||||
except Exception:
|
||||
LOG.debug("Couldn't find untis in aprsd:services:units")
|
||||
units = "metric"
|
||||
else:
|
||||
units = self.config["aprsd"]["units"]
|
||||
|
||||
api_key = self.config["services"]["openweathermap"]["apiKey"]
|
||||
units = CONF.units
|
||||
api_key = CONF.owm_weather_plugin.apiKey
|
||||
try:
|
||||
wx_data = plugin_utils.fetch_openweathermap(
|
||||
api_key,
|
||||
@ -317,6 +308,16 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
command_name = "AVWXWeather"
|
||||
short_description = "AVWX weather of GPS Beacon location"
|
||||
|
||||
def setup(self):
|
||||
if not CONF.avwx_plugin.base_url:
|
||||
LOG.error("Config avwx_plugin.base_url not specified. Disabling")
|
||||
return False
|
||||
elif not CONF.avwx_plugin.apiKey:
|
||||
LOG.error("Config avwx_plugin.apiKey not specified. Disabling")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def help(self):
|
||||
_help = [
|
||||
"avwxweather: Send {} to get weather "
|
||||
@ -339,13 +340,7 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
else:
|
||||
searchcall = fromcall
|
||||
|
||||
try:
|
||||
self.config.exists(["services", "aprs.fi", "apiKey"])
|
||||
except Exception as ex:
|
||||
LOG.error(f"Failed to find config aprs.fi:apikey {ex}")
|
||||
return "No aprs.fi apikey found"
|
||||
|
||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
||||
api_key = CONF.aprs_fi.apiKey
|
||||
try:
|
||||
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
||||
except Exception as ex:
|
||||
@ -360,21 +355,8 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
lat = aprs_data["entries"][0]["lat"]
|
||||
lon = aprs_data["entries"][0]["lng"]
|
||||
|
||||
try:
|
||||
self.config.exists(["services", "avwx", "apiKey"])
|
||||
except Exception as ex:
|
||||
LOG.error(f"Failed to find config avwx:apiKey {ex}")
|
||||
return "No avwx apiKey found"
|
||||
|
||||
try:
|
||||
self.config.exists(self.config, ["services", "avwx", "base_url"])
|
||||
except Exception as ex:
|
||||
LOG.debug(f"Didn't find avwx:base_url {ex}")
|
||||
base_url = "https://avwx.rest"
|
||||
else:
|
||||
base_url = self.config["services"]["avwx"]["base_url"]
|
||||
|
||||
api_key = self.config["services"]["avwx"]["apiKey"]
|
||||
api_key = CONF.avwx_plugin.apiKey
|
||||
base_url = CONF.avwx_plugin.base_url
|
||||
token = f"TOKEN {api_key}"
|
||||
headers = {"Authorization": token}
|
||||
try:
|
||||
|
@ -2,12 +2,14 @@ import datetime
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from oslo_config import cfg
|
||||
import wrapt
|
||||
|
||||
import aprsd
|
||||
from aprsd import packets, plugin, utils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -15,7 +17,6 @@ class APRSDStats:
|
||||
|
||||
_instance = None
|
||||
lock = threading.Lock()
|
||||
config = None
|
||||
|
||||
start_time = None
|
||||
_aprsis_server = None
|
||||
@ -67,10 +68,6 @@ class APRSDStats:
|
||||
cls._instance._aprsis_keepalive = datetime.datetime.now()
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, config=None):
|
||||
if config:
|
||||
self.config = config
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
@property
|
||||
def uptime(self):
|
||||
@ -191,7 +188,7 @@ class APRSDStats:
|
||||
"aprsd": {
|
||||
"version": aprsd.__version__,
|
||||
"uptime": utils.strfdelta(self.uptime),
|
||||
"callsign": self.config["aprsd"]["callsign"],
|
||||
"callsign": CONF.callsign,
|
||||
"memory_current": int(self.memory),
|
||||
"memory_current_str": utils.human_size(self.memory),
|
||||
"memory_peak": int(self.memory_peak),
|
||||
@ -201,7 +198,7 @@ class APRSDStats:
|
||||
},
|
||||
"aprs-is": {
|
||||
"server": str(self.aprsis_server),
|
||||
"callsign": self.config["aprs"]["login"],
|
||||
"callsign": CONF.aprs_network.login,
|
||||
"last_update": last_aprsis_keepalive,
|
||||
},
|
||||
"packets": {
|
||||
@ -215,7 +212,7 @@ class APRSDStats:
|
||||
"ack_sent": self._pkt_cnt["AckPacket"]["tx"],
|
||||
},
|
||||
"email": {
|
||||
"enabled": self.config["aprsd"]["email"]["enabled"],
|
||||
"enabled": CONF.email_plugin.enabled,
|
||||
"sent": int(self._email_tx),
|
||||
"received": int(self._email_rx),
|
||||
"thread_last_update": last_update,
|
||||
|
@ -3,10 +3,13 @@ import logging
|
||||
import time
|
||||
import tracemalloc
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import client, packets, stats, utils
|
||||
from aprsd.threads import APRSDThread, APRSDThreadList
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
@ -14,10 +17,9 @@ class KeepAliveThread(APRSDThread):
|
||||
cntr = 0
|
||||
checker_time = datetime.datetime.now()
|
||||
|
||||
def __init__(self, config):
|
||||
def __init__(self):
|
||||
tracemalloc.start()
|
||||
super().__init__("KeepAlive")
|
||||
self.config = config
|
||||
max_timeout = {"hours": 0.0, "minutes": 2, "seconds": 0}
|
||||
self.max_delta = datetime.timedelta(**max_timeout)
|
||||
|
||||
@ -40,15 +42,9 @@ class KeepAliveThread(APRSDThread):
|
||||
stats_obj.set_memory(current)
|
||||
stats_obj.set_memory_peak(peak)
|
||||
|
||||
try:
|
||||
login = self.config["aprsd"]["callsign"]
|
||||
except KeyError:
|
||||
login = self.config["ham"]["callsign"]
|
||||
login = CONF.callsign
|
||||
|
||||
if pkt_tracker.is_initialized():
|
||||
tracked_packets = len(pkt_tracker)
|
||||
else:
|
||||
tracked_packets = 0
|
||||
tracked_packets = len(pkt_tracker)
|
||||
|
||||
keepalive = (
|
||||
"{} - Uptime {} RX:{} TX:{} Tracker:{} Msgs TX:{} RX:{} "
|
||||
@ -77,7 +73,7 @@ class KeepAliveThread(APRSDThread):
|
||||
if delta > self.max_delta:
|
||||
# We haven't gotten a keepalive from aprs-is in a while
|
||||
# reset the connection.a
|
||||
if not client.KISSClient.is_enabled(self.config):
|
||||
if not client.KISSClient.is_enabled():
|
||||
LOG.warning(f"Resetting connection to APRS-IS {delta}")
|
||||
client.factory.create().reset()
|
||||
|
||||
|
@ -4,18 +4,19 @@ import queue
|
||||
import time
|
||||
|
||||
import aprslib
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import client, packets, plugin
|
||||
from aprsd.threads import APRSDThread, tx
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class APRSDRXThread(APRSDThread):
|
||||
def __init__(self, config, packet_queue):
|
||||
def __init__(self, packet_queue):
|
||||
super().__init__("RX_MSG")
|
||||
self.config = config
|
||||
self.packet_queue = packet_queue
|
||||
self._client = client.factory.create()
|
||||
|
||||
@ -80,8 +81,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
||||
will ack a message before sending the packet to the subclass
|
||||
for processing."""
|
||||
|
||||
def __init__(self, config, packet_queue):
|
||||
self.config = config
|
||||
def __init__(self, packet_queue):
|
||||
self.packet_queue = packet_queue
|
||||
super().__init__("ProcessPKT")
|
||||
self._loop_cnt = 1
|
||||
@ -106,7 +106,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
||||
def process_packet(self, packet):
|
||||
"""Process a packet received from aprs-is server."""
|
||||
LOG.debug(f"RXPKT-LOOP {self._loop_cnt}")
|
||||
our_call = self.config["aprsd"]["callsign"].lower()
|
||||
our_call = CONF.callsign.lower()
|
||||
|
||||
from_call = packet.from_call
|
||||
if packet.addresse:
|
||||
@ -133,7 +133,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
||||
# send an ack last
|
||||
tx.send(
|
||||
packets.AckPacket(
|
||||
from_call=self.config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=from_call,
|
||||
msgNo=msg_id,
|
||||
),
|
||||
@ -178,11 +178,11 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
||||
if isinstance(subreply, packets.Packet):
|
||||
tx.send(subreply)
|
||||
else:
|
||||
wl = self.config["aprsd"]["watch_list"]
|
||||
wl = CONF.watch_list
|
||||
to_call = wl["alert_callsign"]
|
||||
tx.send(
|
||||
packets.MessagePacket(
|
||||
from_call=self.config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=to_call,
|
||||
message_text=subreply,
|
||||
),
|
||||
@ -219,7 +219,7 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
||||
else:
|
||||
tx.send(
|
||||
packets.MessagePacket(
|
||||
from_call=self.config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=from_call,
|
||||
message_text=subreply,
|
||||
),
|
||||
@ -238,7 +238,7 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
||||
LOG.debug(f"Sending '{reply}'")
|
||||
tx.send(
|
||||
packets.MessagePacket(
|
||||
from_call=self.config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=from_call,
|
||||
message_text=reply,
|
||||
),
|
||||
@ -246,12 +246,12 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
||||
|
||||
# If the message was for us and we didn't have a
|
||||
# response, then we send a usage statement.
|
||||
if to_call == self.config["aprsd"]["callsign"] and not replied:
|
||||
if to_call == CONF.callsign and not replied:
|
||||
LOG.warning("Sending help!")
|
||||
message_text = "Unknown command! Send 'help' message for help"
|
||||
tx.send(
|
||||
packets.MessagePacket(
|
||||
from_call=self.config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=from_call,
|
||||
message_text=message_text,
|
||||
),
|
||||
@ -260,11 +260,11 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
||||
LOG.error("Plugin failed!!!")
|
||||
LOG.exception(ex)
|
||||
# Do we need to send a reply?
|
||||
if to_call == self.config["aprsd"]["callsign"]:
|
||||
if to_call == CONF.callsign:
|
||||
reply = "A Plugin failed! try again?"
|
||||
tx.send(
|
||||
packets.MessagePacket(
|
||||
from_call=self.config["aprsd"]["callsign"],
|
||||
from_call=CONF.callsign,
|
||||
to_call=from_call,
|
||||
message_text=reply,
|
||||
),
|
||||
|
@ -1,16 +1,16 @@
|
||||
import abc
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import pickle
|
||||
|
||||
from aprsd import config as aprsd_config
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class ObjectStoreMixin(metaclass=abc.ABCMeta):
|
||||
class ObjectStoreMixin:
|
||||
"""Class 'MIXIN' intended to save/load object data.
|
||||
|
||||
The asumption of how this mixin is used:
|
||||
@ -24,13 +24,6 @@ class ObjectStoreMixin(metaclass=abc.ABCMeta):
|
||||
When APRSD Starts, it calls load()
|
||||
aprsd server -f (flush) will wipe all saved objects.
|
||||
"""
|
||||
@abc.abstractmethod
|
||||
def is_initialized(self):
|
||||
"""Return True if the class has been setup correctly.
|
||||
|
||||
If this returns False, the ObjectStore doesn't save anything.
|
||||
|
||||
"""
|
||||
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
@ -44,25 +37,18 @@ class ObjectStoreMixin(metaclass=abc.ABCMeta):
|
||||
return self.data[id]
|
||||
|
||||
def _init_store(self):
|
||||
if self.is_initialized():
|
||||
sl = self._save_location()
|
||||
if not os.path.exists(sl):
|
||||
LOG.warning(f"Save location {sl} doesn't exist")
|
||||
try:
|
||||
os.makedirs(sl)
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
else:
|
||||
LOG.warning(f"{self.__class__.__name__} is not initialized")
|
||||
|
||||
def _save_location(self):
|
||||
save_location = self.config.get("aprsd.save_location", None)
|
||||
if not save_location:
|
||||
save_location = aprsd_config.DEFAULT_CONFIG_DIR
|
||||
return save_location
|
||||
if not CONF.enable_save:
|
||||
return
|
||||
sl = CONF.save_location
|
||||
if not os.path.exists(sl):
|
||||
LOG.warning(f"Save location {sl} doesn't exist")
|
||||
try:
|
||||
os.makedirs(sl)
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
|
||||
def _save_filename(self):
|
||||
save_location = self._save_location()
|
||||
save_location = CONF.save_location
|
||||
|
||||
return "{}/{}.p".format(
|
||||
save_location,
|
||||
@ -79,45 +65,48 @@ class ObjectStoreMixin(metaclass=abc.ABCMeta):
|
||||
|
||||
def save(self):
|
||||
"""Save any queued to disk?"""
|
||||
if self.is_initialized():
|
||||
if len(self) > 0:
|
||||
LOG.info(
|
||||
f"{self.__class__.__name__}::Saving"
|
||||
f" {len(self)} entries to disk at"
|
||||
f"{self._save_location()}",
|
||||
)
|
||||
with open(self._save_filename(), "wb+") as fp:
|
||||
pickle.dump(self._dump(), fp)
|
||||
else:
|
||||
LOG.debug(
|
||||
"{} Nothing to save, flushing old save file '{}'".format(
|
||||
self.__class__.__name__,
|
||||
self._save_filename(),
|
||||
),
|
||||
)
|
||||
self.flush()
|
||||
if not CONF.enable_save:
|
||||
return
|
||||
if len(self) > 0:
|
||||
LOG.info(
|
||||
f"{self.__class__.__name__}::Saving"
|
||||
f" {len(self)} entries to disk at"
|
||||
f"{CONF.save_location}",
|
||||
)
|
||||
with open(self._save_filename(), "wb+") as fp:
|
||||
pickle.dump(self._dump(), fp)
|
||||
else:
|
||||
LOG.debug(
|
||||
"{} Nothing to save, flushing old save file '{}'".format(
|
||||
self.__class__.__name__,
|
||||
self._save_filename(),
|
||||
),
|
||||
)
|
||||
self.flush()
|
||||
|
||||
def load(self):
|
||||
if self.is_initialized():
|
||||
if os.path.exists(self._save_filename()):
|
||||
try:
|
||||
with open(self._save_filename(), "rb") as fp:
|
||||
raw = pickle.load(fp)
|
||||
if raw:
|
||||
self.data = raw
|
||||
LOG.debug(
|
||||
f"{self.__class__.__name__}::Loaded {len(self)} entries from disk.",
|
||||
)
|
||||
LOG.debug(f"{self.data}")
|
||||
except (pickle.UnpicklingError, Exception) as ex:
|
||||
LOG.error(f"Failed to UnPickle {self._save_filename()}")
|
||||
LOG.error(ex)
|
||||
self.data = {}
|
||||
if not CONF.enable_save:
|
||||
return
|
||||
if os.path.exists(self._save_filename()):
|
||||
try:
|
||||
with open(self._save_filename(), "rb") as fp:
|
||||
raw = pickle.load(fp)
|
||||
if raw:
|
||||
self.data = raw
|
||||
LOG.debug(
|
||||
f"{self.__class__.__name__}::Loaded {len(self)} entries from disk.",
|
||||
)
|
||||
LOG.debug(f"{self.data}")
|
||||
except (pickle.UnpicklingError, Exception) as ex:
|
||||
LOG.error(f"Failed to UnPickle {self._save_filename()}")
|
||||
LOG.error(ex)
|
||||
self.data = {}
|
||||
|
||||
def flush(self):
|
||||
"""Nuke the old pickle file that stored the old results from last aprsd run."""
|
||||
if self.is_initialized():
|
||||
if os.path.exists(self._save_filename()):
|
||||
pathlib.Path(self._save_filename()).unlink()
|
||||
with self.lock:
|
||||
self.data = {}
|
||||
if not CONF.enable_save:
|
||||
return
|
||||
if os.path.exists(self._save_filename()):
|
||||
pathlib.Path(self._save_filename()).unlink()
|
||||
with self.lock:
|
||||
self.data = {}
|
||||
|
@ -1,12 +1,12 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.9
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --annotation-style=line --resolver=backtracking dev-requirements.in
|
||||
#
|
||||
add-trailing-comma==2.4.0 # via gray
|
||||
alabaster==0.7.12 # via sphinx
|
||||
attrs==22.1.0 # via jsonschema, pytest
|
||||
attrs==22.2.0 # via jsonschema, pytest
|
||||
autoflake==1.5.3 # via gray
|
||||
babel==2.11.0 # via sphinx
|
||||
black==22.12.0 # via gray
|
||||
@ -20,21 +20,20 @@ click==8.1.3 # via black, pip-tools
|
||||
colorama==0.4.6 # via tox
|
||||
commonmark==0.9.1 # via rich
|
||||
configargparse==1.5.3 # via gray
|
||||
coverage[toml]==6.5.0 # via pytest-cov
|
||||
coverage[toml]==7.0.1 # via pytest-cov
|
||||
distlib==0.3.6 # via virtualenv
|
||||
docutils==0.19 # via sphinx
|
||||
exceptiongroup==1.0.4 # via pytest
|
||||
exceptiongroup==1.1.0 # via pytest
|
||||
filelock==3.8.2 # via tox, virtualenv
|
||||
fixit==0.1.4 # via gray
|
||||
flake8==6.0.0 # via -r dev-requirements.in, fixit, pep8-naming
|
||||
gray==0.13.0 # via -r dev-requirements.in
|
||||
identify==2.5.9 # via pre-commit
|
||||
identify==2.5.11 # via pre-commit
|
||||
idna==3.4 # via requests
|
||||
imagesize==1.4.1 # via sphinx
|
||||
importlib-metadata==5.1.0 # via sphinx
|
||||
importlib-resources==5.10.1 # via fixit
|
||||
iniconfig==1.1.1 # via pytest
|
||||
isort==5.11.2 # via -r dev-requirements.in, gray
|
||||
isort==5.11.4 # via -r dev-requirements.in, gray
|
||||
jinja2==3.1.2 # via sphinx
|
||||
jsonschema==4.17.3 # via fixit
|
||||
libcst==0.4.9 # via fixit
|
||||
@ -46,8 +45,8 @@ nodeenv==1.7.0 # via pre-commit
|
||||
packaging==22.0 # via build, pyproject-api, pytest, sphinx, tox
|
||||
pathspec==0.10.3 # via black
|
||||
pep517==0.13.0 # via build
|
||||
pep8-naming==0.13.2 # via -r dev-requirements.in
|
||||
pip-tools==6.12.0 # via -r dev-requirements.in
|
||||
pep8-naming==0.13.3 # via -r dev-requirements.in
|
||||
pip-tools==6.12.1 # via -r dev-requirements.in
|
||||
platformdirs==2.6.0 # via black, tox, virtualenv
|
||||
pluggy==1.0.0 # via pytest, tox
|
||||
pre-commit==2.20.0 # via -r dev-requirements.in
|
||||
@ -58,7 +57,7 @@ pyproject-api==1.2.1 # via tox
|
||||
pyrsistent==0.19.2 # via jsonschema
|
||||
pytest==7.2.0 # via -r dev-requirements.in, pytest-cov
|
||||
pytest-cov==4.0.0 # via -r dev-requirements.in
|
||||
pytz==2022.6 # via babel
|
||||
pytz==2022.7 # via babel
|
||||
pyupgrade==3.3.1 # via gray
|
||||
pyyaml==6.0 # via fixit, libcst, pre-commit
|
||||
requests==2.28.1 # via sphinx
|
||||
@ -74,15 +73,14 @@ sphinxcontrib-serializinghtml==1.1.5 # via sphinx
|
||||
tokenize-rt==5.0.0 # via add-trailing-comma, pyupgrade
|
||||
toml==0.10.2 # via autoflake, pre-commit
|
||||
tomli==2.0.1 # via black, build, coverage, mypy, pep517, pyproject-api, pytest, tox
|
||||
tox==4.0.9 # via -r dev-requirements.in
|
||||
typing-extensions==4.4.0 # via black, libcst, mypy, typing-inspect
|
||||
tox==4.0.16 # via -r dev-requirements.in
|
||||
typing-extensions==4.4.0 # via libcst, mypy, typing-inspect
|
||||
typing-inspect==0.8.0 # via libcst
|
||||
unify==0.5 # via gray
|
||||
untokenize==0.1.1 # via unify
|
||||
urllib3==1.26.13 # via requests
|
||||
virtualenv==20.17.1 # via pre-commit, tox
|
||||
wheel==0.38.4 # via pip-tools
|
||||
zipp==3.11.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# pip
|
||||
|
@ -29,3 +29,4 @@ user-agents
|
||||
pyopenssl
|
||||
dataclasses
|
||||
dacite2
|
||||
oslo.config
|
||||
|
@ -1,15 +1,15 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.9
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --annotation-style=line --resolver=backtracking requirements.in
|
||||
#
|
||||
aprslib==0.7.2 # via -r requirements.in
|
||||
attrs==22.1.0 # via -r requirements.in, ax253, kiss3
|
||||
attrs==22.2.0 # via -r requirements.in, ax253, kiss3
|
||||
ax253==0.1.5.post1 # via kiss3
|
||||
beautifulsoup4==4.11.1 # via -r requirements.in
|
||||
bidict==0.22.0 # via python-socketio
|
||||
bitarray==2.6.0 # via ax253, kiss3
|
||||
bitarray==2.6.1 # via ax253, kiss3
|
||||
certifi==2022.12.7 # via requests
|
||||
cffi==1.15.1 # via cryptography
|
||||
charset-normalizer==2.1.1 # via requests
|
||||
@ -19,6 +19,7 @@ commonmark==0.9.1 # via rich
|
||||
cryptography==38.0.4 # via pyopenssl
|
||||
dacite2==2.0.0 # via -r requirements.in
|
||||
dataclasses==0.6 # via -r requirements.in
|
||||
debtcollector==2.5.0 # via oslo-config
|
||||
dnspython==2.2.1 # via eventlet
|
||||
eventlet==0.33.2 # via -r requirements.in
|
||||
flask==2.1.2 # via -r requirements.in, flask-classful, flask-httpauth, flask-socketio
|
||||
@ -28,12 +29,15 @@ flask-socketio==5.3.2 # via -r requirements.in
|
||||
greenlet==2.0.1 # via eventlet
|
||||
idna==3.4 # via requests
|
||||
imapclient==2.3.1 # via -r requirements.in
|
||||
importlib-metadata==5.1.0 # via ax253, flask, kiss3
|
||||
importlib-metadata==5.2.0 # via ax253, kiss3
|
||||
itsdangerous==2.1.2 # via flask
|
||||
jinja2==3.1.2 # via click-completion, flask
|
||||
kiss3==8.0.0 # via -r requirements.in
|
||||
markupsafe==2.1.1 # via jinja2
|
||||
pbr==5.11.0 # via -r requirements.in
|
||||
netaddr==0.8.0 # via oslo-config
|
||||
oslo-config==9.0.0 # via -r requirements.in
|
||||
oslo-i18n==5.1.0 # via oslo-config
|
||||
pbr==5.11.0 # via -r requirements.in, oslo-i18n, stevedore
|
||||
pluggy==1.0.0 # via -r requirements.in
|
||||
pycparser==2.21 # via cffi
|
||||
pygments==2.13.0 # via rich
|
||||
@ -42,13 +46,15 @@ pyserial==3.5 # via pyserial-asyncio
|
||||
pyserial-asyncio==0.6 # via kiss3
|
||||
python-engineio==4.3.4 # via python-socketio
|
||||
python-socketio==5.7.2 # via flask-socketio
|
||||
pytz==2022.6 # via -r requirements.in
|
||||
pyyaml==6.0 # via -r requirements.in
|
||||
requests==2.28.1 # via -r requirements.in, update-checker
|
||||
pytz==2022.7 # via -r requirements.in
|
||||
pyyaml==6.0 # via -r requirements.in, oslo-config
|
||||
requests==2.28.1 # via -r requirements.in, oslo-config, update-checker
|
||||
rfc3986==2.0.0 # via oslo-config
|
||||
rich==12.6.0 # via -r requirements.in
|
||||
shellingham==1.5.0 # via click-completion
|
||||
six==1.16.0 # via -r requirements.in, click-completion, eventlet, imapclient
|
||||
soupsieve==2.3.2.post1 # via beautifulsoup4
|
||||
stevedore==4.1.1 # via oslo-config
|
||||
tabulate==0.9.0 # via -r requirements.in
|
||||
thesmuggler==1.0.1 # via -r requirements.in
|
||||
ua-parser==0.16.1 # via user-agents
|
||||
@ -56,5 +62,5 @@ update-checker==0.18.0 # via -r requirements.in
|
||||
urllib3==1.26.13 # via requests
|
||||
user-agents==2.2.0 # via -r requirements.in
|
||||
werkzeug==2.1.2 # via -r requirements.in, flask
|
||||
wrapt==1.14.1 # via -r requirements.in
|
||||
wrapt==1.14.1 # via -r requirements.in, debtcollector
|
||||
zipp==3.11.0 # via importlib-metadata
|
||||
|
@ -34,6 +34,10 @@ packages =
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
aprsd = aprsd.aprsd:main
|
||||
oslo.config.opts =
|
||||
aprsd.conf = aprsd.conf.opts:list_opts
|
||||
oslo.config.opts.defaults =
|
||||
aprsd.conf = aprsd.conf:set_lib_defaults
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = docs
|
||||
|
@ -5,21 +5,20 @@ from aprsd.plugins import email
|
||||
|
||||
class TestEmail(unittest.TestCase):
|
||||
def test_get_email_from_shortcut(self):
|
||||
config = {"aprsd": {"email": {"shortcuts": {}}}}
|
||||
email_address = "something@something.com"
|
||||
addr = f"-{email_address}"
|
||||
actual = email.get_email_from_shortcut(config, addr)
|
||||
actual = email.get_email_from_shortcut(addr)
|
||||
self.assertEqual(addr, actual)
|
||||
|
||||
config = {"aprsd": {"email": {"nothing": "nothing"}}}
|
||||
actual = email.get_email_from_shortcut(config, addr)
|
||||
actual = email.get_email_from_shortcut(addr)
|
||||
self.assertEqual(addr, actual)
|
||||
|
||||
config = {"aprsd": {"email": {"shortcuts": {"not_used": "empty"}}}}
|
||||
actual = email.get_email_from_shortcut(config, addr)
|
||||
actual = email.get_email_from_shortcut(addr)
|
||||
self.assertEqual(addr, actual)
|
||||
|
||||
config = {"aprsd": {"email": {"shortcuts": {"-wb": email_address}}}}
|
||||
short = "-wb"
|
||||
actual = email.get_email_from_shortcut(config, short)
|
||||
actual = email.get_email_from_shortcut(short)
|
||||
self.assertEqual(email_address, actual)
|
||||
|
@ -17,6 +17,6 @@ class TestMain(unittest.TestCase):
|
||||
"""Test to make sure we fail."""
|
||||
imap_mock.return_value = None
|
||||
smtp_mock.return_value = {"smaiof": "fire"}
|
||||
config = mock.MagicMock()
|
||||
mock.MagicMock()
|
||||
|
||||
email.validate_email_config(config, True)
|
||||
email.validate_email_config(True)
|
||||
|
Loading…
Reference in New Issue
Block a user