From e633b93f859daafea3cf68bb79ad140ed8a42495 Mon Sep 17 00:00:00 2001 From: Piotr Wojciechowski <23406016+WojciechowskiPiotr@users.noreply.github.com> Date: Sat, 2 Feb 2019 13:52:16 +0100 Subject: [PATCH] docker_host_facts: Get system-wide information about docker host (#51373) * * docker_host_facts: Getting docker host info * * docker_host_facts: Getting lists of containers, volumes, images and networks as in respective CLI commands * * docker_host_facts: Adding filters, documentation update * docker_host_facts: Code cleanup * docker_host_facts: Adding verbose output option. Documentation update. * docker_host_facts: Improving author information for ansibot * docker_host_facts: documentation and small code updates, for `docker_disk_usage` returned type is always dict now --- .../modules/cloud/docker/docker_host_facts.py | 326 ++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 lib/ansible/modules/cloud/docker/docker_host_facts.py diff --git a/lib/ansible/modules/cloud/docker/docker_host_facts.py b/lib/ansible/modules/cloud/docker/docker_host_facts.py new file mode 100644 index 00000000000..25b49a0bed1 --- /dev/null +++ b/lib/ansible/modules/cloud/docker/docker_host_facts.py @@ -0,0 +1,326 @@ +#!/usr/bin/python +# +# (c) 2019 Piotr Wojciechowski +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: docker_host_facts + +short_description: Retrieves facts about docker host and lists of objects of the services. + +description: + - Retrieves facts about a docker host. + - Essentially returns the output of C(docker system info). + - Returns lists of objects names for the services - images, networks, volumes, containers. + - Returns disk usage information. + - The output differs depending on API version available on docker host. + - Must be executed on a host running a Docker, otherwise the module will fail. + +version_added: "2.8" + +options: + containers: + description: + - Whether to list containers. + type: bool + default: no + containers_filters: + description: + - A dictionary of filter values used for selecting containers to delete. + - "For example, C(until: 24h)." + - See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/container_prune/#filtering) + for more information on possible filters. + type: dict + images: + description: + - Whether to list images. + type: bool + default: no + images_filters: + description: + - A dictionary of filter values used for selecting images to delete. + - "For example, C(dangling: true)." + - See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/image_prune/#filtering) + for more information on possible filters. + type: dict + networks: + description: + - Whether to list networks. + type: bool + default: no + networks_filters: + description: + - A dictionary of filter values used for selecting networks to delete. + - See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/network_prune/#filtering) + for more information on possible filters. + type: dict + volumes: + description: + - Whether to list volumes. + type: bool + default: no + volumes_filters: + description: + - A dictionary of filter values used for selecting volumes to delete. + - See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/volume_prune/#filtering) + for more information on possible filters. + type: dict + disk_usage: + description: + - Summary information on used disk space by all Docker layers. + - The output is a sum of images, volumes, containers and build cache. + type: bool + default: no + verbose_output: + description: + - When set to C(yes) and I(networks), I(volumes), I(images), I(containers) or I(disk_usage) is set to C(yes) + then 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 L(https://docs.docker.com/engine/api/). + - The verbose output in this module contains only subset of information returned by I(_facts) module + for each type of the objects. + type: bool + default: no +extends_documentation_fragment: + - docker + +author: + - Piotr Wojciechowski (@WojciechowskiPiotr) + +requirements: + - "python >= 2.6" + - "docker-py >= 1.10.0" + - "Please note that the L(docker-py,https://pypi.org/project/docker-py/) Python + module has been superseded by L(docker,https://pypi.org/project/docker/) + (see L(here,https://github.com/docker/docker-py/issues/1310) for details). + For Python 2.6, C(docker-py) must be used. Otherwise, it is recommended to + install the C(docker) Python module. Note that both modules should I(not) + be installed at the same time. Also note that when both modules are installed + and one of them is uninstalled, the other might no longer function and a + reinstall of it is required." + - "Docker API >= 1.21" +''' + +EXAMPLES = ''' +- name: Get info on docker host + docker_host_facts: + register: result + +- name: Get info on docker host and list images + docker_host_facts: + images: yes + register: result + +- name: Get info on docker host and list images matching the filter + docker_host_facts: + images: yes + images_filters: + label: "mylabel" + register: result + +- name: Get info on docker host and verbose list images + docker_host_facts: + images: yes + verbose_output: yes + register: result + +- name: Get info on docker host and used disk space + docker_host_facts: + disk_usage: yes + register: result + +- debug: + var: result.docker_host_facts + +''' + +RETURN = ''' +docker_host_facts: + description: + - Facts representing the basic state of the docker host. Matches the C(docker system info) output. + returned: always + type: dict +docker_volumes_list: + description: + - List of dict objects containing the basic information about each volume. + Keys matches the C(docker volume ls) output unless I(verbose_output=yes). + See description for I(verbose_output). + returned: When I(volumes) is C(yes) + type: list +docker_networks_list: + description: + - List of dict objects containing the basic information about each network. + Keys matches the C(docker network ls) output unless I(verbose_output=yes). + See description for I(verbose_output). + returned: When I(networks) is C(yes) + type: list +docker_containers_list: + description: + - List of dict objects containing the basic information about each container. + Keys matches the C(docker container ls) output unless I(verbose_output=yes). + See description for I(verbose_output). + returned: When I(containers) is C(yes) + type: list +docker_images_list: + description: + - List of dict objects containing the basic information about each image. + Keys matches the C(docker image ls) output unless I(verbose_output=yes). + See description for I(verbose_output). + returned: When I(images) is C(yes) + type: list +docker_disk_usage: + description: + - Information on summary disk usage by images, containers and volumes on docker host + unless I(verbose_output=yes). See description for I(verbose_output). + returned: When I(disk_usage) is C(yes) + type: dict + +''' + +from ansible.module_utils.docker_common import AnsibleDockerClient, DockerBaseClass +from ansible.module_utils._text import to_native + +try: + from docker.errors import APIError, NotFound +except ImportError: + # missing docker-py handled in ansible.module_utils.docker_common + pass + +try: + from ansible.module_utils.docker_common import docker_version, clean_dict_booleans_for_docker_api +except Exception as dummy: + # missing docker-py handled in ansible.module_utils.docker + pass + + +class DockerHostManager(DockerBaseClass): + + def __init__(self, client, results): + + super(DockerHostManager, self).__init__() + + self.client = client + self.results = results + self.verbose_output = self.client.module.params['verbose_output'] + + listed_objects = ['volumes', 'networks', 'containers', 'images'] + + self.results['docker_host_facts'] = self.get_docker_host_facts() + + if self.client.module.params['disk_usage']: + self.results['docker_disk_usage'] = self.get_docker_disk_usage_facts() + + for docker_object in listed_objects: + if self.client.module.params[docker_object]: + returned_name = "docker_" + docker_object + "_list" + 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) + + def get_docker_host_facts(self): + try: + return self.client.info() + except APIError as exc: + self.client.fail_json(msg="Error inspecting docker host: %s" % to_native(exc)) + + def get_docker_disk_usage_facts(self): + try: + if self.verbose_output: + return self.client.df() + else: + return dict(LayerSize=self.client.df()['LayersSize']) + except APIError as exc: + self.client.fail_json(msg="Error inspecting docker host: %s" % to_native(exc)) + + def get_docker_items_list(self, docker_object=None, filters=None, verbose=False): + items = None + items_list = [] + + header_containers = ['Id', 'Image', 'Command', 'Created', 'Status', 'Ports', 'Names'] + header_volumes = ['Driver', 'Name'] + header_images = ['Id', 'RepoTags', 'Created', 'Size'] + header_networks = ['Id', 'Driver', 'Name', 'Scope'] + + try: + if docker_object == 'containers': + items = self.client.containers(filters=filters) + elif docker_object == 'networks': + items = self.client.networks(filters=filters) + elif docker_object == 'images': + items = self.client.images(filters=filters) + elif docker_object == 'volumes': + items = self.client.volumes(filters=filters) + except APIError as exc: + self.client.fail_json(msg="Error inspecting docker host for object '%s': %s" % + (docker_object, to_native(exc))) + + if self.verbose_output: + if docker_object != 'volumes': + return items + else: + return items['Volumes'] + + if docker_object == 'volumes': + items = items['Volumes'] + + for item in items: + item_record = dict() + + if docker_object == 'containers': + for key in header_containers: + item_record[key] = item.get(key) + elif docker_object == 'networks': + for key in header_networks: + item_record[key] = item.get(key) + elif docker_object == 'images': + for key in header_images: + item_record[key] = item.get(key) + elif docker_object == 'volumes': + for key in header_volumes: + item_record[key] = item.get(key) + items_list.append(item_record) + + return items_list + + +def main(): + argument_spec = dict( + containers=dict(type='bool', default=False), + containers_filters=dict(type='dict'), + images=dict(type='bool', default=False), + images_filters=dict(type='dict'), + networks=dict(type='bool', default=False), + networks_filters=dict(type='dict'), + volumes=dict(type='bool', default=False), + volumes_filters=dict(type='dict'), + disk_usage=dict(type='bool', default=False), + verbose_output=dict(type='bool', default=False), + ) + + client = AnsibleDockerClient( + argument_spec=argument_spec, + supports_check_mode=True, + min_docker_version='1.10.0', + min_docker_api_version='1.21', + ) + + results = dict( + changed=False, + docker_host_facts=[] + ) + + DockerHostManager(client, results) + client.module.exit_json(**results) + + +if __name__ == '__main__': + main()