diff --git a/requirements.txt b/requirements.txt index 590e279..5162544 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ BTrees transaction ZEO podman +click \ No newline at end of file diff --git a/src/packetserver/__init__.py b/src/packetserver/__init__.py index 3881f00..a0832b3 100644 --- a/src/packetserver/__init__.py +++ b/src/packetserver/__init__.py @@ -1 +1 @@ -VERSION="0.3.0-alpha" \ No newline at end of file +VERSION="0.4.0-alpha" \ No newline at end of file diff --git a/src/packetserver/client/cli/__init__.py b/src/packetserver/client/cli/__init__.py new file mode 100644 index 0000000..3ad09db --- /dev/null +++ b/src/packetserver/client/cli/__init__.py @@ -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() diff --git a/src/packetserver/client/cli/bulletin.py b/src/packetserver/client/cli/bulletin.py new file mode 100644 index 0000000..e69de29 diff --git a/src/packetserver/client/cli/config.py b/src/packetserver/client/cli/config.py new file mode 100644 index 0000000..510b70c --- /dev/null +++ b/src/packetserver/client/cli/config.py @@ -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 \ No newline at end of file diff --git a/src/packetserver/client/cli/constants.py b/src/packetserver/client/cli/constants.py new file mode 100644 index 0000000..73fc190 --- /dev/null +++ b/src/packetserver/client/cli/constants.py @@ -0,0 +1,5 @@ +import os.path + +DEFAULT_APP_DIR = ".packetserver" +DEFAULT_CONFIG_FILE = "cli.ini" +DEFAULT_DB_FILE = "cli-client.zopedb" \ No newline at end of file diff --git a/src/packetserver/client/cli/db.py b/src/packetserver/client/cli/db.py new file mode 100644 index 0000000..e69de29 diff --git a/src/packetserver/client/cli/job.py b/src/packetserver/client/cli/job.py new file mode 100644 index 0000000..e69de29 diff --git a/src/packetserver/client/cli/message.py b/src/packetserver/client/cli/message.py new file mode 100644 index 0000000..e69de29 diff --git a/src/packetserver/client/cli/object.py b/src/packetserver/client/cli/object.py new file mode 100644 index 0000000..e69de29 diff --git a/src/packetserver/client/cli/server.py b/src/packetserver/client/cli/server.py new file mode 100644 index 0000000..e69de29 diff --git a/src/packetserver/client/cli/util.py b/src/packetserver/client/cli/util.py new file mode 100644 index 0000000..9e5f5f8 --- /dev/null +++ b/src/packetserver/client/cli/util.py @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/packetserver/client/jobs.py b/src/packetserver/client/jobs.py index 380a0ed..f9aea6e 100644 --- a/src/packetserver/client/jobs.py +++ b/src/packetserver/client/jobs.py @@ -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): diff --git a/src/packetserver/server/__init__.py b/src/packetserver/server/__init__.py index 7693db8..154f2fa 100644 --- a/src/packetserver/server/__init__.py +++ b/src/packetserver/server/__init__.py @@ -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() diff --git a/src/packetserver/server/jobs.py b/src/packetserver/server/jobs.py index 1766b93..ed03246 100644 --- a/src/packetserver/server/jobs.py +++ b/src/packetserver/server/jobs.py @@ -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_") diff --git a/src/packetserver/setup.py b/src/packetserver/setup.py new file mode 100644 index 0000000..4f90323 --- /dev/null +++ b/src/packetserver/setup.py @@ -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', + ], + }, +) \ No newline at end of file