mirror of
https://github.com/ansible-collections/community.docker.git
synced 2026-07-29 11:55:04 +00:00
Reformat code with black and isort.
This commit is contained in:
@@ -107,7 +107,7 @@ options:
|
||||
version_added: 3.5.0
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
---
|
||||
# Minimal example using local Docker daemon
|
||||
plugin: community.docker.docker_containers
|
||||
@@ -167,13 +167,16 @@ filters:
|
||||
inventory_hostname.startswith("a")
|
||||
# Exclude all containers that did not match any of the above filters
|
||||
- exclude: true
|
||||
'''
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils._api.errors import (
|
||||
APIError,
|
||||
DockerException,
|
||||
)
|
||||
from ansible_collections.community.docker.plugins.module_utils.common_api import (
|
||||
RequestException,
|
||||
)
|
||||
@@ -183,64 +186,66 @@ from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||
from ansible_collections.community.docker.plugins.plugin_utils.common_api import (
|
||||
AnsibleDockerClient,
|
||||
)
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, DockerException
|
||||
from ansible_collections.community.docker.plugins.plugin_utils.unsafe import make_unsafe
|
||||
from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import parse_filters, filter_host
|
||||
from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import (
|
||||
filter_host,
|
||||
parse_filters,
|
||||
)
|
||||
|
||||
|
||||
MIN_DOCKER_API = None
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
''' Host inventory parser for ansible using Docker daemon as source. '''
|
||||
"""Host inventory parser for ansible using Docker daemon as source."""
|
||||
|
||||
NAME = 'community.docker.docker_containers'
|
||||
NAME = "community.docker.docker_containers"
|
||||
|
||||
def _slugify(self, value):
|
||||
slug = re.sub(r'[^\w-]', '_', value).lower().lstrip('_')
|
||||
return f'docker_{slug}'
|
||||
slug = re.sub(r"[^\w-]", "_", value).lower().lstrip("_")
|
||||
return f"docker_{slug}"
|
||||
|
||||
def _populate(self, client):
|
||||
strict = self.get_option('strict')
|
||||
strict = self.get_option("strict")
|
||||
|
||||
ssh_port = self.get_option('private_ssh_port')
|
||||
default_ip = self.get_option('default_ip')
|
||||
hostname = self.get_option('docker_host')
|
||||
verbose_output = self.get_option('verbose_output')
|
||||
connection_type = self.get_option('connection_type')
|
||||
add_legacy_groups = self.get_option('add_legacy_groups')
|
||||
ssh_port = self.get_option("private_ssh_port")
|
||||
default_ip = self.get_option("default_ip")
|
||||
hostname = self.get_option("docker_host")
|
||||
verbose_output = self.get_option("verbose_output")
|
||||
connection_type = self.get_option("connection_type")
|
||||
add_legacy_groups = self.get_option("add_legacy_groups")
|
||||
|
||||
try:
|
||||
params = {
|
||||
'limit': -1,
|
||||
'all': 1,
|
||||
'size': 0,
|
||||
'trunc_cmd': 0,
|
||||
'since': None,
|
||||
'before': None,
|
||||
"limit": -1,
|
||||
"all": 1,
|
||||
"size": 0,
|
||||
"trunc_cmd": 0,
|
||||
"since": None,
|
||||
"before": None,
|
||||
}
|
||||
containers = client.get_json('/containers/json', params=params)
|
||||
containers = client.get_json("/containers/json", params=params)
|
||||
except APIError as exc:
|
||||
raise AnsibleError(f"Error listing containers: {exc}")
|
||||
|
||||
if add_legacy_groups:
|
||||
self.inventory.add_group('running')
|
||||
self.inventory.add_group('stopped')
|
||||
self.inventory.add_group("running")
|
||||
self.inventory.add_group("stopped")
|
||||
|
||||
extra_facts = {}
|
||||
if self.get_option('configure_docker_daemon'):
|
||||
if self.get_option("configure_docker_daemon"):
|
||||
for option_name, var_name in DOCKER_COMMON_ARGS_VARS.items():
|
||||
value = self.get_option(option_name)
|
||||
if value is not None:
|
||||
extra_facts[var_name] = value
|
||||
|
||||
filters = parse_filters(self.get_option('filters'))
|
||||
filters = parse_filters(self.get_option("filters"))
|
||||
for container in containers:
|
||||
id = container.get('Id')
|
||||
id = container.get("Id")
|
||||
short_id = id[:13]
|
||||
|
||||
try:
|
||||
name = container.get('Names', list())[0].lstrip('/')
|
||||
name = container.get("Names", list())[0].lstrip("/")
|
||||
full_name = name
|
||||
except IndexError:
|
||||
name = short_id
|
||||
@@ -253,66 +258,72 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
full_facts = dict()
|
||||
|
||||
try:
|
||||
inspect = client.get_json('/containers/{0}/json', id)
|
||||
inspect = client.get_json("/containers/{0}/json", id)
|
||||
except APIError as exc:
|
||||
raise AnsibleError(f"Error inspecting container {name} - {exc}")
|
||||
|
||||
state = inspect.get('State') or dict()
|
||||
config = inspect.get('Config') or dict()
|
||||
labels = config.get('Labels') or dict()
|
||||
state = inspect.get("State") or dict()
|
||||
config = inspect.get("Config") or dict()
|
||||
labels = config.get("Labels") or dict()
|
||||
|
||||
running = state.get('Running')
|
||||
running = state.get("Running")
|
||||
|
||||
groups = []
|
||||
|
||||
# Add container to groups
|
||||
image_name = config.get('Image')
|
||||
image_name = config.get("Image")
|
||||
if image_name and add_legacy_groups:
|
||||
groups.append(f'image_{image_name}')
|
||||
groups.append(f"image_{image_name}")
|
||||
|
||||
stack_name = labels.get('com.docker.stack.namespace')
|
||||
stack_name = labels.get("com.docker.stack.namespace")
|
||||
if stack_name:
|
||||
full_facts['docker_stack'] = stack_name
|
||||
full_facts["docker_stack"] = stack_name
|
||||
if add_legacy_groups:
|
||||
groups.append(f'stack_{stack_name}')
|
||||
groups.append(f"stack_{stack_name}")
|
||||
|
||||
service_name = labels.get('com.docker.swarm.service.name')
|
||||
service_name = labels.get("com.docker.swarm.service.name")
|
||||
if service_name:
|
||||
full_facts['docker_service'] = service_name
|
||||
full_facts["docker_service"] = service_name
|
||||
if add_legacy_groups:
|
||||
groups.append(f'service_{service_name}')
|
||||
groups.append(f"service_{service_name}")
|
||||
|
||||
ansible_connection = None
|
||||
if connection_type == 'ssh':
|
||||
if connection_type == "ssh":
|
||||
# Figure out ssh IP and Port
|
||||
try:
|
||||
# Lookup the public facing port Nat'ed to ssh port.
|
||||
network_settings = inspect.get('NetworkSettings') or {}
|
||||
port_settings = network_settings.get('Ports') or {}
|
||||
port = port_settings.get(f'{ssh_port}/tcp')[0]
|
||||
network_settings = inspect.get("NetworkSettings") or {}
|
||||
port_settings = network_settings.get("Ports") or {}
|
||||
port = port_settings.get(f"{ssh_port}/tcp")[0]
|
||||
except (IndexError, AttributeError, TypeError):
|
||||
port = dict()
|
||||
|
||||
try:
|
||||
ip = default_ip if port['HostIp'] == '0.0.0.0' else port['HostIp']
|
||||
ip = default_ip if port["HostIp"] == "0.0.0.0" else port["HostIp"]
|
||||
except KeyError:
|
||||
ip = ''
|
||||
ip = ""
|
||||
|
||||
facts.update(dict(
|
||||
ansible_ssh_host=ip,
|
||||
ansible_ssh_port=port.get('HostPort', 0),
|
||||
))
|
||||
elif connection_type == 'docker-cli':
|
||||
facts.update(dict(
|
||||
ansible_host=full_name,
|
||||
))
|
||||
ansible_connection = 'community.docker.docker'
|
||||
elif connection_type == 'docker-api':
|
||||
facts.update(dict(
|
||||
ansible_host=full_name,
|
||||
))
|
||||
facts.update(
|
||||
dict(
|
||||
ansible_ssh_host=ip,
|
||||
ansible_ssh_port=port.get("HostPort", 0),
|
||||
)
|
||||
)
|
||||
elif connection_type == "docker-cli":
|
||||
facts.update(
|
||||
dict(
|
||||
ansible_host=full_name,
|
||||
)
|
||||
)
|
||||
ansible_connection = "community.docker.docker"
|
||||
elif connection_type == "docker-api":
|
||||
facts.update(
|
||||
dict(
|
||||
ansible_host=full_name,
|
||||
)
|
||||
)
|
||||
facts.update(extra_facts)
|
||||
ansible_connection = 'community.docker.docker_api'
|
||||
ansible_connection = "community.docker.docker_api"
|
||||
|
||||
full_facts.update(facts)
|
||||
for key, value in inspect.items():
|
||||
@@ -323,8 +334,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
|
||||
if ansible_connection:
|
||||
for d in (facts, full_facts):
|
||||
if 'ansible_connection' not in d:
|
||||
d['ansible_connection'] = ansible_connection
|
||||
if "ansible_connection" not in d:
|
||||
d["ansible_connection"] = ansible_connection
|
||||
|
||||
if not filter_host(self, name, full_facts, filters):
|
||||
continue
|
||||
@@ -342,11 +353,17 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
|
||||
# Use constructed if applicable
|
||||
# Composed variables
|
||||
self._set_composite_vars(self.get_option('compose'), full_facts, name, strict=strict)
|
||||
self._set_composite_vars(
|
||||
self.get_option("compose"), full_facts, name, 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'), full_facts, name, strict=strict)
|
||||
self._add_host_to_composed_groups(
|
||||
self.get_option("groups"), full_facts, name, 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'), full_facts, name, strict=strict)
|
||||
self._add_host_to_keyed_groups(
|
||||
self.get_option("keyed_groups"), full_facts, name, strict=strict
|
||||
)
|
||||
|
||||
# We need to do this last since we also add a group called `name`.
|
||||
# When we do this before a set_variable() call, the variables are assigned
|
||||
@@ -362,15 +379,15 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
self.inventory.add_host(name, group=hostname)
|
||||
|
||||
if running is True:
|
||||
self.inventory.add_host(name, group='running')
|
||||
self.inventory.add_host(name, group="running")
|
||||
else:
|
||||
self.inventory.add_host(name, group='stopped')
|
||||
self.inventory.add_host(name, group="stopped")
|
||||
|
||||
def verify_file(self, path):
|
||||
"""Return the possibly of a file being consumable by this plugin."""
|
||||
return (
|
||||
super(InventoryModule, self).verify_file(path) and
|
||||
path.endswith(('docker.yaml', 'docker.yml')))
|
||||
return super(InventoryModule, self).verify_file(path) and path.endswith(
|
||||
("docker.yaml", "docker.yml")
|
||||
)
|
||||
|
||||
def _create_client(self):
|
||||
return AnsibleDockerClient(self, min_docker_api_version=MIN_DOCKER_API)
|
||||
@@ -382,10 +399,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
try:
|
||||
self._populate(client)
|
||||
except DockerException as e:
|
||||
raise AnsibleError(
|
||||
f'An unexpected Docker error occurred: {e}'
|
||||
)
|
||||
raise AnsibleError(f"An unexpected Docker error occurred: {e}")
|
||||
except RequestException as e:
|
||||
raise AnsibleError(
|
||||
f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}'
|
||||
f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}"
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: docker_machine
|
||||
author: Ximon Eighteen (@ximon18)
|
||||
@@ -59,7 +60,7 @@ options:
|
||||
version_added: 3.5.0
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
---
|
||||
# Minimal example
|
||||
plugin: community.docker.docker_machine
|
||||
@@ -96,57 +97,63 @@ keyed_groups:
|
||||
plugin: community.docker.docker_machine
|
||||
compose:
|
||||
ansible_ssh_common_args: '"-o StrictHostKeyChecking=accept-new"'
|
||||
'''
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||
from ansible.utils.display import Display
|
||||
|
||||
from ansible_collections.community.docker.plugins.plugin_utils.unsafe import make_unsafe
|
||||
from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import parse_filters, filter_host
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
from ansible.module_utils.common.text.converters import to_native, to_text
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
|
||||
from ansible.utils.display import Display
|
||||
from ansible_collections.community.docker.plugins.plugin_utils.unsafe import make_unsafe
|
||||
from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import (
|
||||
filter_host,
|
||||
parse_filters,
|
||||
)
|
||||
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
''' Host inventory parser for ansible using Docker machine as source. '''
|
||||
"""Host inventory parser for ansible using Docker machine as source."""
|
||||
|
||||
NAME = 'community.docker.docker_machine'
|
||||
NAME = "community.docker.docker_machine"
|
||||
|
||||
DOCKER_MACHINE_PATH = None
|
||||
|
||||
def _run_command(self, args):
|
||||
if not self.DOCKER_MACHINE_PATH:
|
||||
try:
|
||||
self.DOCKER_MACHINE_PATH = get_bin_path('docker-machine')
|
||||
self.DOCKER_MACHINE_PATH = get_bin_path("docker-machine")
|
||||
except ValueError as e:
|
||||
raise AnsibleError(to_native(e))
|
||||
|
||||
command = [self.DOCKER_MACHINE_PATH]
|
||||
command.extend(args)
|
||||
display.debug(f'Executing command {command}')
|
||||
display.debug(f"Executing command {command}")
|
||||
try:
|
||||
result = subprocess.check_output(command)
|
||||
except subprocess.CalledProcessError as e:
|
||||
display.warning(f'Exception {type(e).__name__} caught while executing command {command}, this was the original exception: {e}')
|
||||
display.warning(
|
||||
f"Exception {type(e).__name__} caught while executing command {command}, this was the original exception: {e}"
|
||||
)
|
||||
raise e
|
||||
|
||||
return to_text(result).strip()
|
||||
|
||||
def _get_docker_daemon_variables(self, machine_name):
|
||||
'''
|
||||
"""
|
||||
Capture settings from Docker Machine that would be needed to connect to the remote Docker daemon installed on
|
||||
the Docker Machine remote host. Note: passing '--shell=sh' is a workaround for 'Error: Unknown shell'.
|
||||
'''
|
||||
"""
|
||||
try:
|
||||
env_lines = self._run_command(['env', '--shell=sh', machine_name]).splitlines()
|
||||
env_lines = self._run_command(
|
||||
["env", "--shell=sh", machine_name]
|
||||
).splitlines()
|
||||
except subprocess.CalledProcessError:
|
||||
# This can happen when the machine is created but provisioning is incomplete
|
||||
return []
|
||||
@@ -174,9 +181,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
def _get_machine_names(self):
|
||||
# Filter out machines that are not in the Running state as we probably cannot do anything useful actions
|
||||
# with them.
|
||||
ls_command = ['ls', '-q']
|
||||
if self.get_option('running_required'):
|
||||
ls_command.extend(['--filter', 'state=Running'])
|
||||
ls_command = ["ls", "-q"]
|
||||
if self.get_option("running_required"):
|
||||
ls_command.extend(["--filter", "state=Running"])
|
||||
|
||||
try:
|
||||
ls_lines = self._run_command(ls_command)
|
||||
@@ -187,7 +194,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
def _inspect_docker_machine_host(self, node):
|
||||
try:
|
||||
inspect_lines = self._run_command(['inspect', node])
|
||||
inspect_lines = self._run_command(["inspect", node])
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
|
||||
@@ -195,7 +202,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
def _ip_addr_docker_machine_host(self, node):
|
||||
try:
|
||||
ip_addr = self._run_command(['ip', node])
|
||||
ip_addr = self._run_command(["ip", node])
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
|
||||
@@ -203,19 +210,21 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
def _should_skip_host(self, machine_name, env_var_tuples, daemon_env):
|
||||
if not env_var_tuples:
|
||||
warning_prefix = f'Unable to fetch Docker daemon env vars from Docker Machine for host {machine_name}'
|
||||
if daemon_env in ('require', 'require-silently'):
|
||||
if daemon_env == 'require':
|
||||
display.warning(f'{warning_prefix}: host will be skipped')
|
||||
warning_prefix = f"Unable to fetch Docker daemon env vars from Docker Machine for host {machine_name}"
|
||||
if daemon_env in ("require", "require-silently"):
|
||||
if daemon_env == "require":
|
||||
display.warning(f"{warning_prefix}: host will be skipped")
|
||||
return True
|
||||
else: # 'optional', 'optional-silently'
|
||||
if daemon_env == 'optional':
|
||||
display.warning(f'{warning_prefix}: host will lack dm_DOCKER_xxx variables')
|
||||
if daemon_env == "optional":
|
||||
display.warning(
|
||||
f"{warning_prefix}: host will lack dm_DOCKER_xxx variables"
|
||||
)
|
||||
return False
|
||||
|
||||
def _populate(self):
|
||||
daemon_env = self.get_option('daemon_env')
|
||||
filters = parse_filters(self.get_option('filters'))
|
||||
daemon_env = self.get_option("daemon_env")
|
||||
filters = parse_filters(self.get_option("filters"))
|
||||
try:
|
||||
for node in self._get_machine_names():
|
||||
node_attrs = self._inspect_docker_machine_host(node)
|
||||
@@ -224,13 +233,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
unsafe_node_attrs = make_unsafe(node_attrs)
|
||||
|
||||
machine_name = unsafe_node_attrs['Driver']['MachineName']
|
||||
machine_name = unsafe_node_attrs["Driver"]["MachineName"]
|
||||
if not filter_host(self, machine_name, unsafe_node_attrs, filters):
|
||||
continue
|
||||
|
||||
# query `docker-machine env` to obtain remote Docker daemon connection settings in the form of commands
|
||||
# that could be used to set environment variables to influence a local Docker client:
|
||||
if daemon_env == 'skip':
|
||||
if daemon_env == "skip":
|
||||
env_var_tuples = []
|
||||
else:
|
||||
env_var_tuples = self._get_docker_daemon_variables(machine_name)
|
||||
@@ -243,49 +252,82 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
# check for valid ip address from inspect output, else explicitly use ip command to find host ip address
|
||||
# this works around an issue seen with Google Compute Platform where the IP address was not available
|
||||
# via the 'inspect' subcommand but was via the 'ip' subcomannd.
|
||||
if unsafe_node_attrs['Driver']['IPAddress']:
|
||||
ip_addr = unsafe_node_attrs['Driver']['IPAddress']
|
||||
if unsafe_node_attrs["Driver"]["IPAddress"]:
|
||||
ip_addr = unsafe_node_attrs["Driver"]["IPAddress"]
|
||||
else:
|
||||
ip_addr = self._ip_addr_docker_machine_host(node)
|
||||
|
||||
# set standard Ansible remote host connection settings to details captured from `docker-machine`
|
||||
# see: https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
|
||||
self.inventory.set_variable(machine_name, 'ansible_host', make_unsafe(ip_addr))
|
||||
self.inventory.set_variable(machine_name, 'ansible_port', unsafe_node_attrs['Driver']['SSHPort'])
|
||||
self.inventory.set_variable(machine_name, 'ansible_user', unsafe_node_attrs['Driver']['SSHUser'])
|
||||
self.inventory.set_variable(machine_name, 'ansible_ssh_private_key_file', unsafe_node_attrs['Driver']['SSHKeyPath'])
|
||||
self.inventory.set_variable(
|
||||
machine_name, "ansible_host", make_unsafe(ip_addr)
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
machine_name, "ansible_port", unsafe_node_attrs["Driver"]["SSHPort"]
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
machine_name, "ansible_user", unsafe_node_attrs["Driver"]["SSHUser"]
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
machine_name,
|
||||
"ansible_ssh_private_key_file",
|
||||
unsafe_node_attrs["Driver"]["SSHKeyPath"],
|
||||
)
|
||||
|
||||
# set variables based on Docker Machine tags
|
||||
tags = unsafe_node_attrs['Driver'].get('Tags') or ''
|
||||
self.inventory.set_variable(machine_name, 'dm_tags', make_unsafe(tags))
|
||||
tags = unsafe_node_attrs["Driver"].get("Tags") or ""
|
||||
self.inventory.set_variable(machine_name, "dm_tags", make_unsafe(tags))
|
||||
|
||||
# set variables based on Docker Machine env variables
|
||||
for kv in env_var_tuples:
|
||||
self.inventory.set_variable(machine_name, f'dm_{kv[0]}', make_unsafe(kv[1]))
|
||||
self.inventory.set_variable(
|
||||
machine_name, f"dm_{kv[0]}", make_unsafe(kv[1])
|
||||
)
|
||||
|
||||
if self.get_option('verbose_output'):
|
||||
self.inventory.set_variable(machine_name, 'docker_machine_node_attributes', unsafe_node_attrs)
|
||||
if self.get_option("verbose_output"):
|
||||
self.inventory.set_variable(
|
||||
machine_name,
|
||||
"docker_machine_node_attributes",
|
||||
unsafe_node_attrs,
|
||||
)
|
||||
|
||||
# Use constructed if applicable
|
||||
strict = self.get_option('strict')
|
||||
strict = self.get_option("strict")
|
||||
|
||||
# Composed variables
|
||||
self._set_composite_vars(self.get_option('compose'), unsafe_node_attrs, machine_name, strict=strict)
|
||||
self._set_composite_vars(
|
||||
self.get_option("compose"),
|
||||
unsafe_node_attrs,
|
||||
machine_name,
|
||||
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'), unsafe_node_attrs, machine_name, strict=strict)
|
||||
self._add_host_to_composed_groups(
|
||||
self.get_option("groups"),
|
||||
unsafe_node_attrs,
|
||||
machine_name,
|
||||
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'), unsafe_node_attrs, machine_name, strict=strict)
|
||||
self._add_host_to_keyed_groups(
|
||||
self.get_option("keyed_groups"),
|
||||
unsafe_node_attrs,
|
||||
machine_name,
|
||||
strict=strict,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise AnsibleError(f'Unable to fetch hosts from Docker Machine, this was the original exception: {e}') from e
|
||||
raise AnsibleError(
|
||||
f"Unable to fetch hosts from Docker Machine, this was the original exception: {e}"
|
||||
) from e
|
||||
|
||||
def verify_file(self, path):
|
||||
"""Return the possibility of a file being consumable by this plugin."""
|
||||
return (
|
||||
super(InventoryModule, self).verify_file(path) and
|
||||
path.endswith(('docker_machine.yaml', 'docker_machine.yml')))
|
||||
return super(InventoryModule, self).verify_file(path) and path.endswith(
|
||||
("docker_machine.yaml", "docker_machine.yml")
|
||||
)
|
||||
|
||||
def parse(self, inventory, loader, path, cache=True):
|
||||
super(InventoryModule, self).parse(inventory, loader, path, cache)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: docker_swarm
|
||||
author:
|
||||
@@ -103,7 +104,7 @@ options:
|
||||
version_added: 3.5.0
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
---
|
||||
# Minimal example using local docker
|
||||
plugin: community.docker.docker_swarm
|
||||
@@ -146,126 +147,176 @@ keyed_groups:
|
||||
# hint: labels containing special characters will be converted to safe names
|
||||
- key: 'Spec.Labels'
|
||||
prefix: label
|
||||
'''
|
||||
"""
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible_collections.community.docker.plugins.module_utils.common import get_connect_params
|
||||
from ansible_collections.community.docker.plugins.module_utils.util import update_tls_hostname
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
||||
from ansible.parsing.utils.addresses import parse_address
|
||||
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||
get_connect_params,
|
||||
)
|
||||
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||
update_tls_hostname,
|
||||
)
|
||||
from ansible_collections.community.docker.plugins.plugin_utils.unsafe import make_unsafe
|
||||
from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import parse_filters, filter_host
|
||||
from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import (
|
||||
filter_host,
|
||||
parse_filters,
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
import docker
|
||||
|
||||
HAS_DOCKER = True
|
||||
except ImportError:
|
||||
HAS_DOCKER = False
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
''' Host inventory parser for ansible using Docker swarm as source. '''
|
||||
"""Host inventory parser for ansible using Docker swarm as source."""
|
||||
|
||||
NAME = 'community.docker.docker_swarm'
|
||||
NAME = "community.docker.docker_swarm"
|
||||
|
||||
def _fail(self, msg):
|
||||
raise AnsibleError(msg)
|
||||
|
||||
def _populate(self):
|
||||
raw_params = dict(
|
||||
docker_host=self.get_option('docker_host'),
|
||||
tls=self.get_option('tls'),
|
||||
tls_verify=self.get_option('validate_certs'),
|
||||
key_path=self.get_option('client_key'),
|
||||
cacert_path=self.get_option('ca_path'),
|
||||
cert_path=self.get_option('client_cert'),
|
||||
tls_hostname=self.get_option('tls_hostname'),
|
||||
api_version=self.get_option('api_version'),
|
||||
timeout=self.get_option('timeout'),
|
||||
use_ssh_client=self.get_option('use_ssh_client'),
|
||||
docker_host=self.get_option("docker_host"),
|
||||
tls=self.get_option("tls"),
|
||||
tls_verify=self.get_option("validate_certs"),
|
||||
key_path=self.get_option("client_key"),
|
||||
cacert_path=self.get_option("ca_path"),
|
||||
cert_path=self.get_option("client_cert"),
|
||||
tls_hostname=self.get_option("tls_hostname"),
|
||||
api_version=self.get_option("api_version"),
|
||||
timeout=self.get_option("timeout"),
|
||||
use_ssh_client=self.get_option("use_ssh_client"),
|
||||
debug=None,
|
||||
)
|
||||
update_tls_hostname(raw_params)
|
||||
connect_params = get_connect_params(raw_params, fail_function=self._fail)
|
||||
self.client = docker.DockerClient(**connect_params)
|
||||
self.inventory.add_group('all')
|
||||
self.inventory.add_group('manager')
|
||||
self.inventory.add_group('worker')
|
||||
self.inventory.add_group('leader')
|
||||
self.inventory.add_group('nonleaders')
|
||||
self.inventory.add_group("all")
|
||||
self.inventory.add_group("manager")
|
||||
self.inventory.add_group("worker")
|
||||
self.inventory.add_group("leader")
|
||||
self.inventory.add_group("nonleaders")
|
||||
|
||||
filters = parse_filters(self.get_option('filters'))
|
||||
filters = parse_filters(self.get_option("filters"))
|
||||
|
||||
if self.get_option('include_host_uri'):
|
||||
if self.get_option('include_host_uri_port'):
|
||||
host_uri_port = str(self.get_option('include_host_uri_port'))
|
||||
elif self.get_option('tls') or self.get_option('validate_certs'):
|
||||
host_uri_port = '2376'
|
||||
if self.get_option("include_host_uri"):
|
||||
if self.get_option("include_host_uri_port"):
|
||||
host_uri_port = str(self.get_option("include_host_uri_port"))
|
||||
elif self.get_option("tls") or self.get_option("validate_certs"):
|
||||
host_uri_port = "2376"
|
||||
else:
|
||||
host_uri_port = '2375'
|
||||
host_uri_port = "2375"
|
||||
|
||||
try:
|
||||
self.nodes = self.client.nodes.list()
|
||||
for node in self.nodes:
|
||||
node_attrs = self.client.nodes.get(node.id).attrs
|
||||
unsafe_node_attrs = make_unsafe(node_attrs)
|
||||
if not filter_host(self, unsafe_node_attrs['ID'], unsafe_node_attrs, filters):
|
||||
if not filter_host(
|
||||
self, unsafe_node_attrs["ID"], unsafe_node_attrs, filters
|
||||
):
|
||||
continue
|
||||
self.inventory.add_host(unsafe_node_attrs['ID'])
|
||||
self.inventory.add_host(unsafe_node_attrs['ID'], group=unsafe_node_attrs['Spec']['Role'])
|
||||
self.inventory.set_variable(unsafe_node_attrs['ID'], 'ansible_host',
|
||||
unsafe_node_attrs['Status']['Addr'])
|
||||
if self.get_option('include_host_uri'):
|
||||
self.inventory.set_variable(unsafe_node_attrs['ID'], 'ansible_host_uri',
|
||||
make_unsafe('tcp://' + unsafe_node_attrs['Status']['Addr'] + ':' + host_uri_port))
|
||||
if self.get_option('verbose_output'):
|
||||
self.inventory.set_variable(unsafe_node_attrs['ID'], 'docker_swarm_node_attributes', unsafe_node_attrs)
|
||||
if 'ManagerStatus' in unsafe_node_attrs:
|
||||
if unsafe_node_attrs['ManagerStatus'].get('Leader'):
|
||||
self.inventory.add_host(unsafe_node_attrs["ID"])
|
||||
self.inventory.add_host(
|
||||
unsafe_node_attrs["ID"], group=unsafe_node_attrs["Spec"]["Role"]
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
unsafe_node_attrs["ID"],
|
||||
"ansible_host",
|
||||
unsafe_node_attrs["Status"]["Addr"],
|
||||
)
|
||||
if self.get_option("include_host_uri"):
|
||||
self.inventory.set_variable(
|
||||
unsafe_node_attrs["ID"],
|
||||
"ansible_host_uri",
|
||||
make_unsafe(
|
||||
"tcp://"
|
||||
+ unsafe_node_attrs["Status"]["Addr"]
|
||||
+ ":"
|
||||
+ host_uri_port
|
||||
),
|
||||
)
|
||||
if self.get_option("verbose_output"):
|
||||
self.inventory.set_variable(
|
||||
unsafe_node_attrs["ID"],
|
||||
"docker_swarm_node_attributes",
|
||||
unsafe_node_attrs,
|
||||
)
|
||||
if "ManagerStatus" in unsafe_node_attrs:
|
||||
if unsafe_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
|
||||
swarm_leader_ip = parse_address(node_attrs['ManagerStatus']['Addr'])[0] or \
|
||||
unsafe_node_attrs['Status']['Addr']
|
||||
if self.get_option('include_host_uri'):
|
||||
self.inventory.set_variable(unsafe_node_attrs['ID'], 'ansible_host_uri',
|
||||
make_unsafe('tcp://' + swarm_leader_ip + ':' + host_uri_port))
|
||||
self.inventory.set_variable(unsafe_node_attrs['ID'], 'ansible_host', make_unsafe(swarm_leader_ip))
|
||||
self.inventory.add_host(unsafe_node_attrs['ID'], group='leader')
|
||||
swarm_leader_ip = (
|
||||
parse_address(node_attrs["ManagerStatus"]["Addr"])[0]
|
||||
or unsafe_node_attrs["Status"]["Addr"]
|
||||
)
|
||||
if self.get_option("include_host_uri"):
|
||||
self.inventory.set_variable(
|
||||
unsafe_node_attrs["ID"],
|
||||
"ansible_host_uri",
|
||||
make_unsafe(
|
||||
"tcp://" + swarm_leader_ip + ":" + host_uri_port
|
||||
),
|
||||
)
|
||||
self.inventory.set_variable(
|
||||
unsafe_node_attrs["ID"],
|
||||
"ansible_host",
|
||||
make_unsafe(swarm_leader_ip),
|
||||
)
|
||||
self.inventory.add_host(unsafe_node_attrs["ID"], group="leader")
|
||||
else:
|
||||
self.inventory.add_host(unsafe_node_attrs['ID'], group='nonleaders')
|
||||
self.inventory.add_host(
|
||||
unsafe_node_attrs["ID"], group="nonleaders"
|
||||
)
|
||||
else:
|
||||
self.inventory.add_host(unsafe_node_attrs['ID'], group='nonleaders')
|
||||
self.inventory.add_host(unsafe_node_attrs["ID"], group="nonleaders")
|
||||
# Use constructed if applicable
|
||||
strict = self.get_option('strict')
|
||||
strict = self.get_option("strict")
|
||||
# Composed variables
|
||||
self._set_composite_vars(self.get_option('compose'),
|
||||
unsafe_node_attrs,
|
||||
unsafe_node_attrs['ID'],
|
||||
strict=strict)
|
||||
self._set_composite_vars(
|
||||
self.get_option("compose"),
|
||||
unsafe_node_attrs,
|
||||
unsafe_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'),
|
||||
unsafe_node_attrs,
|
||||
unsafe_node_attrs['ID'],
|
||||
strict=strict)
|
||||
self._add_host_to_composed_groups(
|
||||
self.get_option("groups"),
|
||||
unsafe_node_attrs,
|
||||
unsafe_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'),
|
||||
unsafe_node_attrs,
|
||||
unsafe_node_attrs['ID'],
|
||||
strict=strict)
|
||||
self._add_host_to_keyed_groups(
|
||||
self.get_option("keyed_groups"),
|
||||
unsafe_node_attrs,
|
||||
unsafe_node_attrs["ID"],
|
||||
strict=strict,
|
||||
)
|
||||
except Exception as e:
|
||||
raise AnsibleError(f'Unable to fetch hosts from Docker swarm API, this was the original exception: {e}')
|
||||
raise AnsibleError(
|
||||
f"Unable to fetch hosts from Docker swarm API, this was the original exception: {e}"
|
||||
)
|
||||
|
||||
def verify_file(self, path):
|
||||
"""Return the possibly of a file being consumable by this plugin."""
|
||||
return (
|
||||
super(InventoryModule, self).verify_file(path) and
|
||||
path.endswith(('docker_swarm.yaml', 'docker_swarm.yml')))
|
||||
return super(InventoryModule, self).verify_file(path) and path.endswith(
|
||||
("docker_swarm.yaml", "docker_swarm.yml")
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user