From c1ed47933be23c34789c5b97d070896c99acf769 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Mon, 4 Nov 2013 17:20:03 -0500 Subject: [PATCH] Fixes #4485 add an ipv6 parameter to accelerate so that the daemon will bind to an ipv6 port instead of ipv4 --- lib/ansible/playbook/__init__.py | 2 ++ lib/ansible/playbook/play.py | 6 ++-- lib/ansible/runner/__init__.py | 2 ++ .../runner/connection_plugins/accelerate.py | 2 +- library/utilities/accelerate | 28 ++++++++++++++++--- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 7625c7ec8a..80f4d04884 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -315,6 +315,7 @@ class PlayBook(object): transport=task.transport, sudo_pass=task.sudo_pass, is_playbook=True, check=self.check, diff=self.diff, environment=task.environment, complex_args=task.args, accelerate=task.play.accelerate, accelerate_port=task.play.accelerate_port, + accelerate_ipv6=task.play.accelerate_ipv6, error_on_undefined_vars=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR ) @@ -456,6 +457,7 @@ class PlayBook(object): transport=play.transport, sudo_pass=self.sudo_pass, is_playbook=True, module_vars=play.vars, default_vars=play.default_vars, check=self.check, diff=self.diff, accelerate=play.accelerate, accelerate_port=play.accelerate_port, + accelerate_ipv6=accelerate_ipv6, ).run() self.stats.compute(setup_results, setup=True) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 47f80f0611..b1d49e61a5 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -32,7 +32,7 @@ class Play(object): __slots__ = [ 'hosts', 'name', 'vars', 'default_vars', 'vars_prompt', 'vars_files', 'handlers', 'remote_user', 'remote_port', 'included_roles', 'accelerate', - 'accelerate_port', 'sudo', 'sudo_user', 'transport', 'playbook', + 'accelerate_port', 'accelerate_ipv6', 'sudo', 'sudo_user', 'transport', 'playbook', 'tags', 'gather_facts', 'serial', '_ds', '_handlers', '_tasks', 'basedir', 'any_errors_fatal', 'roles', 'max_fail_pct' ] @@ -41,7 +41,7 @@ class Play(object): # and don't line up 1:1 with how they are stored VALID_KEYS = [ 'hosts', 'name', 'vars', 'vars_prompt', 'vars_files', - 'tasks', 'handlers', 'remote_user', 'user', 'port', 'include', 'accelerate', 'accelerate_port', + 'tasks', 'handlers', 'remote_user', 'user', 'port', 'include', 'accelerate', 'accelerate_port', 'accelerate_ipv6', 'sudo', 'sudo_user', 'connection', 'tags', 'gather_facts', 'serial', 'any_errors_fatal', 'roles', 'pre_tasks', 'post_tasks', 'max_fail_percentage' ] @@ -104,7 +104,6 @@ class Play(object): raise errors.AnsibleError('hosts declaration is required') elif isinstance(hosts, list): hosts = ';'.join(hosts) - self.serial = int(ds.get('serial', 0)) self.hosts = hosts self.name = ds.get('name', self.hosts) @@ -120,6 +119,7 @@ class Play(object): self.any_errors_fatal = utils.boolean(ds.get('any_errors_fatal', 'false')) self.accelerate = utils.boolean(ds.get('accelerate', 'false')) self.accelerate_port = ds.get('accelerate_port', None) + self.accelerate_ipv6 = ds.get('accelerate_ipv6', False) self.max_fail_pct = int(ds.get('max_fail_percentage', 100)) load_vars = {} diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 0063c42e4b..202c31d5af 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -138,6 +138,7 @@ class Runner(object): complex_args=None, # structured data in addition to module_args, must be a dict error_on_undefined_vars=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR, # ex. False accelerate=False, # use accelerated connection + accelerate_ipv6=False, # accelerated connection w/ IPv6 accelerate_port=None, # port to use with accelerated connection ): @@ -183,6 +184,7 @@ class Runner(object): self.error_on_undefined_vars = error_on_undefined_vars self.accelerate = accelerate self.accelerate_port = accelerate_port + self.accelerate_ipv6 = accelerate_ipv6 self.callbacks.runner = self self.original_transport = self.transport diff --git a/lib/ansible/runner/connection_plugins/accelerate.py b/lib/ansible/runner/connection_plugins/accelerate.py index 91adf7bb11..89f6a1e104 100644 --- a/lib/ansible/runner/connection_plugins/accelerate.py +++ b/lib/ansible/runner/connection_plugins/accelerate.py @@ -84,7 +84,7 @@ class Connection(object): utils.AES_KEYS = self.runner.aes_keys def _execute_accelerate_module(self): - args = "password=%s port=%s debug=%d" % (base64.b64encode(self.key.__str__()), str(self.accport), int(utils.VERBOSITY)) + args = "password=%s port=%s debug=%d ipv6=%s" % (base64.b64encode(self.key.__str__()), str(self.accport), int(utils.VERBOSITY), self.runner.accelerate_ipv6) inject = dict(password=self.key) if self.runner.accelerate_inventory_host: inject = utils.combine_vars(inject, self.runner.inventory.get_variables(self.runner.accelerate_inventory_host)) diff --git a/library/utilities/accelerate b/library/utilities/accelerate index d57562fb50..7ff2e38cd6 100644 --- a/library/utilities/accelerate +++ b/library/utilities/accelerate @@ -47,6 +47,12 @@ options: this number of minutes before turning itself off. required: false default: 30 + ipv6: + description: + - The listener daemon on the remote host will bind to the ipv6 localhost socket + if this parameter is set to true. + required: false + default: false notes: - See the advanced playbooks chapter for more about using accelerated mode. requirements: [ "python-keyczar" ] @@ -137,7 +143,7 @@ def daemonize_self(module, password, port, minutes): if pid > 0: vvv("exiting pid %s" % pid) # exit first parent - module.exit_json(msg="daemonized accelerate on port %s for %s minutes" % (port, minutes)) + module.exit_json(msg="daemonized accelerate on port %s for %s minutes with pid %s" % (port, minutes, str(pid))) except OSError, e: log("fork #1 failed: %d (%s)" % (e.errno, e.strerror)) sys.exit(1) @@ -190,6 +196,15 @@ class ThreadedTCPServer(SocketServer.ThreadingTCPServer): self.timeout = timeout SocketServer.ThreadingTCPServer.__init__(self, server_address, RequestHandlerClass) +class ThreadedTCPV6Server(SocketServer.ThreadingTCPServer): + def __init__(self, server_address, RequestHandlerClass, module, password, timeout): + self.module = module + self.address_family = socket.AF_INET6 + self.key = AesKey.Read(password) + self.allow_reuse_address = True + self.timeout = timeout + SocketServer.ThreadingTCPServer.__init__(self, server_address, RequestHandlerClass) + class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler): def send_data(self, data): packed_len = struct.pack('!Q', len(data)) @@ -385,7 +400,7 @@ class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler): self.server.module.atomic_move(out_path, final_path) return dict() -def daemonize(module, password, port, timeout, minutes): +def daemonize(module, password, port, timeout, minutes, ipv6): try: daemonize_self(module, password, port, minutes) @@ -395,7 +410,10 @@ def daemonize(module, password, port, timeout, minutes): signal.signal(signal.SIGALRM, catcher) signal.setitimer(signal.ITIMER_REAL, 60 * minutes) - server = ThreadedTCPServer(("0.0.0.0", port), ThreadedTCPRequestHandler, module, password, timeout) + if ipv6: + server = ThreadedTCPV6Server(("::", port), ThreadedTCPRequestHandler, module, password, timeout) + else: + server = ThreadedTCPServer(("0.0.0.0", port), ThreadedTCPRequestHandler, module, password, timeout) server.allow_reuse_address = True vv("serving!") @@ -410,6 +428,7 @@ def main(): module = AnsibleModule( argument_spec = dict( port=dict(required=False, default=5099), + ipv6=dict(required=False, default=False), timeout=dict(required=False, default=300), password=dict(required=True), minutes=dict(required=False, default=30), @@ -423,13 +442,14 @@ def main(): timeout = int(module.params['timeout']) minutes = int(module.params['minutes']) debug = int(module.params['debug']) + ipv6 = bool(module.params['ipv6']) if not HAS_KEYCZAR: module.fail_json(msg="keyczar is not installed (on the remote side)") DEBUG_LEVEL=debug - daemonize(module, password, port, timeout, minutes) + daemonize(module, password, port, timeout, minutes, ipv6) # this is magic, see lib/ansible/module_common.py #<>