forked from CCCHH/ansible-infra
Vendor Galaxy Roles and Collections
This commit is contained in:
parent
c1e1897cda
commit
2aed20393f
3553 changed files with 387444 additions and 2 deletions
|
|
@ -0,0 +1,410 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2019 Piotr Wojciechowski <piotr@it-playground.pl>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
module: docker_swarm_info
|
||||
|
||||
short_description: Retrieves facts about Docker Swarm cluster
|
||||
|
||||
description:
|
||||
- Retrieves facts about a Docker Swarm.
|
||||
- Returns lists of swarm objects names for the services - nodes, services, tasks.
|
||||
- The output differs depending on API version available on docker host.
|
||||
- Must be run on Swarm Manager node; otherwise module fails with error message. It does return boolean flags in on both
|
||||
error and success which indicate whether the docker daemon can be communicated with, whether it is in Swarm mode, and
|
||||
whether it is a Swarm Manager node.
|
||||
author:
|
||||
- Piotr Wojciechowski (@WojciechowskiPiotr)
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.docker._docker
|
||||
- community.docker._docker.docker_py_2_documentation
|
||||
- community.docker._attributes
|
||||
- community.docker._attributes.actiongroup_docker
|
||||
- community.docker._attributes.info_module
|
||||
- community.docker._attributes.idempotent_not_modify_state
|
||||
|
||||
options:
|
||||
nodes:
|
||||
description:
|
||||
- Whether to list swarm nodes.
|
||||
type: bool
|
||||
default: false
|
||||
nodes_filters:
|
||||
description:
|
||||
- A dictionary of filter values used for selecting nodes to list.
|
||||
- 'For example, C(name: mynode).'
|
||||
- See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/node_ls/#filtering) for more information
|
||||
on possible filters.
|
||||
type: dict
|
||||
services:
|
||||
description:
|
||||
- Whether to list swarm services.
|
||||
type: bool
|
||||
default: false
|
||||
services_filters:
|
||||
description:
|
||||
- A dictionary of filter values used for selecting services to list.
|
||||
- 'For example, C(name: myservice).'
|
||||
- See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/service_ls/#filtering) for more
|
||||
information on possible filters.
|
||||
type: dict
|
||||
tasks:
|
||||
description:
|
||||
- Whether to list containers.
|
||||
type: bool
|
||||
default: false
|
||||
tasks_filters:
|
||||
description:
|
||||
- A dictionary of filter values used for selecting tasks to list.
|
||||
- 'For example, C(node: mynode-1).'
|
||||
- See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/service_ps/#filtering) for more
|
||||
information on possible filters.
|
||||
type: dict
|
||||
unlock_key:
|
||||
description:
|
||||
- Whether to retrieve the swarm unlock key.
|
||||
type: bool
|
||||
default: false
|
||||
verbose_output:
|
||||
description:
|
||||
- When set to V(true) and O(nodes), O(services), or O(tasks) is set to V(true), then the module output will contain
|
||||
verbose information about objects matching the full output of API method.
|
||||
- For details see the documentation of your version of Docker API at U(https://docs.docker.com/engine/api/).
|
||||
- The verbose output in this module contains only subset of information returned by this info module for each type of
|
||||
the objects.
|
||||
type: bool
|
||||
default: false
|
||||
|
||||
requirements:
|
||||
- "L(Docker SDK for Python,https://docker-py.readthedocs.io/en/stable/) >= 2.0.0"
|
||||
- "Docker API >= 1.25"
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
---
|
||||
- name: Get info on Docker Swarm
|
||||
community.docker.docker_swarm_info:
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- name: Inform about basic flags
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Was able to talk to docker daemon: {{ result.can_talk_to_docker }}
|
||||
Docker in Swarm mode: {{ result.docker_swarm_active }}
|
||||
This is a Manager node: {{ result.docker_swarm_manager }}
|
||||
|
||||
- name: Get info on Docker Swarm and list of registered nodes
|
||||
community.docker.docker_swarm_info:
|
||||
nodes: true
|
||||
register: result
|
||||
|
||||
- name: Get info on Docker Swarm and extended list of registered nodes
|
||||
community.docker.docker_swarm_info:
|
||||
nodes: true
|
||||
verbose_output: true
|
||||
register: result
|
||||
|
||||
- name: Get info on Docker Swarm and filtered list of registered nodes
|
||||
community.docker.docker_swarm_info:
|
||||
nodes: true
|
||||
nodes_filters:
|
||||
name: mynode
|
||||
register: result
|
||||
|
||||
- name: Show swarm facts
|
||||
ansible.builtin.debug:
|
||||
var: result.swarm_facts
|
||||
|
||||
- name: Get the swarm unlock key
|
||||
community.docker.docker_swarm_info:
|
||||
unlock_key: true
|
||||
register: result
|
||||
|
||||
- name: Print swarm unlock key
|
||||
ansible.builtin.debug:
|
||||
var: result.swarm_unlock_key
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
can_talk_to_docker:
|
||||
description:
|
||||
- Will be V(true) if the module can talk to the docker daemon.
|
||||
returned: both on success and on error
|
||||
type: bool
|
||||
docker_swarm_active:
|
||||
description:
|
||||
- Will be V(true) if the module can talk to the docker daemon, and the docker daemon is in Swarm mode.
|
||||
returned: both on success and on error
|
||||
type: bool
|
||||
docker_swarm_manager:
|
||||
description:
|
||||
- Will be V(true) if the module can talk to the docker daemon, the docker daemon is in Swarm mode, and the current node
|
||||
is a manager node.
|
||||
- Only if this one is V(true), the module will not fail.
|
||||
returned: both on success and on error
|
||||
type: bool
|
||||
swarm_facts:
|
||||
description:
|
||||
- Facts representing the basic state of the docker Swarm cluster.
|
||||
- Contains tokens to connect to the Swarm.
|
||||
returned: always
|
||||
type: dict
|
||||
swarm_unlock_key:
|
||||
description:
|
||||
- Contains the key needed to unlock the swarm.
|
||||
returned: When O(unlock_key=true).
|
||||
type: str
|
||||
nodes:
|
||||
description:
|
||||
- List of dict objects containing the basic information about each volume. Keys matches the C(docker node ls) output unless
|
||||
O(verbose_output=true). See description for O(verbose_output).
|
||||
returned: When O(nodes=true)
|
||||
type: list
|
||||
elements: dict
|
||||
services:
|
||||
description:
|
||||
- List of dict objects containing the basic information about each volume. Keys matches the C(docker service ls) output
|
||||
unless O(verbose_output=true). See description for O(verbose_output).
|
||||
returned: When O(services=true)
|
||||
type: list
|
||||
elements: dict
|
||||
tasks:
|
||||
description:
|
||||
- List of dict objects containing the basic information about each volume. Keys matches the C(docker service ps) output
|
||||
unless O(verbose_output=true). See description for O(verbose_output).
|
||||
returned: When O(tasks=true)
|
||||
type: list
|
||||
elements: dict
|
||||
"""
|
||||
|
||||
import traceback
|
||||
import typing as t
|
||||
|
||||
try:
|
||||
from docker.errors import APIError, DockerException
|
||||
except ImportError:
|
||||
# missing Docker SDK for Python handled in ansible.module_utils.docker_common
|
||||
pass
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils._common import (
|
||||
RequestException,
|
||||
)
|
||||
from ansible_collections.community.docker.plugins.module_utils._swarm import (
|
||||
AnsibleDockerSwarmClient,
|
||||
)
|
||||
from ansible_collections.community.docker.plugins.module_utils._util import (
|
||||
DockerBaseClass,
|
||||
clean_dict_booleans_for_docker_api,
|
||||
)
|
||||
|
||||
|
||||
class DockerSwarmManager(DockerBaseClass):
|
||||
def __init__(
|
||||
self, client: AnsibleDockerSwarmClient, results: dict[str, t.Any]
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.client = client
|
||||
self.results = results
|
||||
self.verbose_output = self.client.module.params["verbose_output"]
|
||||
|
||||
listed_objects: list[t.Literal["nodes", "tasks", "services"]] = [
|
||||
"tasks",
|
||||
"services",
|
||||
"nodes",
|
||||
]
|
||||
|
||||
self.client.fail_task_if_not_swarm_manager()
|
||||
|
||||
self.results["swarm_facts"] = self.get_docker_swarm_facts()
|
||||
|
||||
for docker_object in listed_objects:
|
||||
if self.client.module.params[docker_object]:
|
||||
returned_name = docker_object
|
||||
filter_name = docker_object + "_filters"
|
||||
filters = clean_dict_booleans_for_docker_api(
|
||||
client.module.params.get(filter_name)
|
||||
)
|
||||
self.results[returned_name] = self.get_docker_items_list(
|
||||
docker_object, filters
|
||||
)
|
||||
if self.client.module.params["unlock_key"]:
|
||||
self.results["swarm_unlock_key"] = self.get_docker_swarm_unlock_key()
|
||||
|
||||
def get_docker_swarm_facts(self) -> dict[str, t.Any]:
|
||||
try:
|
||||
return self.client.inspect_swarm()
|
||||
except APIError as exc:
|
||||
self.client.fail(f"Error inspecting docker swarm: {exc}")
|
||||
|
||||
def get_docker_items_list(
|
||||
self,
|
||||
docker_object: t.Literal["nodes", "tasks", "services"],
|
||||
filters: dict[str, str],
|
||||
) -> list[dict[str, t.Any]]:
|
||||
items_list: list[dict[str, t.Any]] = []
|
||||
|
||||
try:
|
||||
if docker_object == "nodes":
|
||||
items = self.client.nodes(filters=filters)
|
||||
elif docker_object == "tasks":
|
||||
items = self.client.tasks(filters=filters)
|
||||
elif docker_object == "services":
|
||||
items = self.client.services(filters=filters)
|
||||
else:
|
||||
raise ValueError(f"Invalid docker_object {docker_object}")
|
||||
except APIError as exc:
|
||||
self.client.fail(
|
||||
f"Error inspecting docker swarm for object '{docker_object}': {exc}"
|
||||
)
|
||||
|
||||
if self.verbose_output:
|
||||
return items
|
||||
|
||||
for item in items:
|
||||
item_record = {}
|
||||
|
||||
if docker_object == "nodes":
|
||||
item_record = self.get_essential_facts_nodes(item)
|
||||
elif docker_object == "tasks":
|
||||
item_record = self.get_essential_facts_tasks(item)
|
||||
elif docker_object == "services":
|
||||
item_record = self.get_essential_facts_services(item)
|
||||
if item_record.get("Mode") == "Global":
|
||||
item_record["Replicas"] = len(items)
|
||||
items_list.append(item_record)
|
||||
|
||||
return items_list
|
||||
|
||||
@staticmethod
|
||||
def get_essential_facts_nodes(item: dict[str, t.Any]) -> dict[str, t.Any]:
|
||||
object_essentials = {}
|
||||
|
||||
object_essentials["ID"] = item.get("ID")
|
||||
object_essentials["Hostname"] = item["Description"]["Hostname"]
|
||||
object_essentials["Status"] = item["Status"]["State"]
|
||||
object_essentials["Availability"] = item["Spec"]["Availability"]
|
||||
if "ManagerStatus" in item:
|
||||
object_essentials["ManagerStatus"] = item["ManagerStatus"]["Reachability"]
|
||||
if (
|
||||
"Leader" in item["ManagerStatus"]
|
||||
and item["ManagerStatus"]["Leader"] is True
|
||||
):
|
||||
object_essentials["ManagerStatus"] = "Leader"
|
||||
else:
|
||||
object_essentials["ManagerStatus"] = None
|
||||
object_essentials["EngineVersion"] = item["Description"]["Engine"][
|
||||
"EngineVersion"
|
||||
]
|
||||
|
||||
return object_essentials
|
||||
|
||||
def get_essential_facts_tasks(self, item: dict[str, t.Any]) -> dict[str, t.Any]:
|
||||
object_essentials = {}
|
||||
|
||||
object_essentials["ID"] = item["ID"]
|
||||
# Returning container ID to not trigger another connection to host
|
||||
# Container ID is sufficient to get extended info in other tasks
|
||||
object_essentials["ContainerID"] = item["Status"]["ContainerStatus"][
|
||||
"ContainerID"
|
||||
]
|
||||
object_essentials["Image"] = item["Spec"]["ContainerSpec"]["Image"]
|
||||
object_essentials["Node"] = self.client.get_node_name_by_id(item["NodeID"])
|
||||
object_essentials["DesiredState"] = item["DesiredState"]
|
||||
object_essentials["CurrentState"] = item["Status"]["State"]
|
||||
if "Err" in item["Status"]:
|
||||
object_essentials["Error"] = item["Status"]["Err"]
|
||||
else:
|
||||
object_essentials["Error"] = None
|
||||
|
||||
return object_essentials
|
||||
|
||||
@staticmethod
|
||||
def get_essential_facts_services(item: dict[str, t.Any]) -> dict[str, t.Any]:
|
||||
object_essentials = {}
|
||||
|
||||
object_essentials["ID"] = item["ID"]
|
||||
object_essentials["Name"] = item["Spec"]["Name"]
|
||||
if "Replicated" in item["Spec"]["Mode"]:
|
||||
object_essentials["Mode"] = "Replicated"
|
||||
object_essentials["Replicas"] = item["Spec"]["Mode"]["Replicated"][
|
||||
"Replicas"
|
||||
]
|
||||
elif "Global" in item["Spec"]["Mode"]:
|
||||
object_essentials["Mode"] = "Global"
|
||||
# Number of replicas have to be updated in calling method or may be left as None
|
||||
object_essentials["Replicas"] = None
|
||||
object_essentials["Image"] = item["Spec"]["TaskTemplate"]["ContainerSpec"][
|
||||
"Image"
|
||||
]
|
||||
if item["Spec"].get("EndpointSpec") and "Ports" in item["Spec"]["EndpointSpec"]:
|
||||
object_essentials["Ports"] = item["Spec"]["EndpointSpec"]["Ports"]
|
||||
else:
|
||||
object_essentials["Ports"] = []
|
||||
|
||||
return object_essentials
|
||||
|
||||
def get_docker_swarm_unlock_key(self) -> str | None:
|
||||
unlock_key = self.client.get_unlock_key() or {}
|
||||
return unlock_key.get("UnlockKey") or None
|
||||
|
||||
|
||||
def main() -> None:
|
||||
argument_spec = {
|
||||
"nodes": {"type": "bool", "default": False},
|
||||
"nodes_filters": {"type": "dict"},
|
||||
"tasks": {"type": "bool", "default": False},
|
||||
"tasks_filters": {"type": "dict"},
|
||||
"services": {"type": "bool", "default": False},
|
||||
"services_filters": {"type": "dict"},
|
||||
"unlock_key": {"type": "bool", "default": False},
|
||||
"verbose_output": {"type": "bool", "default": False},
|
||||
}
|
||||
option_minimal_versions = {
|
||||
"unlock_key": {"docker_py_version": "2.7.0"},
|
||||
}
|
||||
|
||||
client = AnsibleDockerSwarmClient(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
min_docker_version="2.0.0",
|
||||
option_minimal_versions=option_minimal_versions,
|
||||
fail_results={
|
||||
"can_talk_to_docker": False,
|
||||
"docker_swarm_active": False,
|
||||
"docker_swarm_manager": False,
|
||||
},
|
||||
)
|
||||
client.fail_results["can_talk_to_docker"] = True
|
||||
client.fail_results["docker_swarm_active"] = client.check_if_swarm_node()
|
||||
client.fail_results["docker_swarm_manager"] = client.check_if_swarm_manager()
|
||||
|
||||
try:
|
||||
results = {
|
||||
"changed": False,
|
||||
}
|
||||
|
||||
DockerSwarmManager(client, results)
|
||||
results.update(client.fail_results)
|
||||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail(
|
||||
f"An unexpected Docker error occurred: {e}",
|
||||
exception=traceback.format_exc(),
|
||||
)
|
||||
except RequestException as e:
|
||||
client.fail(
|
||||
f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}",
|
||||
exception=traceback.format_exc(),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue