Beginning a CLI client application using the client library.

This commit is contained in:
Michael Woods
2025-02-17 23:48:48 -05:00
parent cff2a22078
commit 780c1eb051
16 changed files with 123 additions and 7 deletions

View File

@@ -1 +1 @@
VERSION="0.3.0-alpha"
VERSION="0.4.0-alpha"

View File

@@ -0,0 +1,45 @@
import click
from packetserver.client.cli.config import get_config, default_app_dir, config_path
from packetserver.client.cli.constants import DEFAULT_DB_FILE
import ZODB
import ZODB.FileStorage
import sys
import os
import os.path
from pathlib import Path
from packetserver.client import Client
from packetserver.client.users import get_user_by_username, UserWrapper
VERSION="0.1.0-alpha"
@click.group()
@click.option('--conf', default=config_path(), help='path to configfile')
@click.option('--server', '-s', default='', help="server radio callsign to connect to (required)")
@click.option('--agwpe', '-a', default='localhost', help="AGWPE TNC server address to connect to (config file)")
@click.option('--port', '-p', default=8000, help="AGWPE TNC server port to connect to (config file)")
@click.option('--callsign', '-c', default='', help="radio callsign[+ssid] of this client station (config file)")
@click.version_option(VERSION,"--version", "-v")
@click.pass_context
def cli(ctx, conf, server, agwpe, port, callsign):
"""Command line interface for the PacketServer client and server API."""
cfg = get_config(config_file_path=conf)
storage = ZODB.FileStorage.FileStorage(os.path.join(cfg['cli']['directory'], DEFAULT_DB_FILE))
db = ZODB.DB(storage)
ctx.ensure_object(dict)
ctx.obj['CONFIG'] = cfg
ctx.obj['bbs'] = server
ctx.obj['db'] = db
@click.command()
@click.option('--username', '-u', default='', help="the username (CALLSIGN) to lookup on the bbs")
@click.pass_context
def user(ctx):
"""Query users on the BBS and customize personal settings."""
pass
cli.add_command(user)
if __name__ == '__main__':
cli()

View File

View File

@@ -0,0 +1,24 @@
import os
import os.path
from configparser import ConfigParser
from pathlib import Path
from packetserver.client.cli.constants import DEFAULT_APP_DIR, DEFAULT_CONFIG_FILE
def default_app_dir() -> str:
return os.path.join(str(Path.home()), DEFAULT_APP_DIR)
def config_path(app_path=default_app_dir()) -> str:
return os.path.join(app_path, DEFAULT_CONFIG_FILE)
def get_config(config_file_path=config_path()) -> ConfigParser:
config = ConfigParser()
if os.path.isfile(config_file_path):
config.read(config_file_path)
if not 'cli' in config.sections():
config.add_section('cli')
if 'directory' not in config['cli']:
config['cli']['directory'] = default_app_dir()
return config

View File

@@ -0,0 +1,5 @@
import os.path
DEFAULT_APP_DIR = ".packetserver"
DEFAULT_CONFIG_FILE = "cli.ini"
DEFAULT_DB_FILE = "cli-client.zopedb"

View File

View File

View File

View File

View File

View File

@@ -0,0 +1,7 @@

View File

@@ -119,14 +119,25 @@ def send_job_quick(client: Client, bbs_callsign: str, cmd: Union[str, list], db:
def get_job_id(client: Client, bbs_callsign: str, job_id: int, get_data=True) -> JobWrapper:
req = Request.blank()
req.path = f"job/{job_id}"
req.set_var('data', get_data)
req.method = Request.Method.GET
response = client.send_receive_callsign(req, bbs_callsign)
if response.status_code != 200:
raise RuntimeError(f"Sending job failed: {response.status_code}: {response.payload}")
raise RuntimeError(f"GET job {job_id} failed: {response.status_code}: {response.payload}")
return JobWrapper(response.payload)
def get_user_jobs(): # TODO
pass
def get_user_jobs(client: Client, bbs_callsign: str, get_data=True) -> list[JobWrapper]:
req = Request.blank()
req.path = f"job/user"
req.set_var('data', get_data)
req.method = Request.Method.GET
response = client.send_receive_callsign(req, bbs_callsign)
if response.status_code != 200:
raise RuntimeError(f"GET user jobs failed: {response.status_code}: {response.payload}")
jobs = []
for j in response.payload:
jobs.append(JobWrapper(j))
return jobs
class JobSession:
def __init__(self, client: Client, bbs_callsign: str, default_timeout: int = 300, stutter: int = 2):

View File

@@ -25,7 +25,7 @@ from threading import Thread
from packetserver.server.jobs import get_orchestrator_from_config, Job, JobStatus
from packetserver.runner import RunnerStatus, RunnerFile, Orchestrator, Runner
VERSION="0.2.0-alpha"
VERSION="0.4.0-alpha"
def init_bulletins(root: PersistentMapping):
if 'bulletins' not in root:
@@ -103,7 +103,7 @@ class Server:
if 'runner' in conn.root.config['jobs_config']:
val = str(conn.root.config['jobs_config']['runner']).lower().strip()
if val in ['podman']:
logging.debug("Enabling podman orchestrator")
logging.debug(f"Enabling {val} orchestrator")
self.orchestrator = get_orchestrator_from_config(conn.root.config['jobs_config'])
self.app = pe.app.Application()

View File

@@ -18,7 +18,7 @@ import gzip
import tarfile
import time
import json
from packetserver.runner.podman import TarFileExtractor, PodmanOrchestrator, PodmanRunner, PodmanOptions
from packetserver.common.util import TarFileExtractor
from packetserver.runner import Orchestrator, Runner, RunnerStatus, RunnerFile
from enum import Enum
from io import BytesIO
@@ -38,6 +38,7 @@ def get_orchestrator_from_config(cfg: dict) -> Union[Orchestrator, PodmanOrchest
if 'runner' in cfg:
val = cfg['runner'].lower().strip()
if val == "podman":
from packetserver.runner.podman import PodmanOrchestrator, PodmanOptions
image = cfg.get('image', 'debian')
opts = PodmanOptions(default_timeout=300, max_timeout=3600, image_name=image, max_active_jobs=5,
container_keepalive=300, name_prefix="packetserver_")

22
src/packetserver/setup.py Normal file
View File

@@ -0,0 +1,22 @@
from setuptools import setup, find_packages
setup(
name='packetserver',
version='VERSION="0.4.0-alpha',
packages=find_packages(),
include_package_data=True,
install_requires=[
'click',
'pyham_pe',
'msgpack',
'pyham_ax25',
'ZODB',
'ZEO',
'podman'
],
entry_points={
'console_scripts': [
'packcli = packetserver.client.cli:cli',
],
},
)