summaryrefslogtreecommitdiff
path: root/library/cloud/docker
diff options
context:
space:
mode:
Diffstat (limited to 'library/cloud/docker')
-rw-r--r--library/cloud/docker854
1 files changed, 0 insertions, 854 deletions
diff --git a/library/cloud/docker b/library/cloud/docker
deleted file mode 100644
index a0a52ffc75..0000000000
--- a/library/cloud/docker
+++ /dev/null
@@ -1,854 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2013, Cove Schneider
-# (c) 2014, Joshua Conner <joshua.conner@gmail.com>
-# (c) 2014, Pavel Antonov <antonov@adwz.ru>
-#
-# This file is part of Ansible,
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-
-######################################################################
-
-DOCUMENTATION = '''
----
-module: docker
-version_added: "1.4"
-short_description: manage docker containers
-description:
- - Manage the life cycle of docker containers.
-options:
- count:
- description:
- - Set number of containers to run
- required: False
- default: 1
- aliases: []
- image:
- description:
- - Set container image to use
- required: true
- default: null
- aliases: []
- command:
- description:
- - Set command to run in a container on startup
- required: false
- default: null
- aliases: []
- name:
- description:
- - Set name for container (used to find single container or to provide links)
- required: false
- default: null
- aliases: []
- version_added: "1.5"
- ports:
- description:
- - Set private to public port mapping specification using docker CLI-style syntax [([<host_interface>:[host_port]])|(<host_port>):]<container_port>[/udp]
- required: false
- default: null
- aliases: []
- version_added: "1.5"
- expose:
- description:
- - Set container ports to expose for port mappings or links. (If the port is already exposed using EXPOSE in a Dockerfile, you don't need to expose it again.)
- required: false
- default: null
- aliases: []
- version_added: "1.5"
- publish_all_ports:
- description:
- - Publish all exposed ports to the host interfaces
- required: false
- default: false
- aliases: []
- version_added: "1.5"
- volumes:
- description:
- - Set volume(s) to mount on the container
- required: false
- default: null
- aliases: []
- volumes_from:
- description:
- - Set shared volume(s) from another container
- required: false
- default: null
- aliases: []
- links:
- description:
- - Link container(s) to other container(s) (e.g. links=redis,postgresql:db)
- required: false
- default: null
- aliases: []
- version_added: "1.5"
- memory_limit:
- description:
- - Set RAM allocated to container
- required: false
- default: null
- aliases: []
- default: 256MB
- docker_url:
- description:
- - URL of docker host to issue commands to
- required: false
- default: unix://var/run/docker.sock
- aliases: []
- docker_api_version:
- description:
- - Remote API version to use. This defaults to the current default as specified by docker-py.
- required: false
- default: docker-py default remote API version
- aliases: []
- version_added: "1.8"
- username:
- description:
- - Set remote API username
- required: false
- default: null
- aliases: []
- password:
- description:
- - Set remote API password
- required: false
- default: null
- aliases: []
- hostname:
- description:
- - Set container hostname
- required: false
- default: null
- aliases: []
- env:
- description:
- - Set environment variables (e.g. env="PASSWORD=sEcRe7,WORKERS=4")
- required: false
- default: null
- aliases: []
- dns:
- description:
- - Set custom DNS servers for the container
- required: false
- default: null
- aliases: []
- detach:
- description:
- - Enable detached mode on start up, leaves container running in background
- required: false
- default: true
- aliases: []
- state:
- description:
- - Set the state of the container
- required: false
- default: present
- choices: [ "present", "running", "stopped", "absent", "killed", "restarted" ]
- aliases: []
- privileged:
- description:
- - Set whether the container should run in privileged mode
- required: false
- default: false
- aliases: []
- lxc_conf:
- description:
- - LXC config parameters, e.g. lxc.aa_profile:unconfined
- required: false
- default:
- aliases: []
- name:
- description:
- - Set the name of the container (cannot use with count)
- required: false
- default: null
- aliases: []
- version_added: "1.5"
- stdin_open:
- description:
- - Keep stdin open
- required: false
- default: false
- aliases: []
- version_added: "1.6"
- tty:
- description:
- - Allocate a pseudo-tty
- required: false
- default: false
- aliases: []
- version_added: "1.6"
- net:
- description:
- - Set Network mode for the container (bridge, none, container:<name|id>, host). Requires docker >= 0.11.
- required: false
- default: false
- aliases: []
- version_added: "1.8"
- registry:
- description:
- - The remote registry URL to use for pulling images.
- required: false
- default: ''
- aliases: []
- version_added: "1.8"
-
-author: Cove Schneider, Joshua Conner, Pavel Antonov
-requirements: [ "docker-py >= 0.3.0", "docker >= 0.10.0" ]
-'''
-
-EXAMPLES = '''
-Start one docker container running tomcat in each host of the web group and bind tomcat's listening port to 8080
-on the host:
-
-- hosts: web
- sudo: yes
- tasks:
- - name: run tomcat servers
- docker: image=centos command="service tomcat6 start" ports=8080
-
-The tomcat server's port is NAT'ed to a dynamic port on the host, but you can determine which port the server was
-mapped to using docker_containers:
-
-- hosts: web
- sudo: yes
- tasks:
- - name: run tomcat servers
- docker: image=centos command="service tomcat6 start" ports=8080 count=5
- - name: Display IP address and port mappings for containers
- debug: msg={{inventory_hostname}}:{{item['HostConfig']['PortBindings']['8080/tcp'][0]['HostPort']}}
- with_items: docker_containers
-
-Just as in the previous example, but iterates over the list of docker containers with a sequence:
-
-- hosts: web
- sudo: yes
- vars:
- start_containers_count: 5
- tasks:
- - name: run tomcat servers
- docker: image=centos command="service tomcat6 start" ports=8080 count={{start_containers_count}}
- - name: Display IP address and port mappings for containers
- debug: msg="{{inventory_hostname}}:{{docker_containers[{{item}}]['HostConfig']['PortBindings']['8080/tcp'][0]['HostPort']}}"
- with_sequence: start=0 end={{start_containers_count - 1}}
-
-Stop, remove all of the running tomcat containers and list the exit code from the stopped containers:
-
-- hosts: web
- sudo: yes
- tasks:
- - name: stop tomcat servers
- docker: image=centos command="service tomcat6 start" state=absent
- - name: Display return codes from stopped containers
- debug: msg="Returned {{inventory_hostname}}:{{item}}"
- with_items: docker_containers
-
-Create a named container:
-
-- hosts: web
- sudo: yes
- tasks:
- - name: run tomcat server
- docker: image=centos name=tomcat command="service tomcat6 start" ports=8080
-
-Create multiple named containers:
-
-- hosts: web
- sudo: yes
- tasks:
- - name: run tomcat servers
- docker: image=centos name={{item}} command="service tomcat6 start" ports=8080
- with_items:
- - crookshank
- - snowbell
- - heathcliff
- - felix
- - sylvester
-
-Create containers named in a sequence:
-
-- hosts: web
- sudo: yes
- tasks:
- - name: run tomcat servers
- docker: image=centos name={{item}} command="service tomcat6 start" ports=8080
- with_sequence: start=1 end=5 format=tomcat_%d.example.com
-
-Create two linked containers:
-
-- hosts: web
- sudo: yes
- tasks:
- - name: ensure redis container is running
- docker: image=crosbymichael/redis name=redis
-
- - name: ensure redis_ambassador container is running
- docker: image=svendowideit/ambassador ports=6379:6379 links=redis:redis name=redis_ambassador_ansible
-
-Create containers with options specified as key-value pairs and lists:
-
-- hosts: web
- sudo: yes
- tasks:
- - docker:
- image: namespace/image_name
- links:
- - postgresql:db
- - redis:redis
-
-
-Create containers with options specified as strings and lists as comma-separated strings:
-
-- hosts: web
- sudo: yes
- tasks:
- docker: image=namespace/image_name links=postgresql:db,redis:redis
-
-Create a container with no networking:
-
-- hosts: web
- sudo: yes
- tasks:
- docker: image=namespace/image_name net=none
-
-'''
-
-HAS_DOCKER_PY = True
-
-import sys
-from urlparse import urlparse
-try:
- import docker.client
- import docker.utils
- from requests.exceptions import *
-except ImportError, e:
- HAS_DOCKER_PY = False
-
-try:
- from docker.errors import APIError as DockerAPIError
-except ImportError:
- from docker.client import APIError as DockerAPIError
-
-
-def _human_to_bytes(number):
- suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
-
- if isinstance(number, int):
- return number
- if number[-1] == suffixes[0] and number[-2].isdigit():
- return number[:-1]
-
- i = 1
- for each in suffixes[1:]:
- if number[-len(each):] == suffixes[i]:
- return int(number[:-len(each)]) * (1024 ** i)
- i = i + 1
-
- print "failed=True msg='Could not convert %s to integer'" % (number)
- sys.exit(1)
-
-def _ansible_facts(container_list):
- return {"docker_containers": container_list}
-
-def _docker_id_quirk(inspect):
- # XXX: some quirk in docker
- if 'ID' in inspect:
- inspect['Id'] = inspect['ID']
- del inspect['ID']
- return inspect
-
-class DockerManager:
-
- counters = {'created':0, 'started':0, 'stopped':0, 'killed':0, 'removed':0, 'restarted':0, 'pull':0}
-
- def __init__(self, module):
- self.module = module
-
- self.binds = None
- self.volumes = None
- if self.module.params.get('volumes'):
- self.binds = {}
- self.volumes = {}
- vols = self.module.params.get('volumes')
- for vol in vols:
- parts = vol.split(":")
- # host mount (e.g. /mnt:/tmp, bind mounts host's /tmp to /mnt in the container)
- if len(parts) == 2:
- self.volumes[parts[1]] = {}
- self.binds[parts[0]] = parts[1]
- # with bind mode
- elif len(parts) == 3:
- if parts[2] not in ['ro', 'rw']:
- self.module.fail_json(msg='bind mode needs to either be "ro" or "rw"')
- ro = parts[2] == 'ro'
- self.volumes[parts[1]] = {}
- self.binds[parts[0]] = {'bind': parts[1], 'ro': ro}
- # docker mount (e.g. /www, mounts a docker volume /www on the container at the same location)
- else:
- self.volumes[parts[0]] = {}
-
- self.lxc_conf = None
- if self.module.params.get('lxc_conf'):
- self.lxc_conf = []
- options = self.module.params.get('lxc_conf')
- for option in options:
- parts = option.split(':')
- self.lxc_conf.append({"Key": parts[0], "Value": parts[1]})
-
- self.exposed_ports = None
- if self.module.params.get('expose'):
- self.exposed_ports = self.get_exposed_ports(self.module.params.get('expose'))
-
- self.port_bindings = None
- if self.module.params.get('ports'):
- self.port_bindings = self.get_port_bindings(self.module.params.get('ports'))
-
- self.links = None
- if self.module.params.get('links'):
- self.links = self.get_links(self.module.params.get('links'))
-
- self.env = self.module.params.get('env', None)
-
- # connect to docker server
- docker_url = urlparse(module.params.get('docker_url'))
- docker_api_version = module.params.get('docker_api_version')
- self.client = docker.Client(base_url=docker_url.geturl(), version=docker_api_version)
-
-
- def get_links(self, links):
- """
- Parse the links passed, if a link is specified without an alias then just create the alias of the same name as the link
- """
- processed_links = {}
-
- for link in links:
- parsed_link = link.split(':', 1)
- if(len(parsed_link) == 2):
- processed_links[parsed_link[0]] = parsed_link[1]
- else:
- processed_links[parsed_link[0]] = parsed_link[0]
-
- return processed_links
-
-
- def get_exposed_ports(self, expose_list):
- """
- Parse the ports and protocols (TCP/UDP) to expose in the docker-py `create_container` call from the docker CLI-style syntax.
- """
- if expose_list:
- exposed = []
- for port in expose_list:
- port = str(port).strip()
- if port.endswith('/tcp') or port.endswith('/udp'):
- port_with_proto = tuple(port.split('/'))
- else:
- # assume tcp protocol if not specified
- port_with_proto = (port, 'tcp')
- exposed.append(port_with_proto)
- return exposed
- else:
- return None
-
-
- def get_port_bindings(self, ports):
- """
- Parse the `ports` string into a port bindings dict for the `start_container` call.
- """
- binds = {}
- for port in ports:
- # ports could potentially be an array like [80, 443], so we make sure they're strings
- # before splitting
- parts = str(port).split(':')
- container_port = parts[-1]
- if '/' not in container_port:
- container_port = int(parts[-1])
-
- p_len = len(parts)
- if p_len == 1:
- # Bind `container_port` of the container to a dynamically
- # allocated TCP port on all available interfaces of the host
- # machine.
- bind = ('0.0.0.0',)
- elif p_len == 2:
- # Bind `container_port` of the container to port `parts[0]` on
- # all available interfaces of the host machine.
- bind = ('0.0.0.0', int(parts[0]))
- elif p_len == 3:
- # Bind `container_port` of the container to port `parts[1]` on
- # IP `parts[0]` of the host machine. If `parts[1]` empty bind
- # to a dynamically allocacted port of IP `parts[0]`.
- bind = (parts[0], int(parts[1])) if parts[1] else (parts[0],)
-
- if container_port in binds:
- old_bind = binds[container_port]
- if isinstance(old_bind, list):
- # append to list if it already exists
- old_bind.append(bind)
- else:
- # otherwise create list that contains the old and new binds
- binds[container_port] = [binds[container_port], bind]
- else:
- binds[container_port] = bind
-
- return binds
-
-
- def get_split_image_tag(self, image):
- # If image contains a host or org name, omit that from our check
- if '/' in image:
- registry, resource = image.rsplit('/', 1)
- else:
- registry, resource = None, image
-
- # now we can determine if image has a tag
- if ':' in resource:
- resource, tag = resource.split(':', 1)
- if registry:
- resource = '/'.join((registry, resource))
- else:
- tag = "latest"
- resource = image
-
- return resource, tag
-
- def get_summary_counters_msg(self):
- msg = ""
- for k, v in self.counters.iteritems():
- msg = msg + "%s %d " % (k, v)
-
- return msg
-
- def increment_counter(self, name):
- self.counters[name] = self.counters[name] + 1
-
- def has_changed(self):
- for k, v in self.counters.iteritems():
- if v > 0:
- return True
-
- return False
-
- def get_inspect_containers(self, containers):
- inspect = []
- for i in containers:
- details = self.client.inspect_container(i['Id'])
- details = _docker_id_quirk(details)
- inspect.append(details)
-
- return inspect
-
- def get_deployed_containers(self):
- """determine which images/commands are running already"""
- image = self.module.params.get('image')
- command = self.module.params.get('command')
- if command:
- command = command.strip()
- name = self.module.params.get('name')
- if name and not name.startswith('/'):
- name = '/' + name
- deployed = []
-
- # if we weren't given a tag with the image, we need to only compare on the image name, as that
- # docker will give us back the full image name including a tag in the container list if one exists.
- image, tag = self.get_split_image_tag(image)
-
- for i in self.client.containers(all=True):
- running_image, running_tag = self.get_split_image_tag(i['Image'])
- running_command = i['Command'].strip()
-
- name_matches = False
- if i["Names"]:
- name_matches = (name and name in i['Names'])
- image_matches = (running_image == image)
- tag_matches = (not tag or running_tag == tag)
- # if a container has an entrypoint, `command` will actually equal
- # '{} {}'.format(entrypoint, command)
- command_matches = (not command or running_command.endswith(command))
-
- if name_matches or (name is None and image_matches and tag_matches and command_matches):
- details = self.client.inspect_container(i['Id'])
- details = _docker_id_quirk(details)
- deployed.append(details)
-
- return deployed
-
- def get_running_containers(self):
- running = []
- for i in self.get_deployed_containers():
- if i['State']['Running'] == True and i['State'].get('Ghost', False) == False:
- running.append(i)
-
- return running
-
- def create_containers(self, count=1):
- params = {'image': self.module.params.get('image'),
- 'command': self.module.params.get('command'),
- 'ports': self.exposed_ports,
- 'volumes': self.volumes,
- 'mem_limit': _human_to_bytes(self.module.params.get('memory_limit')),
- 'environment': self.env,
- 'hostname': self.module.params.get('hostname'),
- 'detach': self.module.params.get('detach'),
- 'name': self.module.params.get('name'),
- 'stdin_open': self.module.params.get('stdin_open'),
- 'tty': self.module.params.get('tty'),
- }
-
- if docker.utils.compare_version('1.10', self.client.version()['ApiVersion']) < 0:
- params['dns'] = self.module.params.get('dns')
- params['volumes_from'] = self.module.params.get('volumes_from')
-
- def do_create(count, params):
- results = []
- for _ in range(count):
- result = self.client.create_container(**params)
- self.increment_counter('created')
- results.append(result)
-
- return results
-
- try:
- containers = do_create(count, params)
- except:
- resource = self.module.params.get('image')
- image, tag = self.get_split_image_tag(resource)
- if self.module.params.get('username'):
- try:
- self.client.login(
- self.module.params.get('username'),
- password=self.module.params.get('password'),
- email=self.module.params.get('email'),
- registry=self.module.params.get('registry')
- )
- except:
- self.module.fail_json(msg="failed to login to the remote registry, check your username/password.")
- try:
- self.client.pull(image, tag=tag)
- except:
- self.module.fail_json(msg="failed to pull the specified image: %s" % resource)
- self.increment_counter('pull')
- containers = do_create(count, params)
-
- return containers
-
- def start_containers(self, containers):
- params = {
- 'lxc_conf': self.lxc_conf,
- 'binds': self.binds,
- 'port_bindings': self.port_bindings,
- 'publish_all_ports': self.module.params.get('publish_all_ports'),
- 'privileged': self.module.params.get('privileged'),
- 'links': self.links,
- 'network_mode': self.module.params.get('net'),
- }
- if docker.utils.compare_version('1.10', self.client.version()['ApiVersion']) >= 0 and hasattr(docker, '__version__') and docker.__version__ > '0.3.0':
- params['dns'] = self.module.params.get('dns')
- params['volumes_from'] = self.module.params.get('volumes_from')
-
- for i in containers:
- self.client.start(i['Id'], **params)
- self.increment_counter('started')
-
- def stop_containers(self, containers):
- for i in containers:
- self.client.stop(i['Id'])
- self.increment_counter('stopped')
-
- return [self.client.wait(i['Id']) for i in containers]
-
- def remove_containers(self, containers):
- for i in containers:
- self.client.remove_container(i['Id'])
- self.increment_counter('removed')
-
- def kill_containers(self, containers):
- for i in containers:
- self.client.kill(i['Id'])
- self.increment_counter('killed')
-
- def restart_containers(self, containers):
- for i in containers:
- self.client.restart(i['Id'])
- self.increment_counter('restarted')
-
-
-def check_dependencies(module):
- """
- Ensure `docker-py` >= 0.3.0 is installed, and call module.fail_json with a
- helpful error message if it isn't.
- """
- if not HAS_DOCKER_PY:
- module.fail_json(msg="`docker-py` doesn't seem to be installed, but is required for the Ansible Docker module.")
- else:
- HAS_NEW_ENOUGH_DOCKER_PY = False
- if hasattr(docker, '__version__'):
- # a '__version__' attribute was added to the module but not until
- # after 0.3.0 was added pushed to pip. If it's there, use it.
- if docker.__version__ >= '0.3.0':
- HAS_NEW_ENOUGH_DOCKER_PY = True
- else:
- # HACK: if '__version__' isn't there, we check for the existence of
- # `_get_raw_response_socket` in the docker.Client class, which was
- # added in 0.3.0
- if hasattr(docker.Client, '_get_raw_response_socket'):
- HAS_NEW_ENOUGH_DOCKER_PY = True
-
- if not HAS_NEW_ENOUGH_DOCKER_PY:
- module.fail_json(msg="The Ansible Docker module requires `docker-py` >= 0.3.0.")
-
-
-def main():
- module = AnsibleModule(
- argument_spec = dict(
- count = dict(default=1),
- image = dict(required=True),
- command = dict(required=False, default=None),
- expose = dict(required=False, default=None, type='list'),
- ports = dict(required=False, default=None, type='list'),
- publish_all_ports = dict(default=False, type='bool'),
- volumes = dict(default=None, type='list'),
- volumes_from = dict(default=None),
- links = dict(default=None, type='list'),
- memory_limit = dict(default=0),
- memory_swap = dict(default=0),
- docker_url = dict(default='unix://var/run/docker.sock'),
- docker_api_version = dict(default=docker.client.DEFAULT_DOCKER_API_VERSION),
- username = dict(default=None),
- password = dict(),
- email = dict(),
- registry = dict(),
- hostname = dict(default=None),
- env = dict(type='dict'),
- dns = dict(),
- detach = dict(default=True, type='bool'),
- state = dict(default='running', choices=['absent', 'present', 'running', 'stopped', 'killed', 'restarted']),
- debug = dict(default=False, type='bool'),
- privileged = dict(default=False, type='bool'),
- stdin_open = dict(default=False, type='bool'),
- tty = dict(default=False, type='bool'),
- lxc_conf = dict(default=None, type='list'),
- name = dict(default=None),
- net = dict(default=None)
- )
- )
-
- check_dependencies(module)
-
- try:
- manager = DockerManager(module)
- state = module.params.get('state')
- count = int(module.params.get('count'))
- name = module.params.get('name')
- image = module.params.get('image')
-
- if count < 0:
- module.fail_json(msg="Count must be greater than zero")
- if count > 1 and name:
- module.fail_json(msg="Count and name must not be used together")
-
- running_containers = manager.get_running_containers()
- running_count = len(running_containers)
- delta = count - running_count
- deployed_containers = manager.get_deployed_containers()
- facts = None
- failed = False
- changed = False
-
- # start/stop containers
- if state in [ "running", "present" ]:
-
- # make sure a container with `name` exists, if not create and start it
- if name:
- # first determine if a container with this name exists
- existing_container = None
- for deployed_container in deployed_containers:
- if deployed_container.get('Name') == '/%s' % name:
- existing_container = deployed_container
- break
-
- # the named container is running, but with a
- # different image or tag, so we stop it first
- if existing_container and existing_container.get('Config', dict()).get('Image') != image:
- manager.stop_containers([existing_container])
- manager.remove_containers([existing_container])
- running_containers = manager.get_running_containers()
- deployed_containers = manager.get_deployed_containers()
- existing_container = None
-
- # if the container isn't running (or if we stopped the
- # old version above), create and (maybe) start it up now
- if not existing_container:
- containers = manager.create_containers(1)
- if state == "present": # otherwise it get (re)started later anyways..
- manager.start_containers(containers)
- running_containers = manager.get_running_containers()
- deployed_containers = manager.get_deployed_containers()
-
- if state == "running":
- # make sure a container with `name` is running
- if name and "/" + name not in map(lambda x: x.get('Name'), running_containers):
- manager.start_containers(deployed_containers)
-
- # start more containers if we don't have enough
- elif delta > 0:
- containers = manager.create_containers(delta)
- manager.start_containers(containers)
-
- # stop containers if we have too many
- elif delta < 0:
- containers_to_stop = running_containers[0:abs(delta)]
- containers = manager.stop_containers(containers_to_stop)
- manager.remove_containers(containers_to_stop)
-
- facts = manager.get_running_containers()
- else:
- facts = manager.get_deployed_containers()
-
- # stop and remove containers
- elif state == "absent":
- facts = manager.stop_containers(deployed_containers)
- manager.remove_containers(deployed_containers)
-
- # stop containers
- elif state == "stopped":
- facts = manager.stop_containers(running_containers)
-
- # kill containers
- elif state == "killed":
- manager.kill_containers(running_containers)
-
- # restart containers
- elif state == "restarted":
- manager.restart_containers(running_containers)
- facts = manager.get_inspect_containers(running_containers)
-
- msg = "%s container(s) running image %s with command %s" % \
- (manager.get_summary_counters_msg(), module.params.get('image'), module.params.get('command'))
- changed = manager.has_changed()
-
- module.exit_json(failed=failed, changed=changed, msg=msg, ansible_facts=_ansible_facts(facts))
-
- except DockerAPIError, e:
- changed = manager.has_changed()
- module.exit_json(failed=True, changed=changed, msg="Docker API error: " + e.explanation)
-
- except RequestException, e:
- changed = manager.has_changed()
- module.exit_json(failed=True, changed=changed, msg=repr(e))
-
-# import module snippets
-from ansible.module_utils.basic import *
-
-main()