From 7bd5b53686e103e9219ddc81fd870d2d8d8fa074 Mon Sep 17 00:00:00 2001 From: Bruce Duncan Date: Sun, 19 Feb 2023 21:10:46 +0000 Subject: [PATCH 1/2] Add a property for the config filename --- src/emonhub_setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/emonhub_setup.py b/src/emonhub_setup.py index 05467538..fae4d9c8 100644 --- a/src/emonhub_setup.py +++ b/src/emonhub_setup.py @@ -96,6 +96,10 @@ def __init__(self, filename): raise EmonHubSetupInitError( 'Configuration file error - section: ' + str(e)) + @property + def filename(self): + return self._filename + def check_settings(self): """Check settings From 78a069ba54a45a21793ebaa06504dd840fad4195 Mon Sep 17 00:00:00 2001 From: Bruce Duncan Date: Sun, 19 Feb 2023 21:17:06 +0000 Subject: [PATCH 2/2] Use inotify for config file changes Instead of polling and reparsing the config file every 0.2s, use inotify to reparse only when the file is written to, moved into place, or symlinked. Signed-off-by: Bruce Duncan --- src/emonhub.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/emonhub.py b/src/emonhub.py index e9a5e1fa..e9b3797e 100755 --- a/src/emonhub.py +++ b/src/emonhub.py @@ -19,8 +19,10 @@ import pprint import glob import os +import os.path import getpass from collections import defaultdict +import inotify.adapters import emonhub_setup as ehs import emonhub_coder as ehc @@ -108,13 +110,36 @@ def run(self): # Initialise thread restart counters restart_count = defaultdict(int) + # Watch for config file changes + inotify_setup = inotify.adapters.Inotify() + inotify_setup.add_watch(os.path.dirname(self._setup.filename)) + # config file could be a symlink, so watch that for writes too + inotify_setup.add_watch(os.path.realpath(self._setup.filename)) + # Until asked to stop while not self._exit: # Run setup and update settings if modified - self._setup.run() - if self._setup.check_settings(): - self._update_settings(self._setup.settings) + events = list(inotify_setup.event_gen(yield_nones=False, timeout_s=0.2)) + for (_, type_names, path, filename) in events: + self._log.debug('type_names=%s filename=%s', type_names, filename) + # If a symlink is created we have to watch that too + if 'IN_CREATE' in type_names: + try: + target = os.readlink(os.path.join(path, filename)) + inotify_setup.add_watch(target) + except OSError: + pass + + # We'll reload for any file changes in this directory. There is probably only one anyway. + if 'IN_CLOSE_WRITE' not in type_names and 'IN_MOVED_TO' not in type_names and 'IN_CREATE' not in type_names: + continue + + self._log.info('Reloading setup because type_names=%s for filename=%s', type_names, filename) + self._setup.run() + if self._setup.check_settings(): + self._update_settings(self._setup.settings) + self._log.info('Reload complete') # Auto conf populate nodelist if eha.auto_conf_enabled and ehc.nodelist != self._setup.settings['nodes']: @@ -158,9 +183,6 @@ def run(self): restart_count[name] += 1 self._update_settings(self._setup.settings) - # Sleep until next iteration - time.sleep(0.2) - def close(self): """Close hub. Do some cleanup before leaving."""