From 7d027225bc575eb401dd9124120a45bcf3bf6202 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Mon, 24 Oct 2016 15:58:39 +0200 Subject: [PATCH 01/18] begin integration of trigger handler Signed-off-by: Nico Schottelius --- cdist/trigger.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ scripts/cdist | 19 +++++++++++++++-- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 cdist/trigger.py diff --git a/cdist/trigger.py b/cdist/trigger.py new file mode 100644 index 00000000..88986dd8 --- /dev/null +++ b/cdist/trigger.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# 2016 Nico Schottelius (nico-cdist at schottelius.org) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# + +import logging +import os +import sys +import time +import tempfile +from http.server import BaseHTTPRequestHandler, HTTPServer + + +import cdist +from cdist import core + +class Trigger(): + """cdist trigger handling""" + + def __init__(self, dry_run=False): + self.log = logging.getLogger("trigger") + self.dry_run = dry_run + + def run_http(self): + server_address = ('0.0.0.0', 8000) + httpd = HTTPServer(server_address, testHTTPServer_RequestHandler) + print('running server...') + httpd.serve_forever() + + @staticmethod + def commandline(args): + print("all good") + pass + + +class TriggerHttp(BaseHTTPRequestHandler): + def do_GET(self): + pass diff --git a/scripts/cdist b/scripts/cdist index 9f8d326d..2b11827b 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# 2010-2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2010-2016 Nico Schottelius (nico-cdist at schottelius.org) # 2016 Darko Poljak (darko.poljak at gmail.com) # # This file is part of cdist. @@ -23,7 +23,7 @@ # list of beta sub-commands -BETA_COMMANDS = ['install', ] +BETA_COMMANDS = ['install', 'trigger' ] # list of beta arguments for sub-commands BETA_ARGS = { 'config': ['jobs', ], @@ -69,6 +69,7 @@ def commandline(): import cdist.config import cdist.install import cdist.shell + import cdist.trigger import shutil import os import multiprocessing @@ -84,6 +85,12 @@ def commandline(): '-v', '--verbose', help='Set log level to info, be more verbose', action='store_true', default=False) + parser['beta'] = argparse.ArgumentParser(add_help=False) + parser['beta'].add_argument( + '-b', '--enable-beta', + help=('Enable beta functionalities.'), + action='store_true', dest='beta', default=False) + # Main subcommand parser parser['main'] = argparse.ArgumentParser( description='cdist ' + cdist.VERSION, parents=[parser['loglevel']]) @@ -168,6 +175,14 @@ def commandline(): ' should be POSIX compatible shell.')) parser['shell'].set_defaults(func=cdist.shell.Shell.commandline) + parser['trigger'] = parser['sub'].add_parser( + 'trigger', parents=[parser['loglevel'], parser['beta']]) + parser['trigger'].add_argument( + '-H', '--http-port', + help=('Create trigger listener via http on specified port'), + action='append') + parser['trigger'].set_defaults(func=cdist.trigger.Trigger.commandline) + # Install parser['install'] = parser['sub'].add_parser('install', add_help=False, parents=[parser['config']]) From 92bb0803eb38cb34ad72767ced438714e32b7ae5 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Tue, 25 Oct 2016 12:38:13 +0200 Subject: [PATCH 02/18] Finish base functionality for trigger execution --- cdist/trigger.py | 83 ++++++++++++++++++++++++++++++++++++++++-------- scripts/cdist | 10 ++++-- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index 88986dd8..b53f4ad1 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -21,35 +21,90 @@ # import logging -import os -import sys -import time -import tempfile +import re +import socket +import http.server + from http.server import BaseHTTPRequestHandler, HTTPServer +import multiprocessing import cdist -from cdist import core + +log = logging.getLogger(__name__) class Trigger(): """cdist trigger handling""" - def __init__(self, dry_run=False): + def __init__(self, http_port=None, dry_run=False, ipv4only=False): self.log = logging.getLogger("trigger") self.dry_run = dry_run + self.http_port = int(http_port) + self.ipv4only = ipv4only + + # can only be set once + multiprocessing.set_start_method('forkserver') + + def run_httpd(self): + server_address = ('', self.http_port) + + if self.ipv4only: + httpd = HTTPServerV4(server_address, TriggerHttp) + else: + httpd = HTTPServerV6(server_address, TriggerHttp) - def run_http(self): - server_address = ('0.0.0.0', 8000) - httpd = HTTPServer(server_address, testHTTPServer_RequestHandler) - print('running server...') httpd.serve_forever() + def run(self): + if self.http_port: + self.run_httpd() + @staticmethod def commandline(args): - print("all good") - pass - + t = Trigger(http_port=args.http_port, ipv4only=args.ipv4) + t.run() class TriggerHttp(BaseHTTPRequestHandler): + def __init__(self, *args, **kwargs): + http.server.BaseHTTPRequestHandler.__init__(self, *args, **kwargs) + def do_GET(self): - pass + # FIXME: dispatch to pool instead of single process + host = self.client_address[0] + code = 200 + mode = None + + m = re.match("^/(?Pconfig|install)/.*", self.path) + if m: + mode = m.group('mode') + else: + code = 404 + + if mode: + self.run_cdist(mode, host) + + self.send_response(code) + self.end_headers() + + def do_HEAD(self): + self.do_GET() + + def do_POST(self): + self.do_GET() + + def run_cdist(self, mode, host): + log.debug("Running cdist {} {}".format(mode, host)) + + +class HTTPServerV4(http.server.HTTPServer): + """ + Server that listens to IPv4 and IPv6 requests + """ + address_family = socket.AF_INET + + +class HTTPServerV6(http.server.HTTPServer): + """ + Server that listens both to IPv4 and IPv6 requests + """ + address_family = socket.AF_INET6 diff --git a/scripts/cdist b/scripts/cdist index 2b11827b..8c6b62db 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -175,12 +175,18 @@ def commandline(): ' should be POSIX compatible shell.')) parser['shell'].set_defaults(func=cdist.shell.Shell.commandline) + # Trigger parser['trigger'] = parser['sub'].add_parser( 'trigger', parents=[parser['loglevel'], parser['beta']]) + parser['trigger'].add_argument( + '-4', '--ipv4', + help=('Listen only to IPv4 (instead of IPv4 and IPv4)'), action='store_true') parser['trigger'].add_argument( '-H', '--http-port', - help=('Create trigger listener via http on specified port'), - action='append') + help=('Create trigger listener via http on specified port')) + parser['trigger'].add_argument( + '-n', '--dry-run', + help='Do not execute code', action='store_true') parser['trigger'].set_defaults(func=cdist.trigger.Trigger.commandline) # Install From 579b8d5c72e9616c491674c5bd1d048ea2195038 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sun, 30 Oct 2016 14:48:10 +0100 Subject: [PATCH 03/18] Fix typo. --- scripts/cdist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cdist b/scripts/cdist index 8c6b62db..f4cbab45 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -180,7 +180,7 @@ def commandline(): 'trigger', parents=[parser['loglevel'], parser['beta']]) parser['trigger'].add_argument( '-4', '--ipv4', - help=('Listen only to IPv4 (instead of IPv4 and IPv4)'), action='store_true') + help=('Listen only to IPv4 (instead of IPv4 and IPv6)'), action='store_true') parser['trigger'].add_argument( '-H', '--http-port', help=('Create trigger listener via http on specified port')) From ca67533ce42842fcaa6eaf204c80b14be7bd0629 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sun, 30 Oct 2016 14:49:07 +0100 Subject: [PATCH 04/18] Fix comment typos. --- cdist/trigger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index b53f4ad1..38442c6c 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -98,13 +98,13 @@ class TriggerHttp(BaseHTTPRequestHandler): class HTTPServerV4(http.server.HTTPServer): """ - Server that listens to IPv4 and IPv6 requests + Server that listens only to IPv4 requests. """ address_family = socket.AF_INET class HTTPServerV6(http.server.HTTPServer): """ - Server that listens both to IPv4 and IPv6 requests + Server that listens both to IPv4 and IPv6 requests. """ address_family = socket.AF_INET6 From 535181435f9bdae9c1589251442187f33419ee1c Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 30 Oct 2016 15:01:45 +0100 Subject: [PATCH 05/18] update for darko Signed-off-by: Nico Schottelius --- cdist/config.py | 14 +++++++++++++- cdist/trigger.py | 38 +++++++++++++++++++++++++++----------- scripts/cdist | 1 + 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/cdist/config.py b/cdist/config.py index b0131601..14c99fe8 100644 --- a/cdist/config.py +++ b/cdist/config.py @@ -136,7 +136,7 @@ class Config(object): import multiprocessing # FIXME: Refactor relict - remove later - log = logging.getLogger("cdist") + log = logging.getLogger(__name__) if args.manifest == '-' and args.hostfile == '-': raise cdist.Error(("Cannot read both, manifest and host file, " @@ -227,6 +227,8 @@ class Config(object): raise cdist.Error("Failed to configure the following hosts: " + " ".join(failed_hosts)) + + @classmethod def onehost(cls, host, host_base_path, host_dir_name, args, parallel): """Configure ONE system""" @@ -317,6 +319,16 @@ class Config(object): else: raise + + # FIXME begin to cleanup with this method + @staticmethod + def create_host_tmpdir(host): + base_dir = tempfile.mkdtemp() + hostdir = cdist.str_hash(host) + + return (base_dir, hostdir) + + def run(self): """Do what is most often done: deploy & cleanup""" start_time = time.time() diff --git a/cdist/trigger.py b/cdist/trigger.py index b53f4ad1..b8c4611f 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -29,7 +29,8 @@ from http.server import BaseHTTPRequestHandler, HTTPServer import multiprocessing -import cdist +import cdist.config +import cdist.install log = logging.getLogger(__name__) @@ -42,16 +43,22 @@ class Trigger(): self.http_port = int(http_port) self.ipv4only = ipv4only + self.args = "fun" + # can only be set once multiprocessing.set_start_method('forkserver') + # Create pool suitable for passing objects + def __init_pool(self): + pass + def run_httpd(self): server_address = ('', self.http_port) if self.ipv4only: - httpd = HTTPServerV4(server_address, TriggerHttp) + httpd = HTTPServerV4(self.args, server_address, TriggerHttp) else: - httpd = HTTPServerV6(server_address, TriggerHttp) + httpd = HTTPServerV6(self.args, server_address, TriggerHttp) httpd.serve_forever() @@ -65,15 +72,14 @@ class Trigger(): t.run() class TriggerHttp(BaseHTTPRequestHandler): - def __init__(self, *args, **kwargs): - http.server.BaseHTTPRequestHandler.__init__(self, *args, **kwargs) - def do_GET(self): # FIXME: dispatch to pool instead of single process host = self.client_address[0] code = 200 mode = None + print(self.server.cdistargs) + m = re.match("^/(?Pconfig|install)/.*", self.path) if m: mode = m.group('mode') @@ -95,12 +101,12 @@ class TriggerHttp(BaseHTTPRequestHandler): def run_cdist(self, mode, host): log.debug("Running cdist {} {}".format(mode, host)) + cname = mode.title() + module = getattr(cdist, mode) + theclass = getattr(module, cname) -class HTTPServerV4(http.server.HTTPServer): - """ - Server that listens to IPv4 and IPv6 requests - """ - address_family = socket.AF_INET + host_base_path, hostdir = theclass.create_host_tmpdir(host) + theclass.onehost(host, host_base_path, hostdir, args, parallel=False) class HTTPServerV6(http.server.HTTPServer): @@ -108,3 +114,13 @@ class HTTPServerV6(http.server.HTTPServer): Server that listens both to IPv4 and IPv6 requests """ address_family = socket.AF_INET6 + + def __init__(self, cdistargs, *args, **kwargs): + self.cdistargs = cdistargs + http.server.HTTPServer.__init__(self, *args, **kwargs) + +class HTTPServerV4(HTTPServerV6): + """ + Server that listens to IPv4 and IPv6 requests + """ + address_family = socket.AF_INET diff --git a/scripts/cdist b/scripts/cdist index 8c6b62db..8077c43a 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -150,6 +150,7 @@ def commandline(): '-s', '--sequential', help='Operate on multiple hosts sequentially (default)', action='store_false', dest='parallel') + # remote-copy and remote-exec defaults are environment variables # if set; if not then None - these will be futher handled after # parsing to determine implementation default From d31608984289cf70c747fa27f4cac20a3bc4ed6e Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sun, 30 Oct 2016 16:41:59 +0100 Subject: [PATCH 06/18] Continue with trigger. --- cdist/config.py | 64 ++++++++++++++++------------ cdist/trigger.py | 27 +++++++++--- scripts/cdist | 109 +++++++++++++++++++++++------------------------ 3 files changed, 111 insertions(+), 89 deletions(-) diff --git a/cdist/config.py b/cdist/config.py index 14c99fe8..2ce50b52 100644 --- a/cdist/config.py +++ b/cdist/config.py @@ -130,6 +130,25 @@ class Config(object): for host in source: yield host + @staticmethod + def construct_remote_exec_copy_patterns(args): + # default remote cmd patterns + args.remote_exec_pattern = None + args.remote_copy_pattern = None + + args_dict = vars(args) + # if remote-exec and/or remote-copy args are None then user + # didn't specify command line options nor env vars: + # inspect multiplexing options for default cdist.REMOTE_COPY/EXEC + if (args_dict['remote_copy'] is None or + args_dict['remote_exec'] is None): + mux_opts = inspect_ssh_mux_opts() + if args_dict['remote_exec'] is None: + args.remote_exec_pattern = cdist.REMOTE_EXEC + mux_opts + if args_dict['remote_copy'] is None: + args.remote_copy_pattern = cdist.REMOTE_COPY + mux_opts + + @classmethod def commandline(cls, args): """Configure remote system""" @@ -166,32 +185,14 @@ class Config(object): failed_hosts = [] time_start = time.time() - # default remote cmd patterns - args.remote_exec_pattern = None - args.remote_copy_pattern = None - - args_dict = vars(args) - # if remote-exec and/or remote-copy args are None then user - # didn't specify command line options nor env vars: - # inspect multiplexing options for default cdist.REMOTE_COPY/EXEC - if (args_dict['remote_copy'] is None or - args_dict['remote_exec'] is None): - mux_opts = inspect_ssh_mux_opts() - if args_dict['remote_exec'] is None: - args.remote_exec_pattern = cdist.REMOTE_EXEC + mux_opts - if args_dict['remote_copy'] is None: - args.remote_copy_pattern = cdist.REMOTE_COPY + mux_opts - - if args.out_path: - base_root_path = args.out_path - else: - base_root_path = tempfile.mkdtemp() + cls.construct_remote_exec_copy_patterns(args) + base_root_path = cls.create_base_root_path(args.out_path) hostcnt = 0 for host in itertools.chain(cls.hosts(args.host), cls.hosts(args.hostfile)): - hostdir = cdist.str_hash(host) - host_base_path = os.path.join(base_root_path, hostdir) + host_base_path, hostdir = cls.create_host_base_dirs( + host, hostdir) log.debug("Base root path for target host \"{}\" is \"{}\"".format( host, host_base_path)) @@ -320,13 +321,22 @@ class Config(object): raise - # FIXME begin to cleanup with this method @staticmethod - def create_host_tmpdir(host): - base_dir = tempfile.mkdtemp() - hostdir = cdist.str_hash(host) + def create_base_root_path(out_path=None): + if out_path: + base_root_path = out_path + else: + base_root_path = tempfile.mkdtemp() - return (base_dir, hostdir) + return base_root_path + + + @staticmethod + def create_host_base_dirs(host, base_root_path): + hostdir = cdist.str_hash(host) + host_base_path = os.path.join(base_root_path, hostdir) + + return (host_base_path, hostdir) def run(self): diff --git a/cdist/trigger.py b/cdist/trigger.py index ba94cba0..9027c063 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -37,13 +37,14 @@ log = logging.getLogger(__name__) class Trigger(): """cdist trigger handling""" - def __init__(self, http_port=None, dry_run=False, ipv4only=False): + def __init__(self, http_port=None, dry_run=False, ipv4only=False, + cdistargs=None): self.log = logging.getLogger("trigger") self.dry_run = dry_run self.http_port = int(http_port) self.ipv4only = ipv4only - self.args = "fun" + self.args = cdistargs # can only be set once multiprocessing.set_start_method('forkserver') @@ -68,7 +69,11 @@ class Trigger(): @staticmethod def commandline(args): - t = Trigger(http_port=args.http_port, ipv4only=args.ipv4) + http_port = args.http_port + ipv4only = args.ipv4 + del args.http_port + del args.ipv4 + t = Trigger(http_port=http_port, ipv4only=ipv4only, cdistargs=args) t.run() class TriggerHttp(BaseHTTPRequestHandler): @@ -78,13 +83,16 @@ class TriggerHttp(BaseHTTPRequestHandler): code = 200 mode = None - print(self.server.cdistargs) + self.cdistargs = self.server.cdistargs + print(self.cdistargs) + print('path: ' + str(self.path)) m = re.match("^/(?Pconfig|install)/.*", self.path) if m: mode = m.group('mode') else: code = 404 + print('mode: ' + str(mode)) if mode: self.run_cdist(mode, host) @@ -105,8 +113,15 @@ class TriggerHttp(BaseHTTPRequestHandler): module = getattr(cdist, mode) theclass = getattr(module, cname) - host_base_path, hostdir = theclass.create_host_tmpdir(host) - theclass.onehost(host, host_base_path, hostdir, args, parallel=False) + if hasattr(self.cdistargs, 'out_path'): + out_path = self.cdistargs.out_path + else: + out_path = None + host_base_path, hostdir = theclass.create_host_base_dirs( + host, theclass.create_base_root_path(out_path)) + theclass.construct_remote_exec_copy_patterns(self.cdistargs) + theclass.onehost(host, host_base_path, hostdir, self.cdistargs, + parallel=False) class HTTPServerV6(http.server.HTTPServer): diff --git a/scripts/cdist b/scripts/cdist index 1adbdc9c..621020f5 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -105,20 +105,57 @@ def commandline(): 'banner', parents=[parser['loglevel']]) parser['banner'].set_defaults(func=cdist.banner.banner) - # Config - parser['config'] = parser['sub'].add_parser( - 'config', parents=[parser['loglevel']]) - parser['config'].add_argument( - 'host', nargs='*', help='host(s) to operate on') - parser['config'].add_argument( - '-b', '--enable-beta', - help=('Enable beta functionalities. Beta functionalities ' - 'include the following options: -j/--jobs.'), - action='store_true', dest='beta', default=False) - parser['config'].add_argument( + parser['config_main'] = argparse.ArgumentParser(add_help=False) + parser['config_main'].add_argument( '-c', '--conf-dir', help=('Add configuration directory (can be repeated, ' 'last one wins)'), action='append') + parser['config_main'].add_argument( + '-i', '--initial-manifest', + help='path to a cdist manifest or \'-\' to read from stdin.', + dest='manifest', required=False) + parser['config_main'].add_argument( + '-j', '--jobs', nargs='?', type=check_positive_int, + help=('specify the maximum number of parallel jobs, currently ' + 'only global explorers are supported (currently in beta'), + action='store', dest='jobs', + const=multiprocessing.cpu_count()) + parser['config_main'].add_argument( + '-n', '--dry-run', + help='do not execute code', action='store_true') + parser['config_main'].add_argument( + '-o', '--out-dir', + help='directory to save cdist output in', dest="out_path") + + # remote-copy and remote-exec defaults are environment variables + # if set; if not then None - these will be futher handled after + # parsing to determine implementation default + parser['config_main'].add_argument( + '--remote-copy', + help='Command to use for remote copy (should behave like scp)', + action='store', dest='remote_copy', + default=os.environ.get('CDIST_REMOTE_COPY')) + parser['config_main'].add_argument( + '--remote-exec', + help=('Command to use for remote execution ' + '(should behave like ssh)'), + action='store', dest='remote_exec', + default=os.environ.get('CDIST_REMOTE_EXEC')) + + # Config + parser['config'] = parser['sub'].add_parser( + 'config', parents=[parser['loglevel'], parser['beta'], + parser['config_main']]) + parser['config'].add_argument( + 'host', nargs='*', help='host(s) to operate on') + parser['config'].add_argument( + '-s', '--sequential', + help='operate on multiple hosts sequentially (default)', + action='store_false', dest='parallel') + parser['config'].add_argument( + '-p', '--parallel', + help='operate on multiple hosts in parallel', + action='store_true', dest='parallel') parser['config'].add_argument( '-f', '--file', help=('Read additional hosts to operate on from specified file ' @@ -126,45 +163,6 @@ def commandline(): 'If no host or host file is specified then, by default, ' 'read hosts from stdin.'), dest='hostfile', required=False) - parser['config'].add_argument( - '-i', '--initial-manifest', - help='Path to a cdist manifest or \'-\' to read from stdin.', - dest='manifest', required=False) - parser['config'].add_argument( - '-j', '--jobs', nargs='?', type=check_positive_int, - help=('Specify the maximum number of parallel jobs, currently ' - 'only global explorers are supported (currently in beta'), - action='store', dest='jobs', - const=multiprocessing.cpu_count()) - parser['config'].add_argument( - '-n', '--dry-run', - help='Do not execute code', action='store_true') - parser['config'].add_argument( - '-o', '--out-dir', - help='Directory to save cdist output in', dest="out_path") - parser['config'].add_argument( - '-p', '--parallel', - help='Operate on multiple hosts in parallel', - action='store_true', dest='parallel') - parser['config'].add_argument( - '-s', '--sequential', - help='Operate on multiple hosts sequentially (default)', - action='store_false', dest='parallel') - - # remote-copy and remote-exec defaults are environment variables - # if set; if not then None - these will be futher handled after - # parsing to determine implementation default - parser['config'].add_argument( - '--remote-copy', - help='Command to use for remote copy (should behave like scp)', - action='store', dest='remote_copy', - default=os.environ.get('CDIST_REMOTE_COPY')) - parser['config'].add_argument( - '--remote-exec', - help=('Command to use for remote execution ' - '(should behave like ssh)'), - action='store', dest='remote_exec', - default=os.environ.get('CDIST_REMOTE_EXEC')) parser['config'].set_defaults(func=cdist.config.Config.commandline) # Shell @@ -178,16 +176,15 @@ def commandline(): # Trigger parser['trigger'] = parser['sub'].add_parser( - 'trigger', parents=[parser['loglevel'], parser['beta']]) + 'trigger', parents=[parser['loglevel'], + parser['beta'], + parser['config_main']]) parser['trigger'].add_argument( - '-4', '--ipv4', + '-4', '--ipv4', default=False, help=('Listen only to IPv4 (instead of IPv4 and IPv6)'), action='store_true') parser['trigger'].add_argument( - '-H', '--http-port', + '-H', '--http-port', action='store', default=3000, required=False, help=('Create trigger listener via http on specified port')) - parser['trigger'].add_argument( - '-n', '--dry-run', - help='Do not execute code', action='store_true') parser['trigger'].set_defaults(func=cdist.trigger.Trigger.commandline) # Install From 297367390f719853c46ef775acfec79d7ad3135f Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sun, 30 Oct 2016 17:21:08 +0100 Subject: [PATCH 07/18] Fix bug. --- cdist/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/config.py b/cdist/config.py index 2ce50b52..e6251476 100644 --- a/cdist/config.py +++ b/cdist/config.py @@ -192,7 +192,7 @@ class Config(object): for host in itertools.chain(cls.hosts(args.host), cls.hosts(args.hostfile)): host_base_path, hostdir = cls.create_host_base_dirs( - host, hostdir) + host, base_root_path) log.debug("Base root path for target host \"{}\" is \"{}\"".format( host, host_base_path)) From 8c985fe2cb6d5264bc3117eab0e3ffc68f5464ae Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sun, 30 Oct 2016 20:25:32 +0100 Subject: [PATCH 08/18] Add forking support. --- cdist/trigger.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index 9027c063..937a1d40 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -24,6 +24,7 @@ import logging import re import socket import http.server +import socketserver from http.server import BaseHTTPRequestHandler, HTTPServer @@ -49,10 +50,6 @@ class Trigger(): # can only be set once multiprocessing.set_start_method('forkserver') - # Create pool suitable for passing objects - def __init_pool(self): - pass - def run_httpd(self): server_address = ('', self.http_port) @@ -78,7 +75,6 @@ class Trigger(): class TriggerHttp(BaseHTTPRequestHandler): def do_GET(self): - # FIXME: dispatch to pool instead of single process host = self.client_address[0] code = 200 mode = None @@ -124,7 +120,7 @@ class TriggerHttp(BaseHTTPRequestHandler): parallel=False) -class HTTPServerV6(http.server.HTTPServer): +class HTTPServerV6(socketserver.ForkingMixIn, http.server.HTTPServer): """ Server that listens both to IPv4 and IPv6 requests. """ From 63dc9632d267db2a513ca925c762c2bd9ce6f704 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sun, 30 Oct 2016 20:37:24 +0100 Subject: [PATCH 09/18] Make IPv4 default. --- cdist/trigger.py | 24 +++++++++++------------- scripts/cdist | 5 +++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index 937a1d40..3133247a 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -38,25 +38,23 @@ log = logging.getLogger(__name__) class Trigger(): """cdist trigger handling""" - def __init__(self, http_port=None, dry_run=False, ipv4only=False, + def __init__(self, http_port=None, dry_run=False, ipv6=False, cdistargs=None): self.log = logging.getLogger("trigger") self.dry_run = dry_run self.http_port = int(http_port) - self.ipv4only = ipv4only + self.ipv6 = ipv6 self.args = cdistargs - # can only be set once - multiprocessing.set_start_method('forkserver') - def run_httpd(self): server_address = ('', self.http_port) - if self.ipv4only: - httpd = HTTPServerV4(self.args, server_address, TriggerHttp) + if self.ipv6: + httpdcls = HTTPServerV6 else: - httpd = HTTPServerV6(self.args, server_address, TriggerHttp) + httpdcls = HTTPServerV4 + httpd = httpdcls(self.args, server_address, TriggerHttp) httpd.serve_forever() @@ -67,10 +65,10 @@ class Trigger(): @staticmethod def commandline(args): http_port = args.http_port - ipv4only = args.ipv4 + ipv6 = args.ipv6 del args.http_port - del args.ipv4 - t = Trigger(http_port=http_port, ipv4only=ipv4only, cdistargs=args) + del args.ipv6 + t = Trigger(http_port=http_port, ipv6=ipv6, cdistargs=args) t.run() class TriggerHttp(BaseHTTPRequestHandler): @@ -122,7 +120,7 @@ class TriggerHttp(BaseHTTPRequestHandler): class HTTPServerV6(socketserver.ForkingMixIn, http.server.HTTPServer): """ - Server that listens both to IPv4 and IPv6 requests. + Server that listens to both IPv4 and IPv6 requests. """ address_family = socket.AF_INET6 @@ -132,6 +130,6 @@ class HTTPServerV6(socketserver.ForkingMixIn, http.server.HTTPServer): class HTTPServerV4(HTTPServerV6): """ - Server that listens to IPv4 requests + Server that listens to IPv4 requests. """ address_family = socket.AF_INET diff --git a/scripts/cdist b/scripts/cdist index 621020f5..3fff6d67 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -180,8 +180,9 @@ def commandline(): parser['beta'], parser['config_main']]) parser['trigger'].add_argument( - '-4', '--ipv4', default=False, - help=('Listen only to IPv4 (instead of IPv4 and IPv6)'), action='store_true') + '-6', '--ipv6', default=False, + help=('Listen to both IPv4 and IPv6 (instead of only IPv4)'), + action='store_true') parser['trigger'].add_argument( '-H', '--http-port', action='store', default=3000, required=False, help=('Create trigger listener via http on specified port')) From 05cf49274434987e9d786443a6116779bd51638b Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 31 Oct 2016 07:51:27 +0100 Subject: [PATCH 10/18] Implement dry_run option, add log.debug lines. --- cdist/trigger.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index 3133247a..fafe00ef 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -26,8 +26,6 @@ import socket import http.server import socketserver -from http.server import BaseHTTPRequestHandler, HTTPServer - import multiprocessing import cdist.config @@ -44,8 +42,8 @@ class Trigger(): self.dry_run = dry_run self.http_port = int(http_port) self.ipv6 = ipv6 - self.args = cdistargs + log.debug("IPv6: {0}", self.ipv6) def run_httpd(self): server_address = ('', self.http_port) @@ -56,6 +54,7 @@ class Trigger(): httpdcls = HTTPServerV4 httpd = httpdcls(self.args, server_address, TriggerHttp) + log.debug("Starting server at port {}", self.http_port) httpd.serve_forever() def run(self): @@ -71,25 +70,27 @@ class Trigger(): t = Trigger(http_port=http_port, ipv6=ipv6, cdistargs=args) t.run() -class TriggerHttp(BaseHTTPRequestHandler): +class TriggerHttp(http.server.BaseHTTPRequestHandler): def do_GET(self): host = self.client_address[0] code = 200 mode = None self.cdistargs = self.server.cdistargs - print(self.cdistargs) - print('path: ' + str(self.path)) m = re.match("^/(?Pconfig|install)/.*", self.path) if m: mode = m.group('mode') else: code = 404 - print('mode: ' + str(mode)) - if mode: - self.run_cdist(mode, host) + log.debug("Running cdist for {0} in mode {1}", host, mode) + if self.dry_run: + log.info("Dry run, skipping cdist execution") + else: + self.run_cdist(mode, host) + else: + log.info("Unsupported path {1}, ignoring", mode, self.path) self.send_response(code) self.end_headers() @@ -114,6 +115,8 @@ class TriggerHttp(BaseHTTPRequestHandler): host_base_path, hostdir = theclass.create_host_base_dirs( host, theclass.create_base_root_path(out_path)) theclass.construct_remote_exec_copy_patterns(self.cdistargs) + log.debug("Executing cdist onehost with params: {0}, {1}, {2}, {3}, ", + host, host_base_path, hostdir, self.cdistargs) theclass.onehost(host, host_base_path, hostdir, self.cdistargs, parallel=False) From 686a484b0300c35447a3e4ba5907abf4b31daffd Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 31 Oct 2016 07:59:03 +0100 Subject: [PATCH 11/18] Fix dry_run and logging. --- cdist/trigger.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index fafe00ef..0565a64d 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -43,7 +43,7 @@ class Trigger(): self.http_port = int(http_port) self.ipv6 = ipv6 self.args = cdistargs - log.debug("IPv6: {0}", self.ipv6) + log.debug("IPv6: %s", self.ipv6) def run_httpd(self): server_address = ('', self.http_port) @@ -54,7 +54,9 @@ class Trigger(): httpdcls = HTTPServerV4 httpd = httpdcls(self.args, server_address, TriggerHttp) - log.debug("Starting server at port {}", self.http_port) + log.debug("Starting server at port %d", self.http_port) + if self.dry_run: + log.debug("Running in dry run mode") httpd.serve_forever() def run(self): @@ -67,7 +69,8 @@ class Trigger(): ipv6 = args.ipv6 del args.http_port del args.ipv6 - t = Trigger(http_port=http_port, ipv6=ipv6, cdistargs=args) + t = Trigger(http_port=http_port, dry_run=args.dry_run, ipv6=ipv6, + cdistargs=args) t.run() class TriggerHttp(http.server.BaseHTTPRequestHandler): @@ -84,13 +87,13 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): else: code = 404 if mode: - log.debug("Running cdist for {0} in mode {1}", host, mode) - if self.dry_run: + log.debug("Running cdist for %s in mode %s", host, mode) + if self.server.dry_run: log.info("Dry run, skipping cdist execution") else: self.run_cdist(mode, host) else: - log.info("Unsupported path {1}, ignoring", mode, self.path) + log.info("Unsupported mode in path %s, ignoring", self.path) self.send_response(code) self.end_headers() @@ -102,7 +105,7 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): self.do_GET() def run_cdist(self, mode, host): - log.debug("Running cdist {} {}".format(mode, host)) + log.debug("Running cdist {%s} {%s}", mode, host) cname = mode.title() module = getattr(cdist, mode) @@ -115,7 +118,7 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): host_base_path, hostdir = theclass.create_host_base_dirs( host, theclass.create_base_root_path(out_path)) theclass.construct_remote_exec_copy_patterns(self.cdistargs) - log.debug("Executing cdist onehost with params: {0}, {1}, {2}, {3}, ", + log.debug("Executing cdist onehost with params: %s, %s, %s, %s, ", host, host_base_path, hostdir, self.cdistargs) theclass.onehost(host, host_base_path, hostdir, self.cdistargs, parallel=False) @@ -129,6 +132,7 @@ class HTTPServerV6(socketserver.ForkingMixIn, http.server.HTTPServer): def __init__(self, cdistargs, *args, **kwargs): self.cdistargs = cdistargs + self.dry_run = cdistargs.dry_run http.server.HTTPServer.__init__(self, *args, **kwargs) class HTTPServerV4(HTTPServerV6): From 94e8f0b2b2ccaaee2a81bac7f8900ad8c3a3ef44 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 31 Oct 2016 08:05:57 +0100 Subject: [PATCH 12/18] Minor log.debug message fixes. --- cdist/trigger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index 0565a64d..3bed1a6f 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -105,7 +105,7 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): self.do_GET() def run_cdist(self, mode, host): - log.debug("Running cdist {%s} {%s}", mode, host) + log.debug("Running cdist for %s in mode %s", host, mode) cname = mode.title() module = getattr(cdist, mode) From 376a031a951c497051795009610d15be162e30c5 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 31 Oct 2016 08:07:09 +0100 Subject: [PATCH 13/18] Add more log.debug. --- cdist/trigger.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cdist/trigger.py b/cdist/trigger.py index 3bed1a6f..d7cb3308 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -92,6 +92,7 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): log.info("Dry run, skipping cdist execution") else: self.run_cdist(mode, host) + log.debug("cdist run finished") else: log.info("Unsupported mode in path %s, ignoring", self.path) From aac9906fff1fb356707c9c26fbac0540cae13300 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 31 Oct 2016 08:09:38 +0100 Subject: [PATCH 14/18] Remove redundant log.debug. --- cdist/trigger.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index d7cb3308..e781f8ef 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -106,8 +106,6 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): self.do_GET() def run_cdist(self, mode, host): - log.debug("Running cdist for %s in mode %s", host, mode) - cname = mode.title() module = getattr(cdist, mode) theclass = getattr(module, cname) From 7f0ad6665bf9e2ae8260bcec13f47e5b97cf0a40 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 31 Oct 2016 18:42:16 +0100 Subject: [PATCH 15/18] Add trigger to cdist man page. --- docs/src/man1/cdist.rst | 71 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/src/man1/cdist.rst b/docs/src/man1/cdist.rst index 45ce339e..c9384aaa 100644 --- a/docs/src/man1/cdist.rst +++ b/docs/src/man1/cdist.rst @@ -27,6 +27,11 @@ SYNOPSIS cdist shell [-h] [-d] [-v] [-s SHELL] + cdist trigger [-h] [-d] [-v] [-b] [-c CONF_DIR] [-i MANIFEST] + [-j [JOBS]] [-n] [-o OUT_PATH] + [--remote-copy REMOTE_COPY] [--remote-exec REMOTE_EXEC] + [-6] [-H HTTP_PORT] + DESCRIPTION ----------- @@ -148,6 +153,67 @@ usage. Its primary use is for debugging type parameters. Select shell to use, defaults to current shell. Used shell should be POSIX compatible shell. + +TRIGGER +------- +Start trigger (simple http server) that waits for connections. When host +connects then it triggers config or install command, cdist config is then +executed which configures/installs host. +Request path recognies following formats: + +* :strong:`/config/.*` for config +* :strong:`/install/.*` for install + + +.. option:: -6, --ipv6 + + Listen to both IPv4 and IPv6 (instead of only IPv4) + +.. option:: -b, --enable-beta + + Enable beta functionalities. + +.. option:: -c CONF_DIR, --conf-dir CONF_DIR + + Add configuration directory (can be repeated, last one wins) + +.. option:: -d, --debug + + Set log level to debug + +.. option:: -H HTTP_PORT, --http-port HTTP_PORT + + Create trigger listener via http on specified port + +.. option:: -h, --help + + show this help message and exit + +.. option:: -i MANIFEST, --initial-manifest MANIFEST + + path to a cdist manifest or '-' to read from stdin. + +.. option:: -n, --dry-run + + do not execute code + +.. option:: -o OUT_PATH, --out-dir OUT_PATH + + directory to save cdist output in + +.. option:: --remote-copy REMOTE_COPY + + Command to use for remote copy (should behave like scp) + +.. option:: --remote-exec REMOTE_EXEC + + Command to use for remote execution (should behave like ssh) + +.. option:: -v, --verbose + + Set log level to info, be more verbose + + FILES ----- ~/.cdist @@ -199,6 +265,11 @@ EXAMPLES # Install ikq05.ethz.ch with debug enabled % cdist install -d ikq05.ethz.ch + # Start trigger in verbose mode that will configure host using specified + # init manifest + % cdist trigger -b -v -i ~/.cdist/manifest/init-for-triggered + + ENVIRONMENT ----------- TMPDIR, TEMP, TMP From ff31dcada0dc8d1f5acecaeffba89530b0004f25 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 27 Nov 2016 15:59:48 +0100 Subject: [PATCH 16/18] begin to add local file support Signed-off-by: Nico Schottelius --- cdist/trigger.py | 33 ++++++++++++++++++++++++--------- scripts/cdist | 9 +++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index e781f8ef..9ebd92a5 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -36,13 +36,20 @@ log = logging.getLogger(__name__) class Trigger(): """cdist trigger handling""" + # Arguments that are only trigger specific + triggers_args = [ "http_port", "ipv6", "directory", "content" ] + def __init__(self, http_port=None, dry_run=False, ipv6=False, - cdistargs=None): + directory=None, content=None, cdistargs=None): self.log = logging.getLogger("trigger") self.dry_run = dry_run self.http_port = int(http_port) self.ipv6 = ipv6 self.args = cdistargs + + self.directory = directory + self.content = content + log.debug("IPv6: %s", self.ipv6) def run_httpd(self): @@ -63,14 +70,19 @@ class Trigger(): if self.http_port: self.run_httpd() - @staticmethod - def commandline(args): + @classmethod + def commandline(cls, args): http_port = args.http_port ipv6 = args.ipv6 - del args.http_port - del args.ipv6 - t = Trigger(http_port=http_port, dry_run=args.dry_run, ipv6=ipv6, - cdistargs=args) + + ownargs = {} + for targ in cls.triggers_args: + arg = getattr(args, targ) + ownargs[targ] = arg + + del arg + + t = cls(**ownargs, dry_run=args.dry_run, cdistargs=args) t.run() class TriggerHttp(http.server.BaseHTTPRequestHandler): @@ -81,11 +93,14 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): self.cdistargs = self.server.cdistargs - m = re.match("^/(?Pconfig|install)/.*", self.path) + m = re.match("^/(?Pcdist|file)/(?Pcreate|delete|config|install)/", "/cdist/install/").group('subsystem') + if m: - mode = m.group('mode') + subsystem = m.group('subsystem') + action = m.group('action') else: code = 404 + if mode: log.debug("Running cdist for %s in mode %s", host, mode) if self.server.dry_run: diff --git a/scripts/cdist b/scripts/cdist index 271a2704..25423076 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -191,6 +191,15 @@ def commandline(): parser['trigger'].add_argument( '-H', '--http-port', action='store', default=3000, required=False, help=('Create trigger listener via http on specified port')) + + parser['trigger'].add_argument( + '-D', '--directory', action='store', required=False, + help=('Where to create local files')) + + parser['trigger'].add_argument( + '-C', '--content', action='store', required=False, + help=('What to store in created files')) + parser['trigger'].set_defaults(func=cdist.trigger.Trigger.commandline) # Install From 4207124c5264e28dbacadee913b5ebd4fe338076 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 27 Nov 2016 16:16:34 +0100 Subject: [PATCH 17/18] add debug output when known url was triggered Signed-off-by: Nico Schottelius --- cdist/trigger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index 9ebd92a5..c237db08 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -93,11 +93,12 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): self.cdistargs = self.server.cdistargs - m = re.match("^/(?Pcdist|file)/(?Pcreate|delete|config|install)/", "/cdist/install/").group('subsystem') + m = re.match("^/(?Pcdist|file)/(?Pcreate|delete|config|install)/", "/cdist/install/") if m: subsystem = m.group('subsystem') action = m.group('action') + log.debug("Calling {} -> {}".format(subsystem, action)) else: code = 404 From 9afb57412d6999aacf3ca9c9fc03b44c64a16a2a Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 27 Nov 2016 17:11:34 +0100 Subject: [PATCH 18/18] add file support Signed-off-by: Nico Schottelius --- cdist/trigger.py | 84 ++++++++++++++++++++++++++++++++++-------------- scripts/cdist | 4 +-- 2 files changed, 62 insertions(+), 26 deletions(-) diff --git a/cdist/trigger.py b/cdist/trigger.py index c237db08..ae787578 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -20,11 +20,14 @@ # # +import ipaddress import logging import re import socket import http.server +import os import socketserver +import shutil import multiprocessing @@ -37,10 +40,11 @@ class Trigger(): """cdist trigger handling""" # Arguments that are only trigger specific - triggers_args = [ "http_port", "ipv6", "directory", "content" ] + triggers_args = [ "http_port", "ipv6", "directory", "source" ] + def __init__(self, http_port=None, dry_run=False, ipv6=False, - directory=None, content=None, cdistargs=None): + directory=None, source=None, cdistargs=None): self.log = logging.getLogger("trigger") self.dry_run = dry_run self.http_port = int(http_port) @@ -48,7 +52,7 @@ class Trigger(): self.args = cdistargs self.directory = directory - self.content = content + self.source = source log.debug("IPv6: %s", self.ipv6) @@ -59,7 +63,7 @@ class Trigger(): httpdcls = HTTPServerV6 else: httpdcls = HTTPServerV4 - httpd = httpdcls(self.args, server_address, TriggerHttp) + httpd = httpdcls(self.args, self.directory, self.source, server_address, TriggerHttp) log.debug("Starting server at port %d", self.http_port) if self.dry_run: @@ -86,44 +90,73 @@ class Trigger(): t.run() class TriggerHttp(http.server.BaseHTTPRequestHandler): + actions = { "cdist": [ "config", "install" ], + "file": [ "present", "absent" ] + } + + def do_HEAD(self): + self.dispatch_request() + + def do_POST(self): + self.dispatch_request() + def do_GET(self): + self.dispatch_request() + + def dispatch_request(self): host = self.client_address[0] code = 200 - mode = None self.cdistargs = self.server.cdistargs - m = re.match("^/(?Pcdist|file)/(?Pcreate|delete|config|install)/", "/cdist/install/") + # FIXME: generate regexp based on self.actions + m = re.match("^/(?Pcdist|file)/(?Ppresent|absent|config|install)/", self.path) if m: subsystem = m.group('subsystem') action = m.group('action') - log.debug("Calling {} -> {}".format(subsystem, action)) + handler = getattr(self, "handler_" + subsystem) + + if not action in self.actions[subsystem]: + code = 404 else: code = 404 - if mode: - log.debug("Running cdist for %s in mode %s", host, mode) - if self.server.dry_run: - log.info("Dry run, skipping cdist execution") - else: - self.run_cdist(mode, host) - log.debug("cdist run finished") - else: - log.info("Unsupported mode in path %s, ignoring", self.path) + if code == 200: + log.debug("Calling {} -> {}".format(subsystem, action)) + handler(action, host) self.send_response(code) self.end_headers() - def do_HEAD(self): - self.do_GET() + def handler_file(self, action, host): + if not self.server.directory or not self.server.source: + log.info("Cannot server file request: directory or source not setup") + return - def do_POST(self): - self.do_GET() + try: + ipaddress.ip_address(host) + except ValueError: + log.error("Host is not a valid IP address - aborting") + return - def run_cdist(self, mode, host): - cname = mode.title() - module = getattr(cdist, mode) + dst = os.path.join(self.server.directory, host) + + if action == "present": + shutil.copyfile(self.server.source, dst) + if action == "absent": + if os.path.exists(dst): + os.remove(dst) + + def handler_cdist(self, action, host): + log.debug("Running cdist for %s in mode %s", host, mode) + + if self.server.dry_run: + log.info("Dry run, skipping cdist execution") + return + + cname = action.title() + module = getattr(cdist, action) theclass = getattr(module, cname) if hasattr(self.cdistargs, 'out_path'): @@ -145,9 +178,12 @@ class HTTPServerV6(socketserver.ForkingMixIn, http.server.HTTPServer): """ address_family = socket.AF_INET6 - def __init__(self, cdistargs, *args, **kwargs): + def __init__(self, cdistargs, directory, source, *args, **kwargs): self.cdistargs = cdistargs self.dry_run = cdistargs.dry_run + self.directory = directory + self.source = source + http.server.HTTPServer.__init__(self, *args, **kwargs) class HTTPServerV4(HTTPServerV6): diff --git a/scripts/cdist b/scripts/cdist index 25423076..dfadd75f 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -197,8 +197,8 @@ def commandline(): help=('Where to create local files')) parser['trigger'].add_argument( - '-C', '--content', action='store', required=False, - help=('What to store in created files')) + '-S', '--source', action='store', required=False, + help=('Which file to copy for creation')) parser['trigger'].set_defaults(func=cdist.trigger.Trigger.commandline)