267d107fe6
If no alias is passed one will now be created for you with the same name as the target link container, as per the documentation
769 lines
26 KiB
Python
769 lines
26 KiB
Python
#!/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: []
|
|
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"
|
|
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
|
|
'''
|
|
|
|
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]
|
|
# 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 = None
|
|
if self.module.params.get('env'):
|
|
self.env = dict(map(lambda x: x.split("=", 1), self.module.params.get('env')))
|
|
|
|
# connect to docker server
|
|
docker_url = urlparse(module.params.get('docker_url'))
|
|
self.client = docker.Client(base_url=docker_url.geturl())
|
|
|
|
|
|
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:
|
|
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 '/' in image:
|
|
image = image.split('/')[1]
|
|
tag = None
|
|
if image.find(':') > 0:
|
|
return image.split(':')
|
|
else:
|
|
return image, 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:
|
|
self.client.pull(params['image'])
|
|
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,
|
|
}
|
|
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'),
|
|
user = dict(default=None),
|
|
password = dict(),
|
|
email = dict(),
|
|
hostname = dict(default=None),
|
|
env = dict(type='list'),
|
|
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)
|
|
)
|
|
)
|
|
|
|
check_dependencies(module)
|
|
|
|
try:
|
|
manager = DockerManager(module)
|
|
state = module.params.get('state')
|
|
count = int(module.params.get('count'))
|
|
name = module.params.get('name')
|
|
|
|
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 and "/" + name not in map(lambda x: x.get('Name'), deployed_containers):
|
|
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()
|