#!/usr/bin/python3 import cbor2 import os import pathlib import socket import subprocess import sys DEFAULT_RUN_PATH = pathlib.Path(f"/run/user/{os.getuid()}/flock") handlers = {} class HandlerError(Exception): pass def handler(fun): items = fun.__name__.split("_") hx = handlers while len(items) > 1: if (s := items.pop(0)) not in hx: hx[s] = dict() hx = hx[s] if items[0] in hx: raise Exception(f"Duplicate handler {fun.__name__}") hx[items[0]] = fun class HypervisorNonexistentError(HandlerError): def __init__(self, *args, **kwargs): return super().__init__("Hypervisor not found", *args, **kwargs) class HypervisorStaleError(HandlerError): def __init__(self, *args, **kwargs): return super().__init__("Hypervisor stale", *args, **kwargs) def connect(where: pathlib.Path): if not where.exists(): raise HypervisorNonexistentError() client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: client.connect(bytes(where)) except ConnectionRefusedError: raise HypervisorStaleError() return client def ctl_path(name: str): return DEFAULT_RUN_PATH / f"{name}.ctl" def msg(name: str, data: dict): try: ctl = connect(ctl_path(name)) except HypervisorNonexistentError as e: e.add_note(f"Failed to send message {data} to {name}") raise e ctl.sendall(cbor2.dumps(data)) return cbor2.loads(ctl.recv(1024)) @handler def start(name: str): DEFAULT_RUN_PATH.mkdir(parents=True, exist_ok=True) try: connect(ctl := ctl_path(name)) raise HandlerError("Hypervisor already exists") except HypervisorNonexistentError: pass subprocess.run(["./flock-sim", "-s", ctl, name]) @handler def stop(name: str): for k,v in msg(name, { 0: None }).items(): assert(k == -1) assert(v == "OK") @handler def cleanup(name: str): try: connect(ctl := ctl_path(name)) raise HandlerError("Hypervisor is not stale") except HypervisorStaleError: ctl.unlink() @handler def container_start(hypervisor: str, name: str): print(f"start a machine {name} in {hypervisor}") try: binname = sys.argv.pop(0) except Exception as e: raise RuntimeError from e def usage(name: str): print( f"Usage: {name} ", f"", f"Available commands:", f"\tstart start Flock hypervisor", f"\t creates .ctl in {DEFAULT_RUN_PATH}", f"\tstop stop Flock hypervisor", f"\tcleanup cleanup the control socket left behind a stale hypervisor", f"\tcontainer start start virtual machine", f"\tcontainer stop stop virtual machine", f"\ttelnet run telnet to this machine", sep="\n") cmd = [] hx = handlers while type(hx) is dict: try: hx = hx[cx := sys.argv.pop(0)] except (IndexError, KeyError): usage(binname) exit(2) cmd.append(cx) try: hx(*sys.argv) except HandlerError as e: print(f"Error: {e}") # raise e exit(1) except TypeError as e: usage(binname) print() print(f"Error in command {' '.join(cmd)}.") raise RuntimeError from e exit(2)