Unify the python API class

This commit is contained in:
Hamish Coleman 2021-11-01 15:59:39 +00:00
parent 7f163858cf
commit fbad36705e

View File

@ -23,102 +23,31 @@ import base64
from http import HTTPStatus from http import HTTPStatus
import os
import sys
import importlib.machinery
import importlib.util
class JsonUDP():
"""encapsulate communication with the edge"""
def __init__(self, port): def import_filename(modulename, filename):
self.address = "127.0.0.1" # look in the same dir as this script
self.port = port pathname = os.path.join(os.path.dirname(os.path.abspath(__file__)),
self.tag = 0 filename)
self.key = None loader = importlib.machinery.SourceFileLoader(modulename, pathname)
self.debug = False spec = importlib.util.spec_from_loader(modulename, loader)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) module = importlib.util.module_from_spec(spec)
self.sock.settimeout(1)
def _next_tag(self): try:
tagstr = str(self.tag) loader.exec_module(module)
self.tag = (self.tag + 1) % 1000 except FileNotFoundError as e:
return tagstr print("Script {} not found".format(pathname), file=sys.stderr)
sys.exit(1)
return module
def _cmdstr(self, msgtype, cmdline):
"""Create the full command string to send"""
tagstr = self._next_tag()
options = [tagstr] # We share the implementation of the RPC class with the n2n-ctl script. We
if self.key is not None: # cannot just import the module as 'n2n-ctl' has a dash in its name :-(
options += ['1'] # Flags set for auth key field JsonUDP = import_filename('n2nctl', 'n2n-ctl').JsonUDP
options += [self.key]
optionsstr = ':'.join(options)
return tagstr, ' '.join((msgtype, optionsstr, cmdline))
def _rx(self, tagstr):
"""Wait for rx packets"""
# TODO: there are no timeouts with any of the recv calls
data, _ = self.sock.recvfrom(1024)
data = json.loads(data.decode('utf8'))
# TODO: We assume the first packet we get will be tagged for us
# and be either an "error" or a "begin"
assert(data['_tag'] == tagstr)
if data['_type'] == 'error':
raise ValueError('Error: {}'.format(data['error']))
assert(data['_type'] == 'begin')
# Ideally, we would confirm that this is our "begin", but that
# would need the cmd passed into this method, and that would
# probably require parsing the cmdline passed to us :-(
# assert(data['cmd'] == cmd)
result = list()
error = None
while True:
data, _ = self.sock.recvfrom(1024)
data = json.loads(data.decode('utf8'))
if data['_tag'] != tagstr:
# this packet is not for us, ignore it
continue
if data['_type'] == 'error':
# we still expect an end packet, so save the error
error = ValueError('Error: {}'.format(data['error']))
continue
if data['_type'] == 'end':
if error:
raise error
return result
if data['_type'] != 'row':
raise ValueError('Unknown data type {} from '
'edge'.format(data['_type']))
# remove our boring metadata
del data['_tag']
del data['_type']
if self.debug:
print(data)
result.append(data)
def _call(self, msgtype, cmdline):
"""Perform a rpc call"""
tagstr, msgstr = self._cmdstr(msgtype, cmdline)
self.sock.sendto(msgstr.encode('utf8'), (self.address, self.port))
return self._rx(tagstr)
def read(self, cmdline):
return self._call('r', cmdline)
def write(self, cmdline):
return self._call('w', cmdline)
pages = { pages = {