From cc0bb54d2c7d74f8bb155ce602dfb0eecc6da752 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Wed, 15 Feb 2017 16:02:54 -0500 Subject: [PATCH] the return of reset_connection allows user to force persistent connection to close, needed for when you want to benefit from changes applied to the current play but persistent connections prevent them from being realized. --- lib/ansible/modules/utilities/helper/meta.py | 18 +++++++++------ lib/ansible/plugins/connection/ssh.py | 24 +++++++++----------- lib/ansible/plugins/strategy/__init__.py | 6 +++-- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/ansible/modules/utilities/helper/meta.py b/lib/ansible/modules/utilities/helper/meta.py index a89bfc5d39..a8d3fd3c33 100644 --- a/lib/ansible/modules/utilities/helper/meta.py +++ b/lib/ansible/modules/utilities/helper/meta.py @@ -40,6 +40,7 @@ options: - "C(clear_facts) (added in 2.1) causes the gathered facts for the hosts specified in the play's list of hosts to be cleared, including the fact cache." - "C(clear_host_errors) (added in 2.1) clears the failed state (if any) from hosts specified in the play's list of hosts." - "C(end_play) (added in 2.2) causes the play to end without failing the host." + - "C(reset_connection) (added in 2.3) interrupts a persistent connection (i.e. ssh + control persist)" choices: ['noop', 'flush_handlers', 'refresh_inventory', 'clear_facts', 'clear_host_errors', 'end_play'] required: true default: null @@ -50,29 +51,32 @@ author: ''' EXAMPLES = ''' -# force all notified handlers to run at this point, not waiting for normal sync points - template: src: new.j2 dest: /etc/config.txt notify: myhandler -- meta: flush_handlers +- name: force all notified handlers to run at this point, not waiting for normal sync points + meta: flush_handlers -# reload inventory, useful with dynamic inventories when play makes changes to the existing hosts -- cloud_guest: # this is fake module +- name: reload inventory, useful with dynamic inventories when play makes changes to the existing hosts + cloud_guest: # this is fake module name: newhost state: present - - name: Refresh inventory to ensure new instaces exist in inventory meta: refresh_inventory - name: Clear gathered facts from all currently targeted hosts meta: clear_facts -# bring host back to play after failure -- copy: +- name: bring host back to play after failure + copy: src: file dest: /etc/file remote_user: imightnothavepermission - meta: clear_host_errors + +- user: name={{ansible_user}} groups=input +- name: reset ssh connection to allow user changes to affect 'current login user' + meta: reset_connection ''' diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py index 7a26d15833..b62e9a08a9 100644 --- a/lib/ansible/plugins/connection/ssh.py +++ b/lib/ansible/plugins/connection/ssh.py @@ -776,18 +776,16 @@ class Connection(ConnectionBase): display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self.host) self._file_transport_command(in_path, out_path, 'get') + def reset(self): + # If we have a persistent ssh connection (ControlPersist), we can ask it to stop listening. + cmd = map(to_bytes, self._build_command(self._play_context.ssh_executable, '-O', 'stop', self.host)) + controlpersist, controlpath = self._persistence_controls(cmd) + if controlpersist: + p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + display.vvv(u'sending stop: %s' % cmd) + + self.close() + def close(self): - # If we have a persistent ssh connection (ControlPersist), we can ask it - # to stop listening. Otherwise, there's nothing to do here. - - # TODO: reenable once winrm issues are fixed - # temporarily disabled as we are forced to currently close connections after every task because of winrm - # if self._connected and self._persistent: - # ssh_executable = self._play_context.ssh_executable - # cmd = self._build_command('ssh', '-O', 'stop', self.host) - # - # cmd = map(to_bytes, cmd) - # p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - # stdout, stderr = p.communicate() - self._connected = False diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index 8b277638d6..9252b6c447 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -902,8 +902,10 @@ class StrategyBase: if not host.name in self._tqm._unreachable_hosts: iterator._host_states[host.name].run_state = iterator.ITERATING_COMPLETE msg="ending play" - #elif meta_action == 'reset_connection': - # connection_info.connection.close() + elif meta_action == 'reset_connection': + connection = connection_loader.get(play_context.connection, play_context, '/dev/null') + connection.reset() + msg= 'reset connection' else: raise AnsibleError("invalid meta action requested: %s" % meta_action, obj=task._ds)