#!/usr/bin/env python3 # depends on: # python 3.4 (included in Raspbian Jessie) # APScheduler import sys import argparse import os import toml import shepherd.config import shepherd.module from apscheduler.schedulers.blocking import BlockingScheduler from datetime import datetime from types import SimpleNamespace # Future implementations of checking config differences should be done on # the hash of the nested conf dict, so comments shouldn't affect this. # save old config to somewhere in the shepherd root dir - probably need to # implement a TOML writer in the config module. # later on, there's going to be an issue with a new config being applied # remotely, then the system restarting, and an old edit in /boot being # applied over the top... # Fix this by saving the working config to /boot when new config applied # remotely. def load_config(config_path): confman = shepherd.config.ConfigManager() confman.load(os.path.expanduser(config_path)) core_conf = confman.get_config("shepherd", core_confdef()) # Check for an edit_conf file, and try to load it try: edit_confman = shepherd.config.ConfigManager() edit_confman.load(os.path.expanduser(core_conf["conf_edit_path"])) core_edit_conf = edit_confman.get_config("shepherd", core_confdef()) mod_classes = shepherd.module.find_modules(core_edit_conf["modules"]) mod_configs = edit_confman.get_module_configs(mod_classes) except FileNotFoundError: conf_edit_message = None except shepherd.config.InvalidConfigError as e: conf_edit_message = "Invalid config.\n " + str(e.args) except toml.TomlDecodeError as e: conf_edit_message = "TOML syntax error.\n" + str(e) except Exception: conf_edit_message = "Error processing new config" else: conf_edit_message = ("Successfully applied this config at:" + str(datetime.now())) confman = edit_confman core_conf = core_edit_conf if conf_edit_message is not None: shepherd.config.update_toml_message( os.path.expanduser(core_conf["conf_edit_path"]), conf_edit_message) # if editconf failed, load current config if confman is not edit_confman: mod_classes = shepherd.module.find_modules(core_conf["modules"]) mod_configs = confman.get_module_configs(mod_classes) # If no editconf file was found, write out the current config as a template if conf_edit_message is None: confman.dump_to_file(os.path.expanduser(core_conf["conf_edit_path"]), "Config generated at:" + str(datetime.now())) return (core_conf, mod_classes, mod_configs) def core_confdef(): confdef = shepherd.config.ConfDefinition() confdef.add_def("id", shepherd.config.StringDef()) confdef.add_def("modules", shepherd.config.StringArrayDef()) confdef.add_def("root_dir", shepherd.config.StringDef()) confdef.add_def("conf_edit_path", shepherd.config.StringDef()) return confdef class ShepherdInterface(shepherd.module.Interface): def __init__(self, scheduler, config): super().__init__(None) self.id = config["id"] self.root_dir = config["root_dir"] self.scheduler = scheduler def main(): argparser = argparse.ArgumentParser(description="Keep track of a mob " "of roaming Pis") argparser.add_argument("configfile", nargs='?', metavar="configfile", help="Path to configfile", default="shepherd.toml") args = argparser.parse_args() confman = shepherd.config.ConfigManager() confman.load(os.path.expanduser(args.configfile)) core_conf = confman.get_config("shepherd", core_confdef()) breakpoint() (core_conf, mod_classes, mod_configs) = load_config(args.configfile) scheduler = BlockingScheduler() core_interface = ShepherdInterface(scheduler, core_conf) # get validated config values for modules, then instantiate them modules = {} mod_interfaces = {} for name, mod_class in mod_classes.items(): modules[name] = mod_class(mod_configs[name], core_interface) mod_interfaces[name] = modules[name].interface # run post init after all modules are loaded to allow them to hook in to # each other mod_interfaces = SimpleNamespace(**mod_interfaces) for module in modules.values(): module.init_other_interfaces(mod_interfaces) print(str(datetime.now())) print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C')) try: scheduler.start() except (KeyboardInterrupt, SystemExit): pass if __name__ == "__main__": main()