From fbcb42601738955ce986ef5f11fa65a9f5228c58 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 10:35:53 +0100 Subject: [PATCH 01/14] better configfile locator --- threebot_worker/worker.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/threebot_worker/worker.py b/threebot_worker/worker.py index 71bf415..1afaedf 100755 --- a/threebot_worker/worker.py +++ b/threebot_worker/worker.py @@ -16,16 +16,14 @@ _default_logfile = '/etc/3bot/3bot.log' _default_loglevel = 'ERROR' -if os.environ['VIRTUAL_ENV']: +if os.environ.get('VIRTUAL_ENV', False): current_path = os.path.realpath(os.environ['VIRTUAL_ENV']) - local_configfile = os.path.join(current_path, 'config.ini') + configfile = os.path.join(current_path, 'config.ini') pidfile = os.path.join(current_path, '3bot-worker.pid') Config = ConfigParser.ConfigParser() -if local_configfile and os.path.isfile(local_configfile): - Config.read(local_configfile) -elif os.path.isfile(configfile): +if os.path.isfile(configfile): Config.read(configfile) else: print "No configfile found in: '%s'" % configfile @@ -43,7 +41,6 @@ sys.exit(2) try: - # Read secret key - never share yours! SECRET_KEY = Config.get('3bot-settings', 'SECRET_KEY') except: print "Invalid configfile in: '%s'. Could not find SECRET_KEY declaration" % configfile @@ -72,9 +69,11 @@ else: LOGLEVEL = logging.CRITICAL -logging.basicConfig(filename=LOGFILE, - level=LOGLEVEL, - format='%(asctime)s %(message)s') +logging.basicConfig( + filename=LOGFILE, + level=LOGLEVEL, + format='%(asctime)s %(message)s', +) if len(sys.argv) == 2: if 'start' == sys.argv[1] or 'restart' == sys.argv[1]: @@ -199,7 +198,7 @@ def run(self): logging.error("Could not decrypt received message") if self.debug_mode: raise Exception("Could not decrypt message") - #server.send("", flags=FLAGS) + # server.send("", flags=FLAGS) if __name__ == "__main__": From d423f47947821fc5e1981da26a22ce2713d5e0a7 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 12:53:18 +0100 Subject: [PATCH 02/14] use threebot-worker file as script --- setup.py | 2 +- threebot_worker/threebot-worker | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 threebot_worker/threebot-worker diff --git a/setup.py b/setup.py index 6c42b34..ff24a5f 100644 --- a/setup.py +++ b/setup.py @@ -24,5 +24,5 @@ def long_description(): packages=find_packages(), include_package_data=True, install_requires=open('requirements.txt').read().split('\n'), - scripts=['threebot_worker/worker.py'], + scripts=['threebot_worker/threebot-worker'], ) diff --git a/threebot_worker/threebot-worker b/threebot_worker/threebot-worker new file mode 100644 index 0000000..e9a25f5 --- /dev/null +++ b/threebot_worker/threebot-worker @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +This file is used as a proxy to worker.py +We use 'threebot-worker' as a script in setup.py +""" + +from worker import * # NOQA From ea520c84a40689df0cd7366a6243cc4b14d4d587 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 13:24:32 +0100 Subject: [PATCH 03/14] gather all 3bot directories in ~/3bot/ --- threebot_worker/worker.py | 88 +++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/threebot_worker/worker.py b/threebot_worker/worker.py index 1afaedf..7a3b63b 100755 --- a/threebot_worker/worker.py +++ b/threebot_worker/worker.py @@ -11,22 +11,28 @@ import threebot_crypto from threebot_worker.daemon import Daemon -configfile = '/etc/3bot/config.ini' -pidfile = '/tmp/3bot-worker.pid' -_default_logfile = '/etc/3bot/3bot.log' -_default_loglevel = 'ERROR' +PROTOCOL = 'tcp' -if os.environ.get('VIRTUAL_ENV', False): - current_path = os.path.realpath(os.environ['VIRTUAL_ENV']) - configfile = os.path.join(current_path, 'config.ini') - pidfile = os.path.join(current_path, '3bot-worker.pid') +BASE_FOLDERNAME = '3bot' +BASEDIR = os.path.join(os.path.expanduser("~"), BASE_FOLDERNAME) + +if os.environ.get('VIRTUAL_ENV', False): # in case worker runs in a virtualenv, set BASEDIR to path of virtualenv + BASEDIR = os.path.join(os.path.realpath(os.environ['VIRTUAL_ENV']), BASE_FOLDERNAME) + + +CONFIGFILE = os.path.join(BASEDIR, 'config.ini') +PIDFILE = os.path.join(BASEDIR, '3bot-worker.pid') +SCRIPTDIR = os.path.join(BASEDIR, 'logs') + +LOGFILE = os.path.join(BASEDIR, '3bot.log') +LOGLEVEL = 'ERROR' Config = ConfigParser.ConfigParser() -if os.path.isfile(configfile): - Config.read(configfile) +if os.path.isfile(CONFIGFILE): + Config.read(CONFIGFILE) else: - print "No configfile found in: '%s'" % configfile + print "No configfile found in: '%s'" % CONFIGFILE print "You can find a basic configfile in the Documentation." sys.exit(2) @@ -36,57 +42,51 @@ BOT = Config.get('3bot-settings', 'BOT_ENDPOINT') PORT = Config.get('3bot-settings', 'PORT') except: - print "Invalid configfile in: '%s'. Could not find BOT or PORT declaration" % configfile + print "Invalid configfile in: '%s'. Could not find BOT or PORT declaration" % CONFIGFILE print "You can find a basic configfile in the Documentation." sys.exit(2) try: SECRET_KEY = Config.get('3bot-settings', 'SECRET_KEY') except: - print "Invalid configfile in: '%s'. Could not find SECRET_KEY declaration" % configfile + print "Invalid configfile in: '%s'. Could not find SECRET_KEY declaration" % CONFIGFILE print "You can find a basic configfile in the Documentation." sys.exit(2) -try: - LOGFILE = Config.get('3bot-settings', 'LOGFILE') -except ConfigParser.NoOptionError: - LOGFILE = _default_logfile +LOGFILE = Config.get('3bot-settings', 'LOGFILE', LOGFILE) +LOGLEVEL = Config.get('3bot-settings', 'LOGLEVEL', LOGLEVEL) -try: - _LOGLEVEL = Config.get('3bot-settings', 'LOGLEVEL', _default_loglevel) -except ConfigParser.NoOptionError: - _LOGLEVEL = _default_loglevel - -if _LOGLEVEL == 'DEBUG': - LOGLEVEL = logging.DEBUG -elif _LOGLEVEL == 'INFO': - LOGLEVEL = logging.INFO -elif _LOGLEVEL == 'WARNING': - LOGLEVEL = logging.WARNING -elif _LOGLEVEL == 'ERROR': - LOGLEVEL = logging.ERROR + +if LOGLEVEL == 'DEBUG': + loglevel = logging.DEBUG +elif LOGLEVEL == 'INFO': + loglevel = logging.INFO +elif LOGLEVEL == 'WARNING': + loglevel = logging.WARNING +elif LOGLEVEL == 'ERROR': + loglevel = logging.ERROR else: LOGLEVEL = logging.CRITICAL logging.basicConfig( filename=LOGFILE, - level=LOGLEVEL, + level=loglevel, format='%(asctime)s %(message)s', ) if len(sys.argv) == 2: if 'start' == sys.argv[1] or 'restart' == sys.argv[1]: print '---' - print "Try Starting Worker with following settings found in '%s'" % configfile + print "Try Starting Worker with following settings found in '%s'" % CONFIGFILE print 'ENDPOINT: %s' % str(BOT) print 'PORT: %s' % str(PORT) print 'LOGFILE: %s' % str(LOGFILE) - print 'LOGLEVEL: %s' % str(_LOGLEVEL) + print 'LOGLEVEL: %s' % str(LOGLEVEL) print '---' -def writeScript(directory, script, body): +def write_script(directory, script, body): # create and change to log directory task_path = os.path.join(directory, script) @@ -101,7 +101,7 @@ def writeScript(directory, script, body): return task_path -def runCommand(request): +def run_command(request): """ Calls the action """ @@ -110,9 +110,8 @@ def runCommand(request): log_time = request['workflow_log_time'] workflow_name = request['workflow'] foldername = "%s-%s-%s" % (log_time, str(log_id), workflow_name) - home = os.path.expanduser("~") - directory = os.path.join(home, '3bot', 'logs', foldername) + directory = os.path.join(SCRIPTDIR, foldername) if not os.path.exists(directory): os.makedirs(directory) @@ -126,19 +125,19 @@ def runCommand(request): if request.get('hooks', ) is not None: if request['hooks'].get('pre_task', ) is not None: task_filename = "pre_task_%i" % request['script']['id'] - script_bits.append(writeScript(directory, task_filename, request['hooks']['pre_task'])) + script_bits.append(write_script(directory, task_filename, request['hooks']['pre_task'])) # the main task task_filename = "script_%i" % request['script']['id'] task_path = os.path.join(directory, task_filename) task_body = request['script']['body'] - script_bits.append(writeScript(directory, task_filename, task_body)) + script_bits.append(write_script(directory, task_filename, task_body)) # add post task hook if available if request.get('hooks', ) is not None: if request['hooks'].get('post_task', ) is not None: task_filename = "post_task_%i" % request['script']['id'] - script_bits.append(writeScript(directory, task_filename, request['hooks']['post_task'])) + script_bits.append(write_script(directory, task_filename, request['hooks']['post_task'])) callable = '' if len(script_bits) > 1: @@ -176,8 +175,7 @@ def run(self): context = zmq.Context(1) server = context.socket(zmq.REP) - logging.basicConfig(filename=LOGFILE, level=LOGLEVEL) - server.bind("tcp://%s:%s" % (BOT, PORT)) + server.bind("%s://%s:%s" % (PROTOCOL, BOT, PORT)) while True: request = server.recv(FLAGS) @@ -190,7 +188,7 @@ def run(self): response = {'type': 'ACK'} else: logging.info("Script request") - response = runCommand(request) + response = run_command(request) response = threebot_crypto.encrypt(response, SECRET_KEY) server.send(response, flags=FLAGS) logging.info("Sending response") @@ -204,11 +202,11 @@ def run(self): if __name__ == "__main__": if len(sys.argv) == 3: if 'start' == sys.argv[1] and 'debug' == sys.argv[2]: - daemon = WorkerDeamon(pidfile, debug_mode=True) + daemon = WorkerDeamon(PIDFILE, debug_mode=True) daemon.start() elif len(sys.argv) == 2: - daemon = WorkerDeamon(pidfile) + daemon = WorkerDeamon(PIDFILE) if 'start' == sys.argv[1]: daemon.start() elif 'stop' == sys.argv[1]: From 8c87685e5044fac8e8ecf6d43f95356fa5485b4d Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 13:33:51 +0100 Subject: [PATCH 04/14] . --- threebot_worker/threebot-worker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/threebot_worker/threebot-worker b/threebot_worker/threebot-worker index e9a25f5..0bea4d6 100644 --- a/threebot_worker/threebot-worker +++ b/threebot_worker/threebot-worker @@ -6,4 +6,4 @@ This file is used as a proxy to worker.py We use 'threebot-worker' as a script in setup.py """ -from worker import * # NOQA +from threebot_worker.worker import * # NOQA From 4c62f315e087fa4f8d2e49e120ef9a48cdd6c124 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 13:44:58 +0100 Subject: [PATCH 05/14] . --- threebot_worker/worker.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/threebot_worker/worker.py b/threebot_worker/worker.py index 7a3b63b..d0db4dd 100755 --- a/threebot_worker/worker.py +++ b/threebot_worker/worker.py @@ -54,9 +54,15 @@ sys.exit(2) -LOGFILE = Config.get('3bot-settings', 'LOGFILE', LOGFILE) -LOGLEVEL = Config.get('3bot-settings', 'LOGLEVEL', LOGLEVEL) +try: + LOGFILE = Config.get('3bot-settings', 'LOGFILE') +except ConfigParser.NoOptionError: + pass +try: + LOGLEVEL = Config.get('3bot-settings', 'LOGLEVEL') +except ConfigParser.NoOptionError: + pass if LOGLEVEL == 'DEBUG': loglevel = logging.DEBUG From 238687f9a6c9b6a2470692f18c20fba58af01cc3 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 13:50:54 +0100 Subject: [PATCH 06/14] . --- threebot_worker/threebot-worker | 231 +++++++++++++++++++++++++++++++- 1 file changed, 226 insertions(+), 5 deletions(-) diff --git a/threebot_worker/threebot-worker b/threebot_worker/threebot-worker index 0bea4d6..d0db4dd 100644 --- a/threebot_worker/threebot-worker +++ b/threebot_worker/threebot-worker @@ -1,9 +1,230 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -""" -This file is used as a proxy to worker.py -We use 'threebot-worker' as a script in setup.py -""" +import os +import sys +import subprocess +import zmq +import ConfigParser +import logging -from threebot_worker.worker import * # NOQA +import threebot_crypto +from threebot_worker.daemon import Daemon + +PROTOCOL = 'tcp' + +BASE_FOLDERNAME = '3bot' +BASEDIR = os.path.join(os.path.expanduser("~"), BASE_FOLDERNAME) + +if os.environ.get('VIRTUAL_ENV', False): # in case worker runs in a virtualenv, set BASEDIR to path of virtualenv + BASEDIR = os.path.join(os.path.realpath(os.environ['VIRTUAL_ENV']), BASE_FOLDERNAME) + + +CONFIGFILE = os.path.join(BASEDIR, 'config.ini') +PIDFILE = os.path.join(BASEDIR, '3bot-worker.pid') +SCRIPTDIR = os.path.join(BASEDIR, 'logs') + +LOGFILE = os.path.join(BASEDIR, '3bot.log') +LOGLEVEL = 'ERROR' + +Config = ConfigParser.ConfigParser() + +if os.path.isfile(CONFIGFILE): + Config.read(CONFIGFILE) +else: + print "No configfile found in: '%s'" % CONFIGFILE + print "You can find a basic configfile in the Documentation." + sys.exit(2) + +FLAGS = 0 + +try: + BOT = Config.get('3bot-settings', 'BOT_ENDPOINT') + PORT = Config.get('3bot-settings', 'PORT') +except: + print "Invalid configfile in: '%s'. Could not find BOT or PORT declaration" % CONFIGFILE + print "You can find a basic configfile in the Documentation." + sys.exit(2) + +try: + SECRET_KEY = Config.get('3bot-settings', 'SECRET_KEY') +except: + print "Invalid configfile in: '%s'. Could not find SECRET_KEY declaration" % CONFIGFILE + print "You can find a basic configfile in the Documentation." + sys.exit(2) + + +try: + LOGFILE = Config.get('3bot-settings', 'LOGFILE') +except ConfigParser.NoOptionError: + pass + +try: + LOGLEVEL = Config.get('3bot-settings', 'LOGLEVEL') +except ConfigParser.NoOptionError: + pass + +if LOGLEVEL == 'DEBUG': + loglevel = logging.DEBUG +elif LOGLEVEL == 'INFO': + loglevel = logging.INFO +elif LOGLEVEL == 'WARNING': + loglevel = logging.WARNING +elif LOGLEVEL == 'ERROR': + loglevel = logging.ERROR +else: + LOGLEVEL = logging.CRITICAL + +logging.basicConfig( + filename=LOGFILE, + level=loglevel, + format='%(asctime)s %(message)s', +) + +if len(sys.argv) == 2: + if 'start' == sys.argv[1] or 'restart' == sys.argv[1]: + print '---' + print "Try Starting Worker with following settings found in '%s'" % CONFIGFILE + print 'ENDPOINT: %s' % str(BOT) + print 'PORT: %s' % str(PORT) + print 'LOGFILE: %s' % str(LOGFILE) + print 'LOGLEVEL: %s' % str(LOGLEVEL) + print '---' + + +def write_script(directory, script, body): + # create and change to log directory + task_path = os.path.join(directory, script) + + # create file + with open(task_path, 'w+') as task_file: + task_file.write(str(body.replace('\r\n', '\n'))) + logging.info("Saving new Script file at: %s" % task_path) + + # change permission + os.chmod(task_path, 0755) + + return task_path + + +def run_command(request): + """ + Calls the action + """ + response = {} + log_id = request['workflow_log_id'] + log_time = request['workflow_log_time'] + workflow_name = request['workflow'] + foldername = "%s-%s-%s" % (log_time, str(log_id), workflow_name) + + directory = os.path.join(SCRIPTDIR, foldername) + + if not os.path.exists(directory): + os.makedirs(directory) + + script_bits = [] + + # NOTE: keep order of hooks and task + # TODO: cleaner implementation, not so verbose + + # add pre task hook if available + if request.get('hooks', ) is not None: + if request['hooks'].get('pre_task', ) is not None: + task_filename = "pre_task_%i" % request['script']['id'] + script_bits.append(write_script(directory, task_filename, request['hooks']['pre_task'])) + + # the main task + task_filename = "script_%i" % request['script']['id'] + task_path = os.path.join(directory, task_filename) + task_body = request['script']['body'] + script_bits.append(write_script(directory, task_filename, task_body)) + + # add post task hook if available + if request.get('hooks', ) is not None: + if request['hooks'].get('post_task', ) is not None: + task_filename = "post_task_%i" % request['script']['id'] + script_bits.append(write_script(directory, task_filename, request['hooks']['post_task'])) + + callable = '' + if len(script_bits) > 1: + callable = " && ".join(script_bits) + else: + callable = script_bits[0] + + # execute task script + # http://www.saltycrane.com/blog/2008/09/how-get-stdout-and-stderr-using-python-subprocess-module/ + p = subprocess.Popen(callable, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + logging.info("Executing Script file at: %s" % task_path) + t_stdout = "" + t_stderr = "" + + try: + t_stdout = p.stdout.read() + except AttributeError, e: + logging.info(e) + + try: + t_stderr = p.stderr.read() + except AttributeError, e: + logging.info(e) + + response = {'stdout': t_stdout, 'stderr': t_stderr, 'exit_code': p.wait()} + del p + + return response + + +class WorkerDeamon(Daemon): + + def run(self): + logging.info("Starting the 3bot worker listening on %s:%s" % (BOT, PORT)) + + context = zmq.Context(1) + server = context.socket(zmq.REP) + server.bind("%s://%s:%s" % (PROTOCOL, BOT, PORT)) + + while True: + request = server.recv(FLAGS) + request = threebot_crypto.decrypt(request, SECRET_KEY) + logging.info("Received request") + if request: + response = {'type': 'NOOP'} + if 'type' in request and request['type'] == 'ACC': + logging.info("ACK request") + response = {'type': 'ACK'} + else: + logging.info("Script request") + response = run_command(request) + response = threebot_crypto.encrypt(response, SECRET_KEY) + server.send(response, flags=FLAGS) + logging.info("Sending response") + else: + logging.error("Could not decrypt received message") + if self.debug_mode: + raise Exception("Could not decrypt message") + # server.send("", flags=FLAGS) + + +if __name__ == "__main__": + if len(sys.argv) == 3: + if 'start' == sys.argv[1] and 'debug' == sys.argv[2]: + daemon = WorkerDeamon(PIDFILE, debug_mode=True) + daemon.start() + + elif len(sys.argv) == 2: + daemon = WorkerDeamon(PIDFILE) + if 'start' == sys.argv[1]: + daemon.start() + elif 'stop' == sys.argv[1]: + daemon.stop() + elif 'restart' == sys.argv[1]: + daemon.restart() + elif 'status' == sys.argv[1]: + daemon.status() + else: + print "Unknown command" + sys.exit(2) + sys.exit(0) + else: + print "usage: %s start|stop|restart|status" % sys.argv[0] + sys.exit(2) From edc4b71fda4a989629c162146575078edf2bde2b Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 14:41:57 +0100 Subject: [PATCH 07/14] create Configfile in not exist --- threebot_worker/threebot-worker | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/threebot_worker/threebot-worker b/threebot_worker/threebot-worker index d0db4dd..bf63505 100644 --- a/threebot_worker/threebot-worker +++ b/threebot_worker/threebot-worker @@ -19,7 +19,6 @@ BASEDIR = os.path.join(os.path.expanduser("~"), BASE_FOLDERNAME) if os.environ.get('VIRTUAL_ENV', False): # in case worker runs in a virtualenv, set BASEDIR to path of virtualenv BASEDIR = os.path.join(os.path.realpath(os.environ['VIRTUAL_ENV']), BASE_FOLDERNAME) - CONFIGFILE = os.path.join(BASEDIR, 'config.ini') PIDFILE = os.path.join(BASEDIR, '3bot-worker.pid') SCRIPTDIR = os.path.join(BASEDIR, 'logs') @@ -33,8 +32,20 @@ if os.path.isfile(CONFIGFILE): Config.read(CONFIGFILE) else: print "No configfile found in: '%s'" % CONFIGFILE - print "You can find a basic configfile in the Documentation." - sys.exit(2) + print "..." + print "Creating basic configfile in '%s'" % CONFIGFILE + + cfgfile = open(CONFIGFILE, 'w') + + # add the settings to the structure of the file, and lets write it out... + Config.add_section('3bot-settings') + Config.set('3bot-settings', 'BOT_ENDPOINT', '*') + s = raw_input('Enter PORT') + Config.set('3bot-settings', 'PORT', s) + s = raw_input('Enter SECRET_KEY') + Config.set('3bot-settings', 'SECRET_KEY', s) + Config.write(cfgfile) + cfgfile.close() FLAGS = 0 From f4fd6f14a6a6943034aea21708e6dd2ce89c26a2 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 14:49:31 +0100 Subject: [PATCH 08/14] create Configfile in not exist --- threebot_worker/threebot-worker | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/threebot_worker/threebot-worker b/threebot_worker/threebot-worker index bf63505..b748ef2 100644 --- a/threebot_worker/threebot-worker +++ b/threebot_worker/threebot-worker @@ -32,18 +32,22 @@ if os.path.isfile(CONFIGFILE): Config.read(CONFIGFILE) else: print "No configfile found in: '%s'" % CONFIGFILE - print "..." + print "----" print "Creating basic configfile in '%s'" % CONFIGFILE + print "----" cfgfile = open(CONFIGFILE, 'w') # add the settings to the structure of the file, and lets write it out... Config.add_section('3bot-settings') Config.set('3bot-settings', 'BOT_ENDPOINT', '*') - s = raw_input('Enter PORT') - Config.set('3bot-settings', 'PORT', s) - s = raw_input('Enter SECRET_KEY') - Config.set('3bot-settings', 'SECRET_KEY', s) + + port = raw_input('Enter PORT: ') + Config.set('3bot-settings', 'PORT', port) + + sec_key = raw_input('Enter SECRET_KEY: ') + Config.set('3bot-settings', 'SECRET_KEY', sec_key) + Config.write(cfgfile) cfgfile.close() From 1e838ea45856aeb6a6d3f998c85f9438e41a4b93 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 15:11:03 +0100 Subject: [PATCH 09/14] Bump Version 0.1.0 --- README.md | 47 +++++++++++++++++++++++++------------ threebot_worker/__init__.py | 2 +- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6f2c7b2..9e85444 100644 --- a/README.md +++ b/README.md @@ -4,39 +4,56 @@ A worker is a computer program that runs as a background process on a machine. T Jump to [3bot main repo](https://github.com/3bot/3bot/). -Use [this Gist](https://gist.github.com/walterrenner/4d8863043404bec01d0f) to install the 3bot-worker on your mashine. +## Setup/Installation + $ pip install threebot-worker + $ threebot-worker start -## Configuration -The configuration file contains all the configuration of your 3bot installation and/or components. -This section explains how this configuration work and which parameter are available. -* The configuration file is located under `/etc/3bot/config.ini`. -* There is a overall `3bot-settings` section. -* It's an ini-file. +## Configuration -### The `3bot-settings` section +The configuration file contains all the configuration of your threebot-worker installation. +This section explains which parameter are available. -#### BOT_ENDPOINT +The configuration file is located in `~/3bot/config.ini`. If you installed 3bot in an own virtualenv it +is located in `/3bot/config.ini` -#### LOGFILE +When you first run the threebot-worker Script you will be asked to enter a PORT and SECRET_KEY -* That file/path must be writable by the worker. +## Available settings -#### LOGLEVEL +#### BOT_ENDPOINT -* Valid values for `LOGLEVEL` could be taken from [here](https://docs.python.org/2/howto/logging.html). +default: * +List of hosts the worker should accept connections from (this is not well tested yet) #### PORT +default: None +Port number the worker listens. This Port should be openend by your firewall. The port number from the 3bot application and threebot-worker settings must match. + #### SECRET_KEY -* Do never share your config.ini containing your `SECRET_KEY`! +The secret key is used to establish a secure connection from the 3bot application to the threebot-worker. The secret key from the 3bot application and threebot-worker settings must match. +**Never share your your secret key!** + +#### LOGFILE + +default: `~/3bot/3bot.log` +Path to the logfile. theebot-worker will log all incomming connections, performed workflows and errors + +#### LOGLEVEL +default: `CRITICAL` +Valid values for `LOGLEVEL` could be taken from [here](https://docs.python.org/2/howto/logging.html). -### Example +### Example for `confing.ini` + [3bot-settings] + BOT_ENDPOINT = * + PORT = 55556 + SECRET_KEY = LOGFILE = /var/log/3bot-worker.log LOGLEVEL = DEBUG diff --git a/threebot_worker/__init__.py b/threebot_worker/__init__.py index 1513151..96e6292 100644 --- a/threebot_worker/__init__.py +++ b/threebot_worker/__init__.py @@ -1,4 +1,4 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -__version__ = '0.0.9' +__version__ = '0.1.0' From 7fd466c686b7ecb0ca2d50cf39287534f45e4b48 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 15:15:08 +0100 Subject: [PATCH 10/14] updated changelog for 0.1.0 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1b9040..1045c08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ * . +### 0.1.0 - 27.02.2015 + +* Creates a config.ini if not found +* all threebot-worker relevant files are now located in `~/3bot/` or `/3bot/` +* `threebot-worker` is a global available script +* Updated README + + ### 0.0.3 - 17.11.2014 * Raise exception only when in debug mode From 2cfbb2d7b22633f2b3da4496a2788d38e9348b0b Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 15:36:40 +0100 Subject: [PATCH 11/14] delete worker.py --- threebot_worker/worker.py | 230 -------------------------------------- 1 file changed, 230 deletions(-) delete mode 100755 threebot_worker/worker.py diff --git a/threebot_worker/worker.py b/threebot_worker/worker.py deleted file mode 100755 index d0db4dd..0000000 --- a/threebot_worker/worker.py +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os -import sys -import subprocess -import zmq -import ConfigParser -import logging - -import threebot_crypto -from threebot_worker.daemon import Daemon - -PROTOCOL = 'tcp' - -BASE_FOLDERNAME = '3bot' -BASEDIR = os.path.join(os.path.expanduser("~"), BASE_FOLDERNAME) - -if os.environ.get('VIRTUAL_ENV', False): # in case worker runs in a virtualenv, set BASEDIR to path of virtualenv - BASEDIR = os.path.join(os.path.realpath(os.environ['VIRTUAL_ENV']), BASE_FOLDERNAME) - - -CONFIGFILE = os.path.join(BASEDIR, 'config.ini') -PIDFILE = os.path.join(BASEDIR, '3bot-worker.pid') -SCRIPTDIR = os.path.join(BASEDIR, 'logs') - -LOGFILE = os.path.join(BASEDIR, '3bot.log') -LOGLEVEL = 'ERROR' - -Config = ConfigParser.ConfigParser() - -if os.path.isfile(CONFIGFILE): - Config.read(CONFIGFILE) -else: - print "No configfile found in: '%s'" % CONFIGFILE - print "You can find a basic configfile in the Documentation." - sys.exit(2) - -FLAGS = 0 - -try: - BOT = Config.get('3bot-settings', 'BOT_ENDPOINT') - PORT = Config.get('3bot-settings', 'PORT') -except: - print "Invalid configfile in: '%s'. Could not find BOT or PORT declaration" % CONFIGFILE - print "You can find a basic configfile in the Documentation." - sys.exit(2) - -try: - SECRET_KEY = Config.get('3bot-settings', 'SECRET_KEY') -except: - print "Invalid configfile in: '%s'. Could not find SECRET_KEY declaration" % CONFIGFILE - print "You can find a basic configfile in the Documentation." - sys.exit(2) - - -try: - LOGFILE = Config.get('3bot-settings', 'LOGFILE') -except ConfigParser.NoOptionError: - pass - -try: - LOGLEVEL = Config.get('3bot-settings', 'LOGLEVEL') -except ConfigParser.NoOptionError: - pass - -if LOGLEVEL == 'DEBUG': - loglevel = logging.DEBUG -elif LOGLEVEL == 'INFO': - loglevel = logging.INFO -elif LOGLEVEL == 'WARNING': - loglevel = logging.WARNING -elif LOGLEVEL == 'ERROR': - loglevel = logging.ERROR -else: - LOGLEVEL = logging.CRITICAL - -logging.basicConfig( - filename=LOGFILE, - level=loglevel, - format='%(asctime)s %(message)s', -) - -if len(sys.argv) == 2: - if 'start' == sys.argv[1] or 'restart' == sys.argv[1]: - print '---' - print "Try Starting Worker with following settings found in '%s'" % CONFIGFILE - print 'ENDPOINT: %s' % str(BOT) - print 'PORT: %s' % str(PORT) - print 'LOGFILE: %s' % str(LOGFILE) - print 'LOGLEVEL: %s' % str(LOGLEVEL) - print '---' - - -def write_script(directory, script, body): - # create and change to log directory - task_path = os.path.join(directory, script) - - # create file - with open(task_path, 'w+') as task_file: - task_file.write(str(body.replace('\r\n', '\n'))) - logging.info("Saving new Script file at: %s" % task_path) - - # change permission - os.chmod(task_path, 0755) - - return task_path - - -def run_command(request): - """ - Calls the action - """ - response = {} - log_id = request['workflow_log_id'] - log_time = request['workflow_log_time'] - workflow_name = request['workflow'] - foldername = "%s-%s-%s" % (log_time, str(log_id), workflow_name) - - directory = os.path.join(SCRIPTDIR, foldername) - - if not os.path.exists(directory): - os.makedirs(directory) - - script_bits = [] - - # NOTE: keep order of hooks and task - # TODO: cleaner implementation, not so verbose - - # add pre task hook if available - if request.get('hooks', ) is not None: - if request['hooks'].get('pre_task', ) is not None: - task_filename = "pre_task_%i" % request['script']['id'] - script_bits.append(write_script(directory, task_filename, request['hooks']['pre_task'])) - - # the main task - task_filename = "script_%i" % request['script']['id'] - task_path = os.path.join(directory, task_filename) - task_body = request['script']['body'] - script_bits.append(write_script(directory, task_filename, task_body)) - - # add post task hook if available - if request.get('hooks', ) is not None: - if request['hooks'].get('post_task', ) is not None: - task_filename = "post_task_%i" % request['script']['id'] - script_bits.append(write_script(directory, task_filename, request['hooks']['post_task'])) - - callable = '' - if len(script_bits) > 1: - callable = " && ".join(script_bits) - else: - callable = script_bits[0] - - # execute task script - # http://www.saltycrane.com/blog/2008/09/how-get-stdout-and-stderr-using-python-subprocess-module/ - p = subprocess.Popen(callable, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - logging.info("Executing Script file at: %s" % task_path) - t_stdout = "" - t_stderr = "" - - try: - t_stdout = p.stdout.read() - except AttributeError, e: - logging.info(e) - - try: - t_stderr = p.stderr.read() - except AttributeError, e: - logging.info(e) - - response = {'stdout': t_stdout, 'stderr': t_stderr, 'exit_code': p.wait()} - del p - - return response - - -class WorkerDeamon(Daemon): - - def run(self): - logging.info("Starting the 3bot worker listening on %s:%s" % (BOT, PORT)) - - context = zmq.Context(1) - server = context.socket(zmq.REP) - server.bind("%s://%s:%s" % (PROTOCOL, BOT, PORT)) - - while True: - request = server.recv(FLAGS) - request = threebot_crypto.decrypt(request, SECRET_KEY) - logging.info("Received request") - if request: - response = {'type': 'NOOP'} - if 'type' in request and request['type'] == 'ACC': - logging.info("ACK request") - response = {'type': 'ACK'} - else: - logging.info("Script request") - response = run_command(request) - response = threebot_crypto.encrypt(response, SECRET_KEY) - server.send(response, flags=FLAGS) - logging.info("Sending response") - else: - logging.error("Could not decrypt received message") - if self.debug_mode: - raise Exception("Could not decrypt message") - # server.send("", flags=FLAGS) - - -if __name__ == "__main__": - if len(sys.argv) == 3: - if 'start' == sys.argv[1] and 'debug' == sys.argv[2]: - daemon = WorkerDeamon(PIDFILE, debug_mode=True) - daemon.start() - - elif len(sys.argv) == 2: - daemon = WorkerDeamon(PIDFILE) - if 'start' == sys.argv[1]: - daemon.start() - elif 'stop' == sys.argv[1]: - daemon.stop() - elif 'restart' == sys.argv[1]: - daemon.restart() - elif 'status' == sys.argv[1]: - daemon.status() - else: - print "Unknown command" - sys.exit(2) - sys.exit(0) - else: - print "usage: %s start|stop|restart|status" % sys.argv[0] - sys.exit(2) From 938032b09e9a26b49379ee939a3ea5d8ca75bd81 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 15:50:28 +0100 Subject: [PATCH 12/14] . --- threebot_worker/threebot-worker | 1 + 1 file changed, 1 insertion(+) diff --git a/threebot_worker/threebot-worker b/threebot_worker/threebot-worker index b748ef2..5f254a4 100644 --- a/threebot_worker/threebot-worker +++ b/threebot_worker/threebot-worker @@ -36,6 +36,7 @@ else: print "Creating basic configfile in '%s'" % CONFIGFILE print "----" + os.makedirs(CONFIGFILE) cfgfile = open(CONFIGFILE, 'w') # add the settings to the structure of the file, and lets write it out... From 171e201924db45c1f19d9a759f2fb08ad9dbff3b Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Fri, 27 Feb 2015 16:14:14 +0100 Subject: [PATCH 13/14] . --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9e85444..04d4535 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,13 @@ When you first run the threebot-worker Script you will be asked to enter a PORT #### BOT_ENDPOINT default: * + List of hosts the worker should accept connections from (this is not well tested yet) #### PORT default: None + Port number the worker listens. This Port should be openend by your firewall. The port number from the 3bot application and threebot-worker settings must match. #### SECRET_KEY @@ -41,11 +43,13 @@ The secret key is used to establish a secure connection from the 3bot applicatio #### LOGFILE default: `~/3bot/3bot.log` + Path to the logfile. theebot-worker will log all incomming connections, performed workflows and errors #### LOGLEVEL default: `CRITICAL` + Valid values for `LOGLEVEL` could be taken from [here](https://docs.python.org/2/howto/logging.html). ### Example for `confing.ini` From 6e3a5132bd0b4c4cc583628ebf7137646ca39202 Mon Sep 17 00:00:00 2001 From: Walter Renner Date: Wed, 4 Mar 2015 09:11:32 +0100 Subject: [PATCH 14/14] updated readme --- README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 04d4535..987709d 100644 --- a/README.md +++ b/README.md @@ -19,35 +19,48 @@ This section explains which parameter are available. The configuration file is located in `~/3bot/config.ini`. If you installed 3bot in an own virtualenv it is located in `/3bot/config.ini` -When you first run the threebot-worker Script you will be asked to enter a PORT and SECRET_KEY +You don't need to create a configuration file by yourself. When you first run the threebot-worker, you will be asked to enter values for the required settings. -## Available settings +### Available settings #### BOT_ENDPOINT +required: True + default: * List of hosts the worker should accept connections from (this is not well tested yet) #### PORT +required: True + default: None Port number the worker listens. This Port should be openend by your firewall. The port number from the 3bot application and threebot-worker settings must match. #### SECRET_KEY +required: True + +default: None + The secret key is used to establish a secure connection from the 3bot application to the threebot-worker. The secret key from the 3bot application and threebot-worker settings must match. + **Never share your your secret key!** #### LOGFILE +required: False + default: `~/3bot/3bot.log` -Path to the logfile. theebot-worker will log all incomming connections, performed workflows and errors +Path to the logfile. theebot-worker will log all incomming connections, performed workflows and errors. #### LOGLEVEL +required: False + default: `CRITICAL` Valid values for `LOGLEVEL` could be taken from [here](https://docs.python.org/2/howto/logging.html).