2016-08-12 13:52:37 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
#
|
|
|
|
# Copyright 2016 Red Hat | Ansible
|
2017-07-29 07:20:36 +00:00
|
|
|
# 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
|
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
|
2017-08-16 03:16:38 +00:00
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
2017-03-14 16:07:22 +00:00
|
|
|
'status': ['preview'],
|
2017-03-15 21:28:33 +00:00
|
|
|
'supported_by': 'community'}
|
2017-03-14 16:07:22 +00:00
|
|
|
|
2016-12-06 10:35:05 +00:00
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
DOCUMENTATION = '''
|
|
|
|
module: docker_network
|
|
|
|
version_added: "2.2"
|
|
|
|
short_description: Manage Docker networks
|
|
|
|
description:
|
|
|
|
- Create/remove Docker networks and connect containers to them.
|
|
|
|
- Performs largely the same function as the "docker network" CLI subcommand.
|
|
|
|
options:
|
|
|
|
name:
|
|
|
|
description:
|
|
|
|
- Name of the network to operate on.
|
|
|
|
required: true
|
|
|
|
aliases:
|
|
|
|
- network_name
|
|
|
|
|
|
|
|
connected:
|
|
|
|
description:
|
|
|
|
- List of container names or container IDs to connect to a network.
|
2016-08-12 14:40:46 +00:00
|
|
|
aliases:
|
|
|
|
- containers
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
driver:
|
|
|
|
description:
|
|
|
|
- Specify the type of network. Docker provides bridge and overlay drivers, but 3rd party drivers can also be used.
|
|
|
|
default: bridge
|
|
|
|
|
|
|
|
driver_options:
|
|
|
|
description:
|
|
|
|
- Dictionary of network settings. Consult docker docs for valid options and values.
|
|
|
|
|
|
|
|
force:
|
|
|
|
description:
|
2016-08-23 20:12:57 +00:00
|
|
|
- With state I(absent) forces disconnecting all containers from the
|
|
|
|
network prior to deleting the network. With state I(present) will
|
2016-08-12 13:52:37 +00:00
|
|
|
disconnect all containers, delete the network and re-create the
|
2016-08-23 20:12:57 +00:00
|
|
|
network. This option is required if you have changed the IPAM or
|
2016-08-12 13:52:37 +00:00
|
|
|
driver options and want an existing network to be updated to use the
|
|
|
|
new options.
|
2018-03-15 21:15:24 +00:00
|
|
|
type: bool
|
|
|
|
default: 'no'
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
appends:
|
|
|
|
description:
|
|
|
|
- By default the connected list is canonical, meaning containers not on the list are removed from the network.
|
2016-08-23 20:12:57 +00:00
|
|
|
Use C(appends) to leave existing containers connected.
|
2018-03-15 21:15:24 +00:00
|
|
|
type: bool
|
|
|
|
default: 'no'
|
2016-08-12 13:52:37 +00:00
|
|
|
aliases:
|
|
|
|
- incremental
|
|
|
|
|
2018-11-01 22:59:16 +00:00
|
|
|
enable_ipv6:
|
|
|
|
version_added: 2.8
|
|
|
|
description:
|
|
|
|
- Enable IPv6 networking.
|
|
|
|
type: bool
|
|
|
|
default: null
|
|
|
|
required: false
|
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
ipam_driver:
|
|
|
|
description:
|
2016-10-13 14:47:50 +00:00
|
|
|
- Specify an IPAM driver.
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
ipam_options:
|
|
|
|
description:
|
|
|
|
- Dictionary of IPAM options.
|
2018-11-12 11:33:42 +00:00
|
|
|
- Deprecated in 2.8, will be removed in 2.12. Use parameter C(ipam_config) instead. In Docker 1.10.0, IPAM
|
2018-11-01 22:59:16 +00:00
|
|
|
options were introduced (see L(here,https://github.com/moby/moby/pull/17316)). This module parameter addresses
|
|
|
|
the IPAM config not the newly introduced IPAM options.
|
|
|
|
|
|
|
|
ipam_config:
|
|
|
|
version_added: 2.8
|
|
|
|
description:
|
|
|
|
- List of IPAM config blocks. Consult
|
|
|
|
L(Docker docs,https://docs.docker.com/compose/compose-file/compose-file-v2/#ipam) for valid options and values.
|
2018-11-12 11:33:42 +00:00
|
|
|
Note that I(iprange) is spelled differently here (we use the notation from the Docker Python SDK).
|
2018-11-01 22:59:16 +00:00
|
|
|
type: list
|
|
|
|
default: null
|
|
|
|
required: false
|
2018-11-12 11:33:42 +00:00
|
|
|
suboptions:
|
|
|
|
subnet:
|
|
|
|
description:
|
|
|
|
- IP subset in CIDR notation.
|
|
|
|
type: str
|
|
|
|
iprange:
|
|
|
|
description:
|
|
|
|
- IP address range in CIDR notation.
|
|
|
|
type: str
|
|
|
|
gateway:
|
|
|
|
description:
|
|
|
|
- IP gateway address.
|
|
|
|
type: str
|
|
|
|
aux_addresses:
|
|
|
|
description:
|
|
|
|
- Auxiliary IP addresses used by Network driver, as a mapping from hostname to IP.
|
|
|
|
type: dict
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
state:
|
|
|
|
description:
|
2016-08-23 20:12:57 +00:00
|
|
|
- I(absent) deletes the network. If a network has connected containers, it
|
|
|
|
cannot be deleted. Use the C(force) option to disconnect all containers
|
2016-08-12 13:52:37 +00:00
|
|
|
and delete the network.
|
2016-08-23 20:12:57 +00:00
|
|
|
- I(present) creates the network, if it does not already exist with the
|
2016-08-12 13:52:37 +00:00
|
|
|
specified parameters, and connects the list of containers provided via
|
2016-08-23 20:12:57 +00:00
|
|
|
the connected parameter. Containers not on the list will be disconnected.
|
|
|
|
An empty list will leave no containers connected to the network. Use the
|
|
|
|
C(appends) option to leave existing containers connected. Use the C(force)
|
|
|
|
options to force re-creation of the network.
|
2016-08-12 13:52:37 +00:00
|
|
|
default: present
|
|
|
|
choices:
|
|
|
|
- absent
|
|
|
|
- present
|
|
|
|
|
2018-10-23 06:49:26 +00:00
|
|
|
internal:
|
|
|
|
version_added: 2.8
|
|
|
|
description:
|
|
|
|
- Restrict external access to the network.
|
|
|
|
type: bool
|
|
|
|
default: null
|
|
|
|
required: false
|
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
extends_documentation_fragment:
|
|
|
|
- docker
|
|
|
|
|
2017-03-09 16:20:25 +00:00
|
|
|
author:
|
2016-08-12 13:52:37 +00:00
|
|
|
- "Ben Keith (@keitwb)"
|
|
|
|
- "Chris Houseknecht (@chouseknecht)"
|
2018-10-23 06:49:26 +00:00
|
|
|
- "Dave Bendit (@DBendit)"
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
requirements:
|
|
|
|
- "python >= 2.6"
|
2018-11-01 22:59:16 +00:00
|
|
|
- "docker-py >= 1.10.0"
|
2018-07-11 21:57:39 +00:00
|
|
|
- "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)
|
2018-08-28 16:53:43 +00:00
|
|
|
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."
|
2018-11-01 22:59:16 +00:00
|
|
|
- "The docker server >= 1.10.0"
|
2016-08-12 13:52:37 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
- name: Create a network
|
|
|
|
docker_network:
|
|
|
|
name: network_one
|
|
|
|
|
|
|
|
- name: Remove all but selected list of containers
|
|
|
|
docker_network:
|
|
|
|
name: network_one
|
|
|
|
connected:
|
|
|
|
- container_a
|
|
|
|
- container_b
|
|
|
|
- container_c
|
|
|
|
|
|
|
|
- name: Remove a single container
|
|
|
|
docker_network:
|
|
|
|
name: network_one
|
|
|
|
connected: "{{ fulllist|difference(['container_a']) }}"
|
|
|
|
|
|
|
|
- name: Add a container to a network, leaving existing containers connected
|
|
|
|
docker_network:
|
|
|
|
name: network_one
|
|
|
|
connected:
|
|
|
|
- container_a
|
|
|
|
appends: yes
|
|
|
|
|
2018-11-01 22:59:16 +00:00
|
|
|
- name: Create a network with driver options
|
2016-08-12 13:52:37 +00:00
|
|
|
docker_network:
|
|
|
|
name: network_two
|
|
|
|
driver_options:
|
|
|
|
com.docker.network.bridge.name: net2
|
2018-11-01 22:59:16 +00:00
|
|
|
|
|
|
|
- name: Create a network with custom IPAM config
|
|
|
|
docker_network:
|
|
|
|
name: network_three
|
|
|
|
ipam_config:
|
|
|
|
- subnet: 172.3.27.0/24
|
|
|
|
gateway: 172.3.27.2
|
|
|
|
iprange: 172.3.27.0/26
|
|
|
|
aux_addresses:
|
|
|
|
host1: 172.3.27.3
|
|
|
|
host2: 172.3.27.4
|
|
|
|
|
|
|
|
- name: Create a network with IPv6 IPAM config
|
|
|
|
docker_network:
|
|
|
|
name: network_ipv6_one
|
|
|
|
enable_ipv6: yes
|
|
|
|
ipam_config:
|
|
|
|
- subnet: fdd1:ac8c:0557:7ce1::/64
|
|
|
|
|
|
|
|
- name: Create a network with IPv6 and custom IPv4 IPAM config
|
|
|
|
docker_network:
|
|
|
|
name: network_ipv6_two
|
|
|
|
enable_ipv6: yes
|
|
|
|
ipam_config:
|
|
|
|
- subnet: 172.4.27.0/24
|
|
|
|
- subnet: fdd1:ac8c:0557:7ce2::/64
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
- name: Delete a network, disconnecting all containers
|
|
|
|
docker_network:
|
|
|
|
name: network_one
|
|
|
|
state: absent
|
|
|
|
force: yes
|
|
|
|
'''
|
|
|
|
|
|
|
|
RETURN = '''
|
|
|
|
facts:
|
|
|
|
description: Network inspection results for the affected network.
|
|
|
|
returned: success
|
2017-04-27 11:01:11 +00:00
|
|
|
type: dict
|
2016-08-12 13:52:37 +00:00
|
|
|
sample: {}
|
|
|
|
'''
|
|
|
|
|
2018-11-01 22:59:16 +00:00
|
|
|
import re
|
|
|
|
|
2018-03-06 12:14:31 +00:00
|
|
|
from ansible.module_utils.docker_common import AnsibleDockerClient, DockerBaseClass, HAS_DOCKER_PY_2, HAS_DOCKER_PY_3
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
from docker import utils
|
2018-10-23 05:14:51 +00:00
|
|
|
from docker.errors import NotFound
|
2018-03-06 12:14:31 +00:00
|
|
|
if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3:
|
2017-07-29 07:20:36 +00:00
|
|
|
from docker.types import IPAMPool, IPAMConfig
|
2018-10-18 09:51:58 +00:00
|
|
|
except Exception as dummy:
|
2018-07-11 21:57:39 +00:00
|
|
|
# missing docker-py handled in ansible.module_utils.docker_common
|
2016-08-12 13:52:37 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class TaskParameters(DockerBaseClass):
|
|
|
|
def __init__(self, client):
|
|
|
|
super(TaskParameters, self).__init__()
|
|
|
|
self.client = client
|
|
|
|
|
|
|
|
self.network_name = None
|
|
|
|
self.connected = None
|
|
|
|
self.driver = None
|
|
|
|
self.driver_options = None
|
|
|
|
self.ipam_driver = None
|
|
|
|
self.ipam_options = None
|
2018-11-01 22:59:16 +00:00
|
|
|
self.ipam_config = None
|
2016-08-12 13:52:37 +00:00
|
|
|
self.appends = None
|
|
|
|
self.force = None
|
2018-10-23 06:49:26 +00:00
|
|
|
self.internal = None
|
2016-08-12 13:52:37 +00:00
|
|
|
self.debug = None
|
2018-11-01 22:59:16 +00:00
|
|
|
self.enable_ipv6 = None
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
for key, value in client.module.params.items():
|
|
|
|
setattr(self, key, value)
|
|
|
|
|
|
|
|
|
|
|
|
def container_names_in_network(network):
|
2017-05-24 21:21:24 +00:00
|
|
|
return [c['Name'] for c in network['Containers'].values()] if network['Containers'] else []
|
2016-08-12 13:52:37 +00:00
|
|
|
|
2017-12-07 16:27:06 +00:00
|
|
|
|
2018-11-01 22:59:16 +00:00
|
|
|
CIDR_IPV4 = re.compile(r'^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$')
|
|
|
|
CIDR_IPV6 = re.compile(r'^[0-9a-fA-F:]+/([0-9]|[1-9][0-9]|1[0-2][0-9])$')
|
|
|
|
|
|
|
|
|
|
|
|
def get_ip_version(cidr):
|
|
|
|
"""Gets the IP version of a CIDR string
|
|
|
|
|
|
|
|
:param cidr: Valid CIDR
|
|
|
|
:type cidr: str
|
|
|
|
:return: ``ipv4`` or ``ipv6``
|
|
|
|
:rtype: str
|
|
|
|
:raises ValueError: If ``cidr`` is not a valid CIDR
|
|
|
|
"""
|
|
|
|
if CIDR_IPV4.match(cidr):
|
|
|
|
return 'ipv4'
|
|
|
|
elif CIDR_IPV6.match(cidr):
|
|
|
|
return 'ipv6'
|
|
|
|
raise ValueError('"{0}" is not a valid CIDR'.format(cidr))
|
|
|
|
|
|
|
|
|
2018-11-06 14:39:45 +00:00
|
|
|
def get_driver_options(driver_options):
|
|
|
|
result = dict()
|
|
|
|
if driver_options is not None:
|
|
|
|
for k, v in driver_options.items():
|
|
|
|
# Go doesn't like 'True' or 'False'
|
|
|
|
if v is True:
|
|
|
|
v = 'true'
|
|
|
|
elif v is False:
|
|
|
|
v = 'false'
|
|
|
|
else:
|
|
|
|
v = str(v)
|
|
|
|
result[str(k)] = v
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
class DockerNetworkManager(object):
|
|
|
|
|
|
|
|
def __init__(self, client):
|
|
|
|
self.client = client
|
|
|
|
self.parameters = TaskParameters(client)
|
|
|
|
self.check_mode = self.client.check_mode
|
|
|
|
self.results = {
|
|
|
|
u'changed': False,
|
|
|
|
u'actions': []
|
|
|
|
}
|
|
|
|
self.diff = self.client.module._diff
|
|
|
|
|
|
|
|
self.existing_network = self.get_existing_network()
|
|
|
|
|
|
|
|
if not self.parameters.connected and self.existing_network:
|
|
|
|
self.parameters.connected = container_names_in_network(self.existing_network)
|
|
|
|
|
2018-11-12 11:33:42 +00:00
|
|
|
if (self.parameters.ipam_options['subnet'] or self.parameters.ipam_options['iprange'] or
|
|
|
|
self.parameters.ipam_options['gateway'] or self.parameters.ipam_options['aux_addresses']):
|
2018-11-01 22:59:16 +00:00
|
|
|
self.parameters.ipam_config = [self.parameters.ipam_options]
|
|
|
|
|
2018-11-06 14:39:45 +00:00
|
|
|
if self.parameters.driver_options:
|
|
|
|
self.parameters.driver_options = get_driver_options(self.parameters.driver_options)
|
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
state = self.parameters.state
|
|
|
|
if state == 'present':
|
|
|
|
self.present()
|
|
|
|
elif state == 'absent':
|
|
|
|
self.absent()
|
|
|
|
|
|
|
|
def get_existing_network(self):
|
2018-10-23 05:14:51 +00:00
|
|
|
try:
|
|
|
|
return self.client.inspect_network(self.parameters.network_name)
|
|
|
|
except NotFound:
|
2017-08-30 21:32:45 +00:00
|
|
|
return None
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
def has_different_config(self, net):
|
|
|
|
'''
|
|
|
|
Evaluates an existing network and returns a tuple containing a boolean
|
|
|
|
indicating if the configuration is different and a list of differences.
|
|
|
|
|
|
|
|
:param net: the inspection output for an existing network
|
|
|
|
:return: (bool, list)
|
|
|
|
'''
|
|
|
|
different = False
|
|
|
|
differences = []
|
|
|
|
if self.parameters.driver and self.parameters.driver != net['Driver']:
|
|
|
|
different = True
|
|
|
|
differences.append('driver')
|
|
|
|
if self.parameters.driver_options:
|
|
|
|
if not net.get('Options'):
|
|
|
|
different = True
|
|
|
|
differences.append('driver_options')
|
|
|
|
else:
|
2016-12-12 23:16:23 +00:00
|
|
|
for key, value in self.parameters.driver_options.items():
|
2017-05-24 21:21:24 +00:00
|
|
|
if not (key in net['Options']) or value != net['Options'][key]:
|
2016-08-12 13:52:37 +00:00
|
|
|
different = True
|
|
|
|
differences.append('driver_options.%s' % key)
|
2018-11-01 22:59:16 +00:00
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
if self.parameters.ipam_driver:
|
|
|
|
if not net.get('IPAM') or net['IPAM']['Driver'] != self.parameters.ipam_driver:
|
|
|
|
different = True
|
|
|
|
differences.append('ipam_driver')
|
2018-11-01 22:59:16 +00:00
|
|
|
|
|
|
|
if self.parameters.ipam_config is not None and self.parameters.ipam_config:
|
|
|
|
if not net.get('IPAM') or not net['IPAM']['Config']:
|
2016-08-12 13:52:37 +00:00
|
|
|
different = True
|
2018-11-01 22:59:16 +00:00
|
|
|
differences.append('ipam_config')
|
2016-08-12 13:52:37 +00:00
|
|
|
else:
|
2018-11-01 22:59:16 +00:00
|
|
|
for idx, ipam_config in enumerate(self.parameters.ipam_config):
|
|
|
|
net_config = dict()
|
|
|
|
try:
|
|
|
|
ip_version = get_ip_version(ipam_config['subnet'])
|
|
|
|
for net_ipam_config in net['IPAM']['Config']:
|
|
|
|
if ip_version == get_ip_version(net_ipam_config['Subnet']):
|
|
|
|
net_config = net_ipam_config
|
|
|
|
except ValueError as e:
|
|
|
|
self.client.fail(str(e))
|
|
|
|
|
|
|
|
for key, value in ipam_config.items():
|
2018-11-12 11:33:42 +00:00
|
|
|
if value is None:
|
|
|
|
# due to recursive argument_spec, all keys are always present
|
|
|
|
# (but have default value None if not specified)
|
|
|
|
continue
|
2018-11-01 22:59:16 +00:00
|
|
|
camelkey = None
|
|
|
|
for net_key in net_config:
|
|
|
|
if key == net_key.lower():
|
|
|
|
camelkey = net_key
|
|
|
|
break
|
|
|
|
if not camelkey or net_config.get(camelkey) != value:
|
|
|
|
different = True
|
|
|
|
differences.append('ipam_config[%s].%s' % (idx, key))
|
|
|
|
|
|
|
|
if self.parameters.enable_ipv6 is not None and self.parameters.enable_ipv6 != net.get('EnableIPv6', False):
|
|
|
|
different = True
|
|
|
|
differences.append('enable_ipv6')
|
|
|
|
|
2018-10-23 06:49:26 +00:00
|
|
|
if self.parameters.internal is not None:
|
|
|
|
if self.parameters.internal:
|
|
|
|
if not net.get('Internal'):
|
|
|
|
different = True
|
|
|
|
differences.append('internal')
|
|
|
|
else:
|
|
|
|
if net.get('Internal'):
|
|
|
|
different = True
|
|
|
|
differences.append('internal')
|
2016-08-12 13:52:37 +00:00
|
|
|
return different, differences
|
|
|
|
|
|
|
|
def create_network(self):
|
|
|
|
if not self.existing_network:
|
2018-11-01 22:59:16 +00:00
|
|
|
params = dict(
|
|
|
|
driver=self.parameters.driver,
|
|
|
|
options=self.parameters.driver_options,
|
|
|
|
)
|
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
ipam_pools = []
|
2018-11-01 22:59:16 +00:00
|
|
|
if self.parameters.ipam_config:
|
|
|
|
for ipam_pool in self.parameters.ipam_config:
|
|
|
|
if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3:
|
|
|
|
ipam_pools.append(IPAMPool(**ipam_pool))
|
|
|
|
else:
|
|
|
|
ipam_pools.append(utils.create_ipam_pool(**ipam_pool))
|
2016-08-12 13:52:37 +00:00
|
|
|
|
2018-11-16 06:20:32 +00:00
|
|
|
if self.parameters.ipam_driver or ipam_pools:
|
|
|
|
# Only add ipam parameter if a driver was specified or if IPAM parameters
|
|
|
|
# were specified. Leaving this parameter away can significantly speed up
|
|
|
|
# creation; on my machine creation with this option needs ~15 seconds,
|
|
|
|
# and without just a few seconds.
|
|
|
|
if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3:
|
|
|
|
params['ipam'] = IPAMConfig(driver=self.parameters.ipam_driver,
|
|
|
|
pool_configs=ipam_pools)
|
|
|
|
else:
|
|
|
|
params['ipam'] = utils.create_ipam_config(driver=self.parameters.ipam_driver,
|
|
|
|
pool_configs=ipam_pools)
|
2018-11-01 22:59:16 +00:00
|
|
|
|
|
|
|
if self.parameters.enable_ipv6 is not None:
|
|
|
|
params['enable_ipv6'] = self.parameters.enable_ipv6
|
|
|
|
if self.parameters.internal is not None:
|
|
|
|
params['internal'] = self.parameters.internal
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
if not self.check_mode:
|
2018-11-01 22:59:16 +00:00
|
|
|
resp = self.client.create_network(self.parameters.network_name, **params)
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
self.existing_network = self.client.inspect_network(resp['Id'])
|
|
|
|
self.results['actions'].append("Created network %s with driver %s" % (self.parameters.network_name, self.parameters.driver))
|
|
|
|
self.results['changed'] = True
|
|
|
|
|
|
|
|
def remove_network(self):
|
|
|
|
if self.existing_network:
|
|
|
|
self.disconnect_all_containers()
|
|
|
|
if not self.check_mode:
|
|
|
|
self.client.remove_network(self.parameters.network_name)
|
|
|
|
self.results['actions'].append("Removed network %s" % (self.parameters.network_name,))
|
|
|
|
self.results['changed'] = True
|
|
|
|
|
|
|
|
def is_container_connected(self, container_name):
|
|
|
|
return container_name in container_names_in_network(self.existing_network)
|
|
|
|
|
|
|
|
def connect_containers(self):
|
|
|
|
for name in self.parameters.connected:
|
|
|
|
if not self.is_container_connected(name):
|
|
|
|
if not self.check_mode:
|
|
|
|
self.client.connect_container_to_network(name, self.parameters.network_name)
|
|
|
|
self.results['actions'].append("Connected container %s" % (name,))
|
|
|
|
self.results['changed'] = True
|
|
|
|
|
|
|
|
def disconnect_missing(self):
|
2017-08-23 16:10:53 +00:00
|
|
|
if not self.existing_network:
|
|
|
|
return
|
2017-05-24 21:21:24 +00:00
|
|
|
containers = self.existing_network['Containers']
|
|
|
|
if not containers:
|
|
|
|
return
|
|
|
|
for c in containers.values():
|
2016-08-12 13:52:37 +00:00
|
|
|
name = c['Name']
|
|
|
|
if name not in self.parameters.connected:
|
|
|
|
self.disconnect_container(name)
|
|
|
|
|
|
|
|
def disconnect_all_containers(self):
|
|
|
|
containers = self.client.inspect_network(self.parameters.network_name)['Containers']
|
2017-05-24 21:21:24 +00:00
|
|
|
if not containers:
|
|
|
|
return
|
2016-08-12 13:52:37 +00:00
|
|
|
for cont in containers.values():
|
|
|
|
self.disconnect_container(cont['Name'])
|
|
|
|
|
|
|
|
def disconnect_container(self, container_name):
|
|
|
|
if not self.check_mode:
|
|
|
|
self.client.disconnect_container_from_network(container_name, self.parameters.network_name)
|
|
|
|
self.results['actions'].append("Disconnected container %s" % (container_name,))
|
|
|
|
self.results['changed'] = True
|
|
|
|
|
|
|
|
def present(self):
|
|
|
|
different = False
|
|
|
|
differences = []
|
|
|
|
if self.existing_network:
|
|
|
|
different, differences = self.has_different_config(self.existing_network)
|
|
|
|
|
|
|
|
if self.parameters.force or different:
|
|
|
|
self.remove_network()
|
|
|
|
self.existing_network = None
|
|
|
|
|
|
|
|
self.create_network()
|
|
|
|
self.connect_containers()
|
|
|
|
if not self.parameters.appends:
|
|
|
|
self.disconnect_missing()
|
|
|
|
|
|
|
|
if self.diff or self.check_mode or self.parameters.debug:
|
|
|
|
self.results['diff'] = differences
|
|
|
|
|
|
|
|
if not self.check_mode and not self.parameters.debug:
|
|
|
|
self.results.pop('actions')
|
|
|
|
|
2017-04-28 13:45:53 +00:00
|
|
|
self.results['ansible_facts'] = {u'docker_network': self.get_existing_network()}
|
2016-08-12 13:52:37 +00:00
|
|
|
|
|
|
|
def absent(self):
|
|
|
|
self.remove_network()
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
argument_spec = dict(
|
2017-12-07 16:27:06 +00:00
|
|
|
network_name=dict(type='str', required=True, aliases=['name']),
|
2018-11-12 11:33:42 +00:00
|
|
|
connected=dict(type='list', default=[], aliases=['containers'], elements='str'),
|
2017-12-07 16:27:06 +00:00
|
|
|
state=dict(type='str', default='present', choices=['present', 'absent']),
|
|
|
|
driver=dict(type='str', default='bridge'),
|
|
|
|
driver_options=dict(type='dict', default={}),
|
|
|
|
force=dict(type='bool', default=False),
|
|
|
|
appends=dict(type='bool', default=False, aliases=['incremental']),
|
2018-11-01 22:59:16 +00:00
|
|
|
ipam_driver=dict(type='str'),
|
2018-11-12 11:33:42 +00:00
|
|
|
ipam_options=dict(type='dict', default={}, removed_in_version='2.12', options=dict(
|
|
|
|
subnet=dict(type='str'),
|
|
|
|
iprange=dict(type='str'),
|
|
|
|
gateway=dict(type='str'),
|
|
|
|
aux_addresses=dict(type='dict'),
|
|
|
|
)),
|
|
|
|
ipam_config=dict(type='list', elements='dict', options=dict(
|
|
|
|
subnet=dict(type='str'),
|
|
|
|
iprange=dict(type='str'),
|
|
|
|
gateway=dict(type='str'),
|
|
|
|
aux_addresses=dict(type='dict'),
|
|
|
|
)),
|
2018-11-01 22:59:16 +00:00
|
|
|
enable_ipv6=dict(type='bool'),
|
|
|
|
internal=dict(type='bool'),
|
2017-12-07 16:27:06 +00:00
|
|
|
debug=dict(type='bool', default=False)
|
2016-08-12 13:52:37 +00:00
|
|
|
)
|
|
|
|
|
2018-11-01 22:59:16 +00:00
|
|
|
mutually_exclusive = [
|
|
|
|
('ipam_config', 'ipam_options')
|
|
|
|
]
|
|
|
|
|
2016-08-12 13:52:37 +00:00
|
|
|
client = AnsibleDockerClient(
|
|
|
|
argument_spec=argument_spec,
|
2018-11-01 22:59:16 +00:00
|
|
|
mutually_exclusive=mutually_exclusive,
|
2018-10-23 06:49:26 +00:00
|
|
|
supports_check_mode=True,
|
2018-11-01 22:59:16 +00:00
|
|
|
min_docker_version='1.10.0'
|
|
|
|
# "The docker server >= 1.10.0"
|
2016-08-12 13:52:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
cm = DockerNetworkManager(client)
|
|
|
|
client.module.exit_json(**cm.results)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|