diff --git a/config/propagator.cfg.example b/config/propagator.cfg.example index f96e72a..3491bbe 100644 --- a/config/propagator.cfg.example +++ b/config/propagator.cfg.example @@ -1,9 +1,10 @@ [general] repobase=/srv/git +logs_dir=~/.propagator/logs [smtp] host=localhost port=25 auth=no user=none pass=none diff --git a/propagator/remoteslave/__init__.py b/propagator/remoteslave/__init__.py index e8ca1a2..3bdabb7 100644 --- a/propagator/remoteslave/__init__.py +++ b/propagator/remoteslave/__init__.py @@ -1,71 +1,47 @@ # This file is part of Propagator, a KDE Sysadmin Project # # Copyright 2015 Boudhayan Gupta # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of KDE e.V. (or its successor approved by the # membership of KDE e.V.) nor the names of its contributors may be used # to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os import sys import argparse -import importlib import logbook -from propagator import VERSION from propagator.remoteslave import amqp +from propagator.remoteslave.slavecore import SlaveCore def cmdline_process(): parser = argparse.ArgumentParser(description = "Run a Propagator Remote Slave") parser.add_argument("remotename", type = str, help = "Start a slave for this remote type") args = parser.parse_args() return args.remotename def main(): - # initialise logging logbook.StreamHandler(sys.stdout).push_application() - log = logbook.Logger("RemoteSlave-{}".format(str(os.getpid()))) - - # process the command line and figure out which remote plugin we want to run remote_name = cmdline_process() - log.info("This is KDE Propagator {} - Remote Slave".format(VERSION)) - log.info(" Starting...") - log.info("remote plugin requested: {}".format(remote_name)) - plugin_name = "propagator.remotes.{}".format(remote_name) - try: - remote = importlib.import_module(plugin_name) - except ImportError: - log.critical("remote plugin not found: {}".format(remote_name)) - log.critical("this slave will now exit.") - sys.exit(1) - log.info("loaded remote plugin: {}".format(remote_name)) - - print(" [*] Waiting for logs. To exit press CTRL+C") - #channel = amqp.create_channel_consumer("logs") - #channel.basic_consume(callback, amqp.queue_name_for_slave("logs")) - #channel.start_consuming() - -def callback(ch, method, properties, body): - ch.basic_ack(method.delivery_tag) - print(" [x] %r" % body) + worker = SlaveCore(remote_name) + worker() diff --git a/propagator/remoteslave/slavecore.py b/propagator/remoteslave/slavecore.py new file mode 100644 index 0000000..28a39bc --- /dev/null +++ b/propagator/remoteslave/slavecore.py @@ -0,0 +1,95 @@ +# This file is part of Propagator, a KDE Sysadmin Project +# +# Copyright 2015 Boudhayan Gupta +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of KDE e.V. (or its successor approved by the +# membership of KDE e.V.) nor the names of its contributors may be used +# to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys +import os +import signal +import logbook +import importlib + +from propagator import VERSION as version +from propagator.core.config import config_general +from propagator.remoteslave import amqp + +class SlaveCore(object): + def __init__(self, slave_name): + # set up the logger as early as possible + self.log = logbook.Logger("RemoteSlave-{}".format(str(os.getpid()))) + self.log.info("This is KDE Propagator {} - Remote Slave".format(version)) + self.log.info(" Starting...") + + # create the operations log handler and load in the slave + self.opslog = self.init_slave_logger(slave_name) + self.remote = self.init_slave_module(slave_name) + + # set up the amqp channel, and bind it to the consumer callback + self.channel = amqp.create_channel_consumer(slave_name) + self.channel.basic_consume(self.process_single_message, amqp.queue_name_for_slave(slave_name)) + + def __call__(self): + # set up sigterm to also raise KeyboardInterrupt + signal.signal(signal.SIGTERM, signal.getsignal(signal.SIGINT)) + + # run the main loop + self.log.info("listening for new tasks...") + try: + self.channel.start_consuming() + except KeyboardInterrupt: + self.channel.stop_consuming() + self.log.info("slave is shutting down...") + + def init_slave_logger(self, slave_name): + # get the logs directory and ensure that it exists + default_logdir = os.path.expanduser("~/.propagator/logs") + logdir = config_general.get("logs_dir", default_logdir) + if not os.path.isdir(logdir): + os.makedirs(logdir) + + # fire up a logger with its own handler to redirect to the file + logpath = os.path.join(logdir, "remote.{}.log".format(slave_name)) + logger = logbook.Logger("slave-{}".format(slave_name)) + logger.handlers.append(logbook.FileHandler(logpath)) + + # done, return logger + return logger + + def init_slave_module(self, slave_name): + self.log.info("remote plugin requested: {}".format(slave_name)) + plugin_name = "propagator.remotes.{}".format(slave_name) + try: + self.remote = importlib.import_module(plugin_name) + except ImportError: + self.log.critical("remote plugin not found: {}".format(slave_name)) + self.log.critical("this slave will now exit.") + sys.exit(1) + self.log.info("loaded remote plugin: {}".format(slave_name)) + + def process_single_message(self, channel, method, properties, body): + print("[*] Body: {}".format(body)) + channel.basic_ack(method.delivery_tag)