#!/usr/bin/env python3 # depends on: # python 3.4 (included in Raspbian Jessie) # APScheduler import argparse import os from datetime import datetime import toml import shepherd.scheduler import shepherd.config import shepherd.plugin import shepherd.control # 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 define_core_config(confdef): confdef.add_def("id", shepherd.config.StringDef()) confdef.add_def("hostname", shepherd.config.StringDef(default="", optional=True)) confdef.add_def("plugins", shepherd.config.StringArrayDef()) confdef.add_def("plugin_dir", shepherd.config.StringDef()) confdef.add_def("root_dir", shepherd.config.StringDef()) confdef.add_def("conf_edit_path", shepherd.config.StringDef()) confdef.add_def("control_server", shepherd.config.StringDef()) confdef.add_def("api_key", shepherd.config.StringDef()) def load_config(config_path,load_editconf): # Load config from default location confman = shepherd.config.ConfigManager() confman.load(os.path.expanduser(config_path)) # Create core confdef and populate it core_confdef = shepherd.config.ConfDefinition() define_core_config(core_confdef) # attempt to retrive core config and validate it core_conf = confman.get_config("shepherd", core_confdef) edit_confman = None conf_edit_message = None if load_editconf: # Check for an edit_conf file, and try to load it and plugin configs 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) plugin_classes = shepherd.plugin.find_plugins( core_edit_conf["plugins"]) plugin_configs = edit_confman.get_plugin_configs(plugin_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 for plugins if confman is not edit_confman: plugin_classes = shepherd.plugin.find_plugins(core_conf["plugins"]) plugin_configs = confman.get_plugin_configs(plugin_classes) # If no editconf file was found, write out the current config as a template if (conf_edit_message is None) and load_editconf: confman.dump_to_file(os.path.expanduser(core_conf["conf_edit_path"]), "Config generated at:" + str(datetime.now())) return (core_conf, plugin_classes, plugin_configs) 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") argparser.add_argument('-e', '--noedit',help="Disable the editable config temporarily", action="store_true", default=False) argparser.add_argument("-t", "--test", help="Test and interface function of the from 'plugin:function'", default=None) args = argparser.parse_args() confman = shepherd.config.ConfigManager() confman.load(os.path.expanduser(args.configfile)) (core_conf, plugin_classes, plugin_configs) = load_config(args.configfile, not args.noedit) if args.test is None: shepherd.control.init_control(core_conf, plugin_configs) shepherd.scheduler.init_scheduler(core_conf) shepherd.plugin.init_plugins(plugin_classes, plugin_configs, core_conf) if args.test is None: shepherd.control.post_logs() shepherd.scheduler.restore_jobs() print(str(datetime.now())) if args.test is not None: (test_plugin, test_func) = args.test.split(':') func = getattr(shepherd.plugin.plugin_functions[test_plugin], test_func) print(func()) return print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C')) try: shepherd.scheduler.start() except (KeyboardInterrupt, SystemExit): pass if __name__ == "__main__": main()