Beginning a CLI client application using the client library.
This commit is contained in:
@@ -6,3 +6,4 @@ BTrees
|
|||||||
transaction
|
transaction
|
||||||
ZEO
|
ZEO
|
||||||
podman
|
podman
|
||||||
|
click
|
||||||
@@ -1 +1 @@
|
|||||||
VERSION="0.3.0-alpha"
|
VERSION="0.4.0-alpha"
|
||||||
45
src/packetserver/client/cli/__init__.py
Normal file
45
src/packetserver/client/cli/__init__.py
Normal 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()
|
||||||
0
src/packetserver/client/cli/bulletin.py
Normal file
0
src/packetserver/client/cli/bulletin.py
Normal file
24
src/packetserver/client/cli/config.py
Normal file
24
src/packetserver/client/cli/config.py
Normal 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
|
||||||
5
src/packetserver/client/cli/constants.py
Normal file
5
src/packetserver/client/cli/constants.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import os.path
|
||||||
|
|
||||||
|
DEFAULT_APP_DIR = ".packetserver"
|
||||||
|
DEFAULT_CONFIG_FILE = "cli.ini"
|
||||||
|
DEFAULT_DB_FILE = "cli-client.zopedb"
|
||||||
0
src/packetserver/client/cli/db.py
Normal file
0
src/packetserver/client/cli/db.py
Normal file
0
src/packetserver/client/cli/job.py
Normal file
0
src/packetserver/client/cli/job.py
Normal file
0
src/packetserver/client/cli/message.py
Normal file
0
src/packetserver/client/cli/message.py
Normal file
0
src/packetserver/client/cli/object.py
Normal file
0
src/packetserver/client/cli/object.py
Normal file
0
src/packetserver/client/cli/server.py
Normal file
0
src/packetserver/client/cli/server.py
Normal file
7
src/packetserver/client/cli/util.py
Normal file
7
src/packetserver/client/cli/util.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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:
|
def get_job_id(client: Client, bbs_callsign: str, job_id: int, get_data=True) -> JobWrapper:
|
||||||
req = Request.blank()
|
req = Request.blank()
|
||||||
req.path = f"job/{job_id}"
|
req.path = f"job/{job_id}"
|
||||||
|
req.set_var('data', get_data)
|
||||||
req.method = Request.Method.GET
|
req.method = Request.Method.GET
|
||||||
response = client.send_receive_callsign(req, bbs_callsign)
|
response = client.send_receive_callsign(req, bbs_callsign)
|
||||||
if response.status_code != 200:
|
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)
|
return JobWrapper(response.payload)
|
||||||
|
|
||||||
def get_user_jobs(): # TODO
|
def get_user_jobs(client: Client, bbs_callsign: str, get_data=True) -> list[JobWrapper]:
|
||||||
pass
|
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:
|
class JobSession:
|
||||||
def __init__(self, client: Client, bbs_callsign: str, default_timeout: int = 300, stutter: int = 2):
|
def __init__(self, client: Client, bbs_callsign: str, default_timeout: int = 300, stutter: int = 2):
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ from threading import Thread
|
|||||||
from packetserver.server.jobs import get_orchestrator_from_config, Job, JobStatus
|
from packetserver.server.jobs import get_orchestrator_from_config, Job, JobStatus
|
||||||
from packetserver.runner import RunnerStatus, RunnerFile, Orchestrator, Runner
|
from packetserver.runner import RunnerStatus, RunnerFile, Orchestrator, Runner
|
||||||
|
|
||||||
VERSION="0.2.0-alpha"
|
VERSION="0.4.0-alpha"
|
||||||
|
|
||||||
def init_bulletins(root: PersistentMapping):
|
def init_bulletins(root: PersistentMapping):
|
||||||
if 'bulletins' not in root:
|
if 'bulletins' not in root:
|
||||||
@@ -103,7 +103,7 @@ class Server:
|
|||||||
if 'runner' in conn.root.config['jobs_config']:
|
if 'runner' in conn.root.config['jobs_config']:
|
||||||
val = str(conn.root.config['jobs_config']['runner']).lower().strip()
|
val = str(conn.root.config['jobs_config']['runner']).lower().strip()
|
||||||
if val in ['podman']:
|
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.orchestrator = get_orchestrator_from_config(conn.root.config['jobs_config'])
|
||||||
|
|
||||||
self.app = pe.app.Application()
|
self.app = pe.app.Application()
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import gzip
|
|||||||
import tarfile
|
import tarfile
|
||||||
import time
|
import time
|
||||||
import json
|
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 packetserver.runner import Orchestrator, Runner, RunnerStatus, RunnerFile
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@@ -38,6 +38,7 @@ def get_orchestrator_from_config(cfg: dict) -> Union[Orchestrator, PodmanOrchest
|
|||||||
if 'runner' in cfg:
|
if 'runner' in cfg:
|
||||||
val = cfg['runner'].lower().strip()
|
val = cfg['runner'].lower().strip()
|
||||||
if val == "podman":
|
if val == "podman":
|
||||||
|
from packetserver.runner.podman import PodmanOrchestrator, PodmanOptions
|
||||||
image = cfg.get('image', 'debian')
|
image = cfg.get('image', 'debian')
|
||||||
opts = PodmanOptions(default_timeout=300, max_timeout=3600, image_name=image, max_active_jobs=5,
|
opts = PodmanOptions(default_timeout=300, max_timeout=3600, image_name=image, max_active_jobs=5,
|
||||||
container_keepalive=300, name_prefix="packetserver_")
|
container_keepalive=300, name_prefix="packetserver_")
|
||||||
|
|||||||
22
src/packetserver/setup.py
Normal file
22
src/packetserver/setup.py
Normal 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',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user