Improve cli and fix daemon main loop.

This commit is contained in:
sfigato 2019-02-05 15:11:31 +01:00 committed by blallo
parent d2148dfafb
commit 862598f597
Signed by: blallo
GPG Key ID: 0CBE577C9B72DC3F
2 changed files with 92 additions and 23 deletions

View File

@ -35,6 +35,9 @@ def _check_name(lifo_path: str, name: T.Optional[str]) -> str:
@click.group() @click.group()
@click.option("-d", "--debug", is_flag=True, default=False, help="Enable debug mode.") @click.option("-d", "--debug", is_flag=True, default=False, help="Enable debug mode.")
@click.option(
"--headless", is_flag=True, default=True, help="Start the clients in headless mode."
)
@click.option( @click.option(
"-v", "-v",
"--verbose", "--verbose",
@ -45,20 +48,25 @@ def _check_name(lifo_path: str, name: T.Optional[str]) -> str:
@click.option( @click.option(
"-f", "-f",
"--fifo", "--fifo",
type=click.Path(), type=click.STRING,
default="/tmp/bot_z.cmd", default="bot_z.cmd",
help="Path to the control fifo.", help="Path to the control fifo.",
) )
@click.option( @click.option(
"-w", "-w",
"--workdir", "--workdir",
type=click.Path(exists=True, readable=True, writable=True, resolve_path=True), type=click.Path(exists=True, readable=True, writable=True, resolve_path=True),
default="/tmp/", default="/tmp",
help="The working dir where to launch the daemon and where the lockfile is put.", help="The working dir where to launch the daemon and where the lockfile is put.",
) )
@click.pass_context @click.pass_context
def cli( def cli(
ctx: click.Context, debug: bool, verbose: bool, fifo: str, workdir: str ctx: click.Context,
debug: bool,
headless: bool,
verbose: bool,
fifo: str,
workdir: str,
) -> None: ) -> None:
""" """
Group cli. Group cli.
@ -67,8 +75,9 @@ def cli(
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
ctx.ensure_object(dict) ctx.ensure_object(dict)
ctx.obj["debug"] = debug ctx.obj["debug"] = debug
ctx.obj["headless"] = headless
ctx.obj["verbose"] = verbose ctx.obj["verbose"] = verbose
ctx.obj["fifo"] = fifo ctx.obj["fifo"] = os.path.join(workdir, fifo)
ctx.obj["workdir"] = workdir ctx.obj["workdir"] = workdir
ctx.obj["lifo"] = os.path.join(workdir, "botz_open_daemons.list") ctx.obj["lifo"] = os.path.join(workdir, "botz_open_daemons.list")
@ -124,10 +133,11 @@ def start_daemon_command(
base_uri=baseuri, base_uri=baseuri,
timeout=timeout, timeout=timeout,
proxy=proxy_tuple, proxy=proxy_tuple,
headless=not ctx.obj["debug"], headless=ctx.obj["headless"],
debug=ctx.obj["debug"], debug=ctx.obj["debug"],
foreground=foreground, foreground=foreground,
) )
if not foreground:
logger.info("Daemon started.") logger.info("Daemon started.")
@ -174,6 +184,19 @@ def start_command(ctx: click.Context, name: str) -> None:
logger.info("Start sent.") logger.info("Start sent.")
@cli.command("stop-daemon")
@click.pass_context
def stop_daemon_command(ctx: click.Context) -> None:
"""
Writes on the fifo. Invokes the stop of all the clients and the
subsequent shutdown of the daemon.
"""
logger.debug("Sending the stop-daemon command down the pipe: %s", ctx.obj["fifo"])
with open(ctx.obj["fifo"], "w") as fifo:
fifo.write(cmd_marshal(name="", cmd="stop-daemon"))
logger.info("Stop-daemon sent.")
@cli.command("stop") @cli.command("stop")
@click.option("-n", "--name", default=None, help="The instance to interact with.") @click.option("-n", "--name", default=None, help="The instance to interact with.")
@click.pass_context @click.pass_context
@ -186,7 +209,6 @@ def stop_command(ctx: click.Context, name: T.Optional[str]) -> None:
logging.info("Stopping instance: %s", name) logging.info("Stopping instance: %s", name)
with open(ctx.obj["fifo"], "w") as fifo: with open(ctx.obj["fifo"], "w") as fifo:
fifo.write(cmd_marshal(name=name, cmd="stop")) fifo.write(cmd_marshal(name=name, cmd="stop"))
fifo.flush()
logger.info("Stop sent.") logger.info("Stop sent.")
@ -201,7 +223,6 @@ def reload_command(ctx: click.Context, name: T.Optional[str]) -> None:
name = _check_name(ctx.obj["lifo"], name) name = _check_name(ctx.obj["lifo"], name)
with open(ctx.obj["fifo"], "w") as fifo: with open(ctx.obj["fifo"], "w") as fifo:
fifo.write(cmd_marshal(name=name, cmd="reload")) fifo.write(cmd_marshal(name=name, cmd="reload"))
fifo.flush()
logger.info("Reload sent.") logger.info("Reload sent.")
@ -212,7 +233,13 @@ def status_command(ctx: click.Context, name: T.Optional[str]) -> None:
""" """
Writes on the fifo. Queries the status. Writes on the fifo. Queries the status.
""" """
try:
name = _check_name(ctx.obj["lifo"], name) name = _check_name(ctx.obj["lifo"], name)
except IndexError:
if len(PLifo.All(ctx.obj["lifo"])) != 0:
raise
logger.warning("No clients registered.")
return
_status_command(ctx, name) _status_command(ctx, name)

View File

@ -36,6 +36,12 @@ logger.addHandler(syslog)
logger.debug("Init at debug") logger.debug("Init at debug")
class StopDaemon(Exception):
"""Auxiliary exception to help stop the program in daemon mode."""
pass
def start_daemon( def start_daemon(
name: str, name: str,
base_uri: str, base_uri: str,
@ -135,7 +141,12 @@ def daemon_process(
""" """
The daemon function. The daemon function.
""" """
if debug:
logger.setLevel(logging.DEBUG)
lifo_path = os.path.join(working_dir, "botz_open_daemons.list") lifo_path = os.path.join(working_dir, "botz_open_daemons.list")
if os.path.exists(lifo_path):
logger.warning("Lifo (%s) exists. Removing!", lifo_path)
os.remove(lifo_path)
starter = functools.partial( starter = functools.partial(
start_daemon, start_daemon,
@ -187,8 +198,11 @@ def daemon_process(
return return
with context: with context:
logger.debug("Started in background") logger.debug("Started in background")
try:
listen_commands(lifo_path, fifo_path, cmd_map) listen_commands(lifo_path, fifo_path, cmd_map)
except StopDaemon:
os.remove(lifo_path) os.remove(lifo_path)
return
def listen_client( def listen_client(
@ -234,24 +248,52 @@ def listen_commands(
for cmd_str in fifo: for cmd_str in fifo:
logger.debug("Command received in main loop: %s", cmd_str) logger.debug("Command received in main loop: %s", cmd_str)
cmd = cmd_unmarshal(cmd_str) cmd = cmd_unmarshal(cmd_str)
logger.debug("Cmd unmarshalled: %s", cmd)
if cmd["cmd"] == "start":
if cmd["name"] not in client_chans: if cmd["name"] not in client_chans:
client_chans[cmd["name"]] = queue.Queue(1) client_chans[cmd["name"]] = queue.Queue(1)
logger.debug('Queue created for "%s".', cmd["name"])
if cmd["name"] in PLifo.All(lifopath): if cmd["name"] in PLifo.All(lifopath):
logger.warning("Name %s is yet used. Not proceeding.", name) logger.warning("Name %s is yet used. Not proceeding.", name)
continue continue
logger.debug(
'"%s" being pushed onto "%s"...', cmd["name"], lifopath
)
PLifo.Push(lifopath, cmd["name"]) PLifo.Push(lifopath, cmd["name"])
logger.debug("Client %s has been newly created.", cmd["name"]) logger.debug("Client %s has been newly created.", cmd["name"])
logger.debug("Opening client: %s", cmd["name"]) chan = client_chans.get(cmd["name"], None)
q = client_chans[cmd["name"]]
if cmd["cmd"] == "start":
logger.debug("Starting new client in a thread...") logger.debug("Starting new client in a thread...")
client = threading.Thread( client = threading.Thread(
name=cmd["name"], name=cmd["name"],
target=functools.partial( target=functools.partial(
listen_client, name=cmd["name"], chan=q, cmd_map=cmd_map listen_client, name=cmd["name"], chan=chan, cmd_map=cmd_map
), ),
) )
client.start() client.start()
logger.debug('Client "%s" started.', cmd["name"]) logger.debug('Client "%s" started.', cmd["name"])
continue continue
q.put(cmd)
if cmd["cmd"] == "stop-daemon":
stop_all(client_chans)
logger.info("Daemon clients stopped. Exiting...")
raise StopDaemon
logger.debug("Opening client: %s", cmd["name"])
chan = client_chans.get(cmd["name"], None)
if chan is None:
logger.warning(
'No client found with name "%s". Skipping.', cmd["name"]
)
continue
chan.put(cmd)
def stop_all(client_chans: T.Dict[str, queue.Queue]) -> None:
"""
Send the stop command to all the clients.
"""
for name, chan in client_chans.items():
logger.debug('Stopping "%s"...', name)
chan.put({"name": name, "cmd": "stop"})
logger.info("Stopped %s.", name)