mirror of
https://github.com/craigerl/aprsd.git
synced 2024-11-25 17:38:44 -05:00
Added the fetch-stats command
You can now fetch and view the stats of a live running aprsd server if it has enabled the rpc server in the config file's rpc_settings block. You just have to match the magic word as specified in the config file to authorize against the rpc server. aprsd fetch-stats --ip-address <ip of aprsd> --port <port> --magic-word <magic word>
This commit is contained in:
parent
fe0d71de4d
commit
b2e621da4b
131
aprsd/cmds/fetch_stats.py
Normal file
131
aprsd/cmds/fetch_stats.py
Normal file
@ -0,0 +1,131 @@
|
||||
# Fetch active stats from a remote running instance of aprsd server
|
||||
# This uses the RPC server to fetch the stats from the remote server.
|
||||
|
||||
import logging
|
||||
|
||||
import click
|
||||
from click_params import IP_ADDRESS
|
||||
from oslo_config import cfg
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
# local imports here
|
||||
import aprsd
|
||||
from aprsd import cli_helper
|
||||
from aprsd.main import cli
|
||||
from aprsd.rpc import client as rpc_client
|
||||
|
||||
|
||||
# setup the global logger
|
||||
# logging.basicConfig(level=logging.DEBUG) # level=10
|
||||
LOG = logging.getLogger("APRSD")
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@cli.command()
|
||||
@cli_helper.add_options(cli_helper.common_options)
|
||||
@click.option(
|
||||
"--ip-address", type=IP_ADDRESS,
|
||||
default=CONF.rpc_settings.ip,
|
||||
help="IP address of the remote aprsd server to fetch stats from.",
|
||||
)
|
||||
@click.option(
|
||||
"--port", type=int,
|
||||
default=CONF.rpc_settings.port,
|
||||
help="Port of the remote aprsd server rpc port to fetch stats from.",
|
||||
)
|
||||
@click.option(
|
||||
"--magic-word", type=str,
|
||||
default=CONF.rpc_settings.magic_word,
|
||||
help="Magic word of the remote aprsd server rpc port to fetch stats from.",
|
||||
)
|
||||
@click.pass_context
|
||||
@cli_helper.process_standard_options
|
||||
def fetch_stats(ctx, ip_address, port, magic_word):
|
||||
"""Fetch stats from a remote running instance of aprsd server."""
|
||||
LOG.info(f"APRSD Fetch-Stats started version: {aprsd.__version__}")
|
||||
|
||||
CONF.log_opt_values(LOG, logging.DEBUG)
|
||||
|
||||
msg = f"Fetching stats from {ip_address}:{port} with magic word '{magic_word}'"
|
||||
|
||||
console = Console()
|
||||
with console.status(msg) as status:
|
||||
client = rpc_client.RPCClient(ip_address, port, magic_word)
|
||||
stats = client.get_stats_dict()
|
||||
console.print_json(data=stats)
|
||||
aprsd_title = (
|
||||
"APRSD "
|
||||
f"[bold cyan]v{stats['aprsd']['version']}[/] "
|
||||
f"Callsign [bold green]{stats['aprsd']['callsign']}[/] "
|
||||
f"Uptime [bold yellow]{stats['aprsd']['uptime']}[/]"
|
||||
)
|
||||
|
||||
console.rule(f"Stats from {ip_address}:{port} with magic word '{magic_word}'")
|
||||
console.print("\n\n")
|
||||
console.rule(aprsd_title)
|
||||
|
||||
# Show the connection to APRS
|
||||
# It can be a connection to an APRS-IS server or a local TNC via KISS or KISSTCP
|
||||
if "aprs-is" in stats:
|
||||
title = f"APRS-IS Connection {stats['aprs-is']['server']}"
|
||||
table = Table(title=title)
|
||||
table.add_column("Key")
|
||||
table.add_column("Value")
|
||||
for key, value in stats["aprs-is"].items():
|
||||
table.add_row(key, value)
|
||||
console.print(table)
|
||||
|
||||
|
||||
msgs_table = Table(title="Messages")
|
||||
msgs_table.add_column("Key")
|
||||
msgs_table.add_column("Value")
|
||||
for key, value in stats["messages"].items():
|
||||
msgs_table.add_row(key, str(value))
|
||||
|
||||
console.print(msgs_table)
|
||||
|
||||
packets_table = Table(title="Packets")
|
||||
packets_table.add_column("Key")
|
||||
packets_table.add_column("Value")
|
||||
for key, value in stats["packets"].items():
|
||||
packets_table.add_row(key, str(value))
|
||||
|
||||
console.print(packets_table)
|
||||
|
||||
if "plugins" in stats:
|
||||
plugins_table = Table(title="Plugins")
|
||||
plugins_table.add_column("Plugin")
|
||||
plugins_table.add_column("Enabled")
|
||||
plugins_table.add_column("Version")
|
||||
plugins_table.add_column("TX")
|
||||
plugins_table.add_column("RX")
|
||||
for key, value in stats["plugins"].items():
|
||||
plugins_table.add_row(
|
||||
key,
|
||||
str(stats["plugins"][key]["enabled"]),
|
||||
stats["plugins"][key]["version"],
|
||||
str(stats["plugins"][key]["tx"]),
|
||||
str(stats["plugins"][key]["rx"]),
|
||||
)
|
||||
|
||||
console.print(plugins_table)
|
||||
|
||||
if "seen_list" in stats["aprsd"]:
|
||||
seen_table = Table(title="Seen List")
|
||||
seen_table.add_column("Callsign")
|
||||
seen_table.add_column("Message Count")
|
||||
seen_table.add_column("Last Heard")
|
||||
for key, value in stats["aprsd"]["seen_list"].items():
|
||||
seen_table.add_row(key, str(value["count"]), value["last"])
|
||||
|
||||
console.print(seen_table)
|
||||
|
||||
if "watch_list" in stats["aprsd"]:
|
||||
watch_table = Table(title="Watch List")
|
||||
watch_table.add_column("Callsign")
|
||||
watch_table.add_column("Last Heard")
|
||||
for key, value in stats["aprsd"]["watch_list"].items():
|
||||
watch_table.add_row(key, value["last"])
|
||||
|
||||
console.print(watch_table)
|
@ -70,8 +70,8 @@ def main():
|
||||
# First import all the possible commands for the CLI
|
||||
# The commands themselves live in the cmds directory
|
||||
from .cmds import ( # noqa
|
||||
completion, dev, healthcheck, list_plugins, listen, send_message,
|
||||
server, webchat,
|
||||
completion, dev, fetch_stats, healthcheck, list_plugins, listen,
|
||||
send_message, server, webchat,
|
||||
)
|
||||
cli(auto_envvar_prefix="APRSD")
|
||||
|
||||
|
@ -16,23 +16,32 @@ class RPCClient:
|
||||
_instance = None
|
||||
_rpc_client = None
|
||||
|
||||
ip = None
|
||||
port = None
|
||||
magic_word = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, ip=None, port=None, magic_word=None):
|
||||
self.ip = str(ip) or CONF.rpc_settings.ip
|
||||
self.port = int(port) or CONF.rpc_settings.port
|
||||
self.magic_word = magic_word or CONF.rpc_settings.magic_word
|
||||
self._check_settings()
|
||||
self.get_rpc_client()
|
||||
|
||||
def _check_settings(self):
|
||||
if not CONF.rpc_settings.enabled:
|
||||
LOG.error("RPC is not enabled, no way to get stats!!")
|
||||
LOG.warning("RPC is not enabled, no way to get stats!!")
|
||||
|
||||
if CONF.rpc_settings.magic_word == conf.common.APRSD_DEFAULT_MAGIC_WORD:
|
||||
if self.magic_word == conf.common.APRSD_DEFAULT_MAGIC_WORD:
|
||||
LOG.warning("You are using the default RPC magic word!!!")
|
||||
LOG.warning("edit aprsd.conf and change rpc_settings.magic_word")
|
||||
|
||||
LOG.debug(f"RPC Client: {self.ip}:{self.port} {self.magic_word}")
|
||||
|
||||
def _rpyc_connect(
|
||||
self, host, port,
|
||||
service=rpyc.VoidService,
|
||||
@ -53,11 +62,11 @@ class RPCClient:
|
||||
|
||||
def get_rpc_client(self):
|
||||
if not self._rpc_client:
|
||||
magic = CONF.rpc_settings.magic_word
|
||||
CONF.rpc_settings.magic_word
|
||||
self._rpc_client = self._rpyc_connect(
|
||||
CONF.rpc_settings.ip,
|
||||
CONF.rpc_settings.port,
|
||||
authorizer=lambda sock: sock.send(magic.encode()),
|
||||
self.ip,
|
||||
self.port,
|
||||
authorizer=lambda sock: sock.send(self.magic_word.encode()),
|
||||
)
|
||||
return self._rpc_client
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
aprslib>=0.7.0
|
||||
click
|
||||
click-params
|
||||
click-completion
|
||||
flask==2.1.2
|
||||
werkzeug==2.1.2
|
||||
|
@ -13,13 +13,15 @@ bitarray==2.7.6 # via ax253, kiss3
|
||||
certifi==2023.5.7 # via requests
|
||||
cffi==1.15.1 # via cryptography
|
||||
charset-normalizer==3.2.0 # via requests
|
||||
click==8.1.4 # via -r requirements.in, click-completion, flask
|
||||
click==8.1.4 # via -r requirements.in, click-completion, click-params, flask
|
||||
click-completion==0.5.2 # via -r requirements.in
|
||||
click-params==0.4.1 # via -r requirements.in
|
||||
commonmark==0.9.1 # via rich
|
||||
cryptography==38.0.1 # via -r requirements.in, pyopenssl
|
||||
dacite2==2.0.0 # via -r requirements.in
|
||||
dataclasses==0.6 # via -r requirements.in
|
||||
debtcollector==2.5.0 # via oslo-config
|
||||
decorator==5.1.1 # via validators
|
||||
dnspython==2.3.0 # via eventlet
|
||||
eventlet==0.33.3 # via -r requirements.in
|
||||
flask==2.1.2 # via -r requirements.in, flask-classful, flask-httpauth, flask-socketio
|
||||
@ -66,6 +68,7 @@ ua-parser==0.18.0 # via user-agents
|
||||
update-checker==0.18.0 # via -r requirements.in
|
||||
urllib3==2.0.3 # via requests
|
||||
user-agents==2.2.0 # via -r requirements.in
|
||||
validators==0.20.0 # via click-params
|
||||
werkzeug==2.1.2 # via -r requirements.in, flask
|
||||
wrapt==1.15.0 # via -r requirements.in, debtcollector
|
||||
zipp==3.15.0 # via importlib-metadata
|
||||
zipp==3.16.0 # via importlib-metadata
|
||||
|
Loading…
Reference in New Issue
Block a user