fix reload

This commit is contained in:
2020-11-08 00:47:10 +01:00
parent 380045d6bf
commit 8e36dbc4a5
3 changed files with 47 additions and 36 deletions

View File

@@ -18,7 +18,6 @@ import argparse
import asyncio import asyncio
import logging import logging
import logging.handlers import logging.handlers
import pyinotify
import signal import signal
import sys import sys
@@ -28,18 +27,6 @@ from pyinotifyd._install import install, uninstall
__version__ = "0.0.2" __version__ = "0.0.2"
def get_pyinotifyd_from_config(name, config_file):
config = {}
exec(f"from {name}.scheduler import *", config)
with open(config_file, "r") as c:
exec(c.read(), globals(), config)
daemon = config[f"{name}"]
assert isinstance(daemon, Pyinotifyd), \
f"{name}: expected {type(Pyinotifyd)}, " \
f"got {type(daemon)}"
return daemon
class Pyinotifyd: class Pyinotifyd:
def __init__(self, watches=[], shutdown_timeout=30, logname="daemon"): def __init__(self, watches=[], shutdown_timeout=30, logname="daemon"):
self.set_watches(watches) self.set_watches(watches)
@@ -47,8 +34,6 @@ class Pyinotifyd:
logname = (logname or __name__) logname = (logname or __name__)
self._log = logging.getLogger(logname) self._log = logging.getLogger(logname)
self._loop = asyncio.get_event_loop() self._loop = asyncio.get_event_loop()
self._notifiers = []
self._wm = pyinotify.WatchManager()
def set_watches(self, watches): def set_watches(self, watches):
if not isinstance(watches, list): if not isinstance(watches, list):
@@ -58,7 +43,8 @@ class Pyinotifyd:
assert isinstance(watch, Watch), \ assert isinstance(watch, Watch), \
f"watches: expected {type(Watch)}, got {type(watch)}" f"watches: expected {type(Watch)}, got {type(watch)}"
self._watches = watches self._watches = []
self._watches.extend(watches)
def add_watch(self, *args, **kwargs): def add_watch(self, *args, **kwargs):
self._watches.append(Watch(*args, **kwargs)) self._watches.append(Watch(*args, **kwargs))
@@ -73,36 +59,48 @@ class Pyinotifyd:
if not loop: if not loop:
loop = self._loop loop = self._loop
self._log.info("starting")
if len(self._watches) == 0: if len(self._watches) == 0:
self._log.warning( self._log.warning(
"no watches configured, the daemon will not do anything") "no watches configured, the daemon will not do anything")
for watch in self._watches: for watch in self._watches:
self._log.info( self._log.info(
f"start listening for inotify events on '{watch.path()}'") f"start listening for inotify events on '{watch.path()}'")
self._notifiers.append(watch.event_notifier(self._wm, loop)) watch.start(loop)
def stop(self): def stop(self):
self._log.info("stop listening for inotify events") for watch in self._watches:
for notifier in self._notifiers: self._log.info(f"stop listening for inotify events on '{watch.path()}'")
notifier.stop() watch.stop()
self._notifiers = []
return self._shutdown_timeout return self._shutdown_timeout
def get_pyinotifyd_from_config(name, config_file):
config = {}
exec(f"from {name} import Pyinotifyd", {}, config)
exec(f"from {name}.scheduler import *", {}, config)
exec(f"from {name}.watch import EventMap, Watch", {}, config)
with open(config_file, "r") as c:
exec(c.read(), {}, config)
daemon = config[f"{name}"]
assert isinstance(daemon, Pyinotifyd), \
f"{name}: expected {type(Pyinotifyd)}, " \
f"got {type(daemon)}"
return daemon
class DaemonInstance: class DaemonInstance:
def __init__(self, instance, logname="daemon"): def __init__(self, instance, logname="daemon"):
self._instance = instance self._instance = instance
self._shutdown = False self._shutdown = False
self._log = logging.getLogger(logname) self._log = logging.getLogger(logname)
self._timeout = None
def start(self): def start(self):
self._instance.start() self._instance.start()
def stop(self): def stop(self):
self._timeout = self._instance.stop() return self._instance.stop()
def _get_pending_tasks(self): def _get_pending_tasks(self):
return [t for t in asyncio.all_tasks() return [t for t in asyncio.all_tasks()
@@ -110,23 +108,23 @@ class DaemonInstance:
async def shutdown(self, signame): async def shutdown(self, signame):
if self._shutdown: if self._shutdown:
self._log.info( self._log.warning(
f"got signal {signame}, but shutdown already in progress") f"got signal {signame}, but shutdown already in progress")
return return
self._shutdown = True self._shutdown = True
self._log.info(f"got signal {signame}, shutdown") self._log.info(f"got signal {signame}, shutdown")
self.stop() timeout = self.stop()
pending = self._get_pending_tasks() pending = self._get_pending_tasks()
if pending: if pending:
if self._timeout: if timeout:
future = asyncio.gather(*pending) future = asyncio.gather(*pending)
self._log.info( self._log.info(
f"wait {self._timeout} seconds for {len(pending)} " f"wait {timeout} seconds for {len(pending)} "
f"remaining task(s) to complete") f"remaining task(s) to complete")
try: try:
await asyncio.wait_for(future, self._timeout) await asyncio.wait_for(future, timeout)
pending = [] pending = []
except asyncio.TimeoutError: except asyncio.TimeoutError:
future.cancel() future.cancel()
@@ -149,7 +147,7 @@ class DaemonInstance:
self._shutdown = False self._shutdown = False
self._log.info("shutdown complete") self._log.info("shutdown complete")
async def reload(self, signame, name, config): async def reload(self, signame, name, config, debug=False):
if self._shutdown: if self._shutdown:
self._log.info( self._log.info(
f"got signal {signame}, but shutdown already in progress") f"got signal {signame}, but shutdown already in progress")
@@ -161,6 +159,8 @@ class DaemonInstance:
except Exception as e: except Exception as e:
logging.exception(f"unable to reload config '{config}': {e}") logging.exception(f"unable to reload config '{config}': {e}")
else: else:
if debug:
logging.getLogger().setLevel(logging.DEBUG)
self.stop() self.stop()
self._instance = instance self._instance = instance
self.start() self.start()
@@ -258,6 +258,7 @@ def main():
if args.debug: if args.debug:
root_logger.setLevel(loglevel) root_logger.setLevel(loglevel)
formatter = logging.Formatter( formatter = logging.Formatter(
f"%(asctime)s - {myname}/%(name)s - %(levelname)s - %(message)s") f"%(asctime)s - {myname}/%(name)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter) ch.setFormatter(formatter)
@@ -272,7 +273,7 @@ def main():
loop.add_signal_handler( loop.add_signal_handler(
getattr(signal, "SIGHUP"), getattr(signal, "SIGHUP"),
lambda: asyncio.ensure_future( lambda: asyncio.ensure_future(
daemon.reload(signame, myname, args.config))) daemon.reload("SIGHUP", myname, args.config, args.debug)))
daemon.start() daemon.start()
loop.run_forever() loop.run_forever()

View File

@@ -276,7 +276,7 @@ class FileManagerScheduler(TaskScheduler):
if os.path.exists(dst): if os.path.exists(dst):
raise RuntimeError( raise RuntimeError(
f"unable to move file from '{path} " f"unable to move file from '{path} "
f"to '{dst}', dstination path exists already") f"to '{dst}', destination path exists already")
dst_dir = os.path.dirname(dst) dst_dir = os.path.dirname(dst)
if not os.path.isdir(dst_dir) and rule.auto_create: if not os.path.isdir(dst_dir) and rule.auto_create:

View File

@@ -98,15 +98,25 @@ class Watch:
self._rec = rec self._rec = rec
self._auto_add = auto_add self._auto_add = auto_add
self._watch_manager = pyinotify.WatchManager()
self._notifier = None
def path(self): def path(self):
return self._path return self._path
def event_notifier(self, wm, loop=asyncio.get_event_loop()): def start(self, loop=asyncio.get_event_loop()):
handler = pyinotify.ProcessEvent() handler = pyinotify.ProcessEvent()
for flag, values in self._event_map.items(): for flag, values in self._event_map.items():
setattr(handler, f"process_{flag}", _TaskList(values).execute) setattr(handler, f"process_{flag}", _TaskList(values).execute)
wm.add_watch(self._path, pyinotify.ALL_EVENTS, rec=self._rec, self._watch_manager.add_watch(self._path, pyinotify.ALL_EVENTS,
auto_add=self._auto_add, do_glob=True) rec=self._rec, auto_add=self._auto_add,
do_glob=True)
return pyinotify.AsyncioNotifier(wm, loop, default_proc_fun=handler) self._notifier = pyinotify.AsyncioNotifier(
self._watch_manager, loop, default_proc_fun=handler)
def stop(self):
self._notifier.stop()
self._notifier = None