| from __future__ import absolute_import |
| from __future__ import unicode_literals |
| |
| import logging |
| |
| from docker.errors import NotFound |
| from docker.utils import create_ipam_config |
| from docker.utils import create_ipam_pool |
| |
| from .config import ConfigurationError |
| |
| |
| log = logging.getLogger(__name__) |
| |
| |
| class Network(object): |
| def __init__(self, client, project, name, driver=None, driver_opts=None, |
| ipam=None, external_name=None): |
| self.client = client |
| self.project = project |
| self.name = name |
| self.driver = driver |
| self.driver_opts = driver_opts |
| self.ipam = create_ipam_config_from_dict(ipam) |
| self.external_name = external_name |
| |
| def ensure(self): |
| if self.external_name: |
| try: |
| self.inspect() |
| log.debug( |
| 'Network {0} declared as external. No new ' |
| 'network will be created.'.format(self.name) |
| ) |
| except NotFound: |
| raise ConfigurationError( |
| 'Network {name} declared as external, but could' |
| ' not be found. Please create the network manually' |
| ' using `{command} {name}` and try again.'.format( |
| name=self.external_name, |
| command='docker network create' |
| ) |
| ) |
| return |
| |
| try: |
| data = self.inspect() |
| if self.driver and data['Driver'] != self.driver: |
| raise ConfigurationError( |
| 'Network "{}" needs to be recreated - driver has changed' |
| .format(self.full_name)) |
| if data['Options'] != (self.driver_opts or {}): |
| raise ConfigurationError( |
| 'Network "{}" needs to be recreated - options have changed' |
| .format(self.full_name)) |
| except NotFound: |
| driver_name = 'the default driver' |
| if self.driver: |
| driver_name = 'driver "{}"'.format(self.driver) |
| |
| log.info( |
| 'Creating network "{}" with {}' |
| .format(self.full_name, driver_name) |
| ) |
| |
| self.client.create_network( |
| name=self.full_name, |
| driver=self.driver, |
| options=self.driver_opts, |
| ipam=self.ipam, |
| ) |
| |
| def remove(self): |
| if self.external_name: |
| log.info("Network %s is external, skipping", self.full_name) |
| return |
| |
| log.info("Removing network {}".format(self.full_name)) |
| self.client.remove_network(self.full_name) |
| |
| def inspect(self): |
| return self.client.inspect_network(self.full_name) |
| |
| @property |
| def full_name(self): |
| if self.external_name: |
| return self.external_name |
| return '{0}_{1}'.format(self.project, self.name) |
| |
| |
| def create_ipam_config_from_dict(ipam_dict): |
| if not ipam_dict: |
| return None |
| |
| return create_ipam_config( |
| driver=ipam_dict.get('driver'), |
| pool_configs=[ |
| create_ipam_pool( |
| subnet=config.get('subnet'), |
| iprange=config.get('ip_range'), |
| gateway=config.get('gateway'), |
| aux_addresses=config.get('aux_addresses'), |
| ) |
| for config in ipam_dict.get('config', []) |
| ], |
| ) |
| |
| |
| def build_networks(name, config_data, client): |
| network_config = config_data.networks or {} |
| networks = { |
| network_name: Network( |
| client=client, project=name, name=network_name, |
| driver=data.get('driver'), |
| driver_opts=data.get('driver_opts'), |
| ipam=data.get('ipam'), |
| external_name=data.get('external_name'), |
| ) |
| for network_name, data in network_config.items() |
| } |
| |
| if 'default' not in networks: |
| networks['default'] = Network(client, name, 'default') |
| |
| return networks |
| |
| |
| class ProjectNetworks(object): |
| |
| def __init__(self, networks, use_networking): |
| self.networks = networks or {} |
| self.use_networking = use_networking |
| |
| @classmethod |
| def from_services(cls, services, networks, use_networking): |
| service_networks = { |
| network: networks.get(network) |
| for service in services |
| for network in get_network_names_for_service(service) |
| } |
| unused = set(networks) - set(service_networks) - {'default'} |
| if unused: |
| log.warn( |
| "Some networks were defined but are not used by any service: " |
| "{}".format(", ".join(unused))) |
| return cls(service_networks, use_networking) |
| |
| def remove(self): |
| if not self.use_networking: |
| return |
| for network in self.networks.values(): |
| try: |
| network.remove() |
| except NotFound: |
| log.warn("Network %s not found.", network.full_name) |
| |
| def initialize(self): |
| if not self.use_networking: |
| return |
| |
| for network in self.networks.values(): |
| network.ensure() |
| |
| |
| def get_network_defs_for_service(service_dict): |
| if 'network_mode' in service_dict: |
| return {} |
| networks = service_dict.get('networks', {'default': None}) |
| return dict( |
| (net, (config or {})) |
| for net, config in networks.items() |
| ) |
| |
| |
| def get_network_names_for_service(service_dict): |
| return get_network_defs_for_service(service_dict).keys() |
| |
| |
| def get_networks(service_dict, network_definitions): |
| networks = {} |
| for name, netdef in get_network_defs_for_service(service_dict).items(): |
| network = network_definitions.get(name) |
| if network: |
| networks[network.full_name] = netdef |
| else: |
| raise ConfigurationError( |
| 'Service "{}" uses an undefined network "{}"' |
| .format(service_dict['name'], name)) |
| |
| return networks |