diff --git a/lib/ansible/plugins/inventory/docker_swarm.py b/lib/ansible/plugins/inventory/docker_swarm.py index e440056e21a..f078ed080c3 100644 --- a/lib/ansible/plugins/inventory/docker_swarm.py +++ b/lib/ansible/plugins/inventory/docker_swarm.py @@ -56,6 +56,16 @@ DOCUMENTATION = ''' tls_hostname: description: When verifying the authenticity of the Docker Host server, provide the expected name of the server. type: str + include_host_uri: + description: Toggle to return the additional attribute I(ansible_host_uri) which contains the URI of the + swarm leader in format of M(tcp://172.16.0.1:2376). This value may be used without additional + modification as value of option I(docker_host) in Docker Swarm modules when connecting via API. + The port always defaults to M(2376). + type: bool + default: no + include_host_uri_port: + description: Override the detected port number included in I(ansible_host_uri) + type: int ''' EXAMPLES = ''' @@ -125,7 +135,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable): def _get_tls_connect_params(self): if self.get_option('tls') and self.get_option('cert_path') and self.get_option('key_path'): # TLS with certs and no host verification - tls_config = self._get_tls_config(client_cert=(self.get_option('cert_path'), self.get_option('key_path')), + tls_config = self._get_tls_config(client_cert=(self.get_option('cert_path'), + self.get_option('key_path')), verify=False) return tls_config @@ -137,12 +148,14 @@ class InventoryModule(BaseInventoryPlugin, Constructable): if self.get_option('tls_verify') and self.get_option('cert_path') and self.get_option('key_path'): # TLS with certs and host verification if self.get_option('cacert_path'): - tls_config = self._get_tls_config(client_cert=(self.get_option('cert_path'), self.get_option('key_path')), + tls_config = self._get_tls_config(client_cert=(self.get_option('cert_path'), + self.get_option('key_path')), ca_cert=self.get_option('cacert_path'), verify=True, assert_hostname=self.get_option('tls_hostname')) else: - tls_config = self._get_tls_config(client_cert=(self.get_option('cert_path'), self.get_option('key_path')), + tls_config = self._get_tls_config(client_cert=(self.get_option('cert_path'), + self.get_option('key_path')), verify=True, assert_hostname=self.get_option('tls_hostname')) @@ -165,37 +178,65 @@ class InventoryModule(BaseInventoryPlugin, Constructable): return None def _populate(self): - self.client = docker.DockerClient(base_url=self.get_option('host'), tls=self._get_tls_connect_params()) + self.client = docker.DockerClient(base_url=self.get_option('host'), + tls=self._get_tls_connect_params()) self.inventory.add_group('all') self.inventory.add_group('manager') self.inventory.add_group('worker') self.inventory.add_group('leader') + + if self.get_option('include_host_uri', True): + if self.get_option('include_host_uri_port'): + host_uri_port = self.get_option('include_host_uri_port') + elif self.get_option('tls') or self.get_option('tls_verify'): + host_uri_port = "2376" + else: + host_uri_port = "2375" + try: self.nodes = self.client.nodes.list() for self.node in self.nodes: self.node_attrs = self.client.nodes.get(self.node.id).attrs self.inventory.add_host(self.node_attrs['ID']) self.inventory.add_host(self.node_attrs['ID'], group=self.node_attrs['Spec']['Role']) - self.inventory.set_variable(self.node_attrs['ID'], 'ansible_host', self.node_attrs['Status']['Addr']) + self.inventory.set_variable(self.node_attrs['ID'], 'ansible_host', + self.node_attrs['Status']['Addr']) + if self.get_option('include_host_uri', True): + self.inventory.set_variable(self.node_attrs['ID'], 'ansible_host_uri', + "tcp://" + self.node_attrs['Status']['Addr'] + ":" + host_uri_port) if self.get_option('verbose_output', True): self.inventory.set_variable(self.node_attrs['ID'], 'docker_swarm_node_attributes', self.node_attrs) if 'ManagerStatus' in self.node_attrs: if self.node_attrs['ManagerStatus'].get('Leader'): # This is workaround of bug in Docker when in some cases the Leader IP is 0.0.0.0 # Check moby/moby#35437 for details - self.inventory.set_variable(self.node_attrs['ID'], 'ansible_host', - parse_address(self.node_attrs['ManagerStatus']['Addr'])[0]) + swarm_leader_ip = parse_address(self.node_attrs['ManagerStatus']['Addr'])[0] or \ + self.node_attrs['Status']['Addr'] + if self.get_option('include_host_uri', True): + self.inventory.set_variable(self.node_attrs['ID'], 'ansible_host_uri', "tcp://" + + swarm_leader_ip + ":" + host_uri_port) + self.inventory.set_variable(self.node_attrs['ID'], 'ansible_host', swarm_leader_ip) self.inventory.add_host(self.node_attrs['ID'], group='leader') # Use constructed if applicable strict = self.get_option('strict') # Composed variables - self._set_composite_vars(self.get_option('compose'), self.node_attrs, self.node_attrs['ID'], strict=strict) + self._set_composite_vars(self.get_option('compose'), + self.node_attrs, + self.node_attrs['ID'], + strict=strict) # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group - self._add_host_to_composed_groups(self.get_option('groups'), self.node_attrs, self.node_attrs['ID'], strict=strict) + self._add_host_to_composed_groups(self.get_option('groups'), + self.node_attrs, + self.node_attrs['ID'], + strict=strict) # Create groups based on variable values and add the corresponding hosts to it - self._add_host_to_keyed_groups(self.get_option('keyed_groups'), self.node_attrs, self.node_attrs['ID'], strict=strict) + self._add_host_to_keyed_groups(self.get_option('keyed_groups'), + self.node_attrs, + self.node_attrs['ID'], + strict=strict) except Exception as e: - raise AnsibleError('Unable to fetch hosts from Docker swarm API, this was the original exception: %s' % to_native(e)) + raise AnsibleError('Unable to fetch hosts from Docker swarm API, this was the original exception: %s' % + to_native(e)) def verify_file(self, path): """Return the possibly of a file being consumable by this plugin.""" @@ -205,7 +246,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable): def parse(self, inventory, loader, path, cache=True): if not HAS_DOCKER: - raise AnsibleError('The Docker swarm dynamic inventory plugin requires the Docker SDK for Python: https://github.com/docker/docker-py.') + raise AnsibleError('The Docker swarm dynamic inventory plugin requires the Docker SDK for Python: ' + 'https://github.com/docker/docker-py.') super(InventoryModule, self).parse(inventory, loader, path, cache) self._read_config_data(path) self._populate()