You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
shepherd-agent/shepherd/core.py

139 lines
4.6 KiB

#!/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()