Last active
August 29, 2015 14:21
-
-
Save ppmathis/269618ba001b51abb0d2 to your computer and use it in GitHub Desktop.
Ansible Connection Bootstrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
# First, we are going to check if the netcat binary is available. | |
# This is required to quickly check if the preferred SSH port is reachable. | |
- name: Connection Bootstrapper >> Check if 'nc' binary is available | |
delegate_to: 127.0.0.1 | |
local_action: stat path=/usr/bin/nc | |
register: nc_binary_check | |
- name: Connection Bootstrapper >> Abort execution if 'nc' binary does not exist | |
fail: | |
msg: "The configuration host does not provide the 'nc' binary (netcat), please install and retry." | |
when: not nc_binary_check.stat.exists | |
# Here we are checking if the preferred SSH port is reachable and if the | |
# test succeeds, we try connecting and authenticating against the host. | |
- name: Connection Bootstrapper >> Check if preferred SSH port is reachable | |
local_action: shell /usr/bin/nc -w5 {{ inventory_hostname }} {{ deployment.ssh_port }} | |
register: preferred_port_check | |
failed_when: preferred_port_check.stderr | |
changed_when: false | |
ignore_errors: true | |
- name: Connection Bootstrapper >> [Preferred Credentials] Set host facts | |
set_fact: | |
ansible_ssh_user: '{{ deployment.ssh_user }}' | |
ansible_ssh_port: '{{ deployment.ssh_port }}' | |
when: preferred_port_check|success | |
- name: Connection Bootstrapper >> [Preferred Credentials] Try connecting and authenticating against host | |
action: ping | |
environment: | |
soft_connection_errors: true | |
register: preferred_credentials_check | |
when: preferred_port_check|success | |
ignore_errors: true | |
# If the preferred credentials failed, because either the port or connection check | |
# failed, we try if the fallback credentials work. This is especially useful for | |
# bootstrapping servers which do not have the required SSH configurations. | |
- name: Connection Bootstrapper >> [Fallback Credentials] Set host facts | |
set_fact: | |
ansible_ssh_user: '{{ deployment.ssh_fallback_user }}' | |
ansible_ssh_port: '{{ deployment.ssh_fallback_port }}' | |
when: preferred_port_check|failed or preferred_credentials_check|failed | |
- name: Connection Bootstrapper >> [Fallback Credentials] Try connecting and authenticating against host | |
action: ping | |
environment: | |
soft_connection_errors: true | |
register: fallback_credentials_check | |
when: preferred_port_check|failed or preferred_credentials_check|failed | |
ignore_errors: true | |
# If both credentials failed, we abort the execution for the host before any other | |
# play gets executed. Hereby we are able to ensure that the user gets a nice error message. | |
- name: Connection Bootstrapper >> Abort execution if both connection credentials have failed | |
fail: | |
msg: "Connection to host seems to be impossible, all tries have failed." | |
when: (preferred_port_check|failed or preferred_credentials_check|failed) and fallback_credentials_check|failed |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ansible.runner | |
class object(object): pass | |
class SoftConnectionErrorRunner(ansible.runner.Runner): | |
def __init__(self, *args, **kwargs): | |
super(SoftConnectionErrorRunner, self).__init__(*args, **kwargs) | |
# Array which contains the hostnames where connection errors were suppressed | |
self.__ignored_hosts = [] | |
# Wrap our SoftConnectionErrrorRunnerCallbacks() class around the supplied XYZRunnerCallbacks() class | |
if not isinstance(self.callbacks, SoftConnectionErrorRunnerCallbacks): | |
SoftConnectionErrorRunnerCallbacks.__bases__ = (self.callbacks.__class__,) | |
self.callbacks.__class__ = SoftConnectionErrorRunnerCallbacks | |
def _partition_results(self, results): | |
# All connection errors which were suppressed should be now changed to failed results. | |
# This can be done by adjusting the ReturnData object manually and calling the appropriate callback. | |
for result in results: | |
if result.host in self.__ignored_hosts: | |
result.comm_ok = True | |
self.callbacks.on_failed(result.host, result.result, True) | |
return super(SoftConnectionErrorRunner, self)._partition_results(results) | |
class SoftConnectionErrorRunnerCallbacks(object): | |
def on_unreachable(self, host, res): | |
# Check if the task turned soft connection errors on and if not, just proceed with the usual callbacks | |
if not self.task.environment.get('soft_connection_errors', False): | |
return super(SoftConnectionErrorRunnerCallbacks, self).on_unreachable(host, res) | |
# Do not execute any 3rd-party callbacks and remember the hostname so that it can be whitelisted later on | |
if host not in self.runner._SoftConnectionErrorRunner__ignored_hosts: | |
self.runner._SoftConnectionErrorRunner__ignored_hosts.append(host) | |
class CallbackModule(object): | |
def __init__(self): | |
# Overwrite the Ansible Runner() class with our own, which hooks a few methods of the original class. | |
# This might break in future releases, but seems to be the best method to achieve what we are looking for. | |
ansible.runner.Runner = SoftConnectionErrorRunner |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment