diff --git a/zun/container/docker/driver.py b/zun/container/docker/driver.py index 59e2ce53d..5b495ca3e 100644 --- a/zun/container/docker/driver.py +++ b/zun/container/docker/driver.py @@ -284,9 +284,14 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, kwargs['volumes'] = [b['bind'] for b in binds.values()] self._process_exposed_ports(network_driver.neutron_api, container, kwargs) - self._process_networking_config( - context, container, requested_networks, host_config, - kwargs, docker) + # Process the first requested network at create time. The rest + # will be processed after create. + requested_network = requested_networks.pop() + security_group_ids = utils.get_security_group_ids( + context, container.security_groups) + network_driver.process_networking_config( + container, requested_network, host_config, kwargs, docker, + security_group_ids=security_group_ids) if container.auto_remove: host_config['auto_remove'] = container.auto_remove if self._should_limit_memory(container): @@ -377,48 +382,10 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, ports.append((port, proto)) kwargs['ports'] = ports - def _process_networking_config(self, context, container, - requested_networks, host_config, - container_kwargs, docker): - network_driver = zun_network.driver(context=context, docker_api=docker) - neutron_api = network_driver.neutron_api - - # Process the first requested network at create time. The rest - # will be processed after create. - requested_network = requested_networks.pop() - docker_net_name = self._get_docker_network_name( - context, requested_network['network']) - security_group_ids = utils.get_security_group_ids( - context, container.security_groups) - docker_network = network_driver.inspect_network(docker_net_name) - device_owner = network_driver.get_device_owner() - neutron_net_id = docker_network['Options']['neutron.net.uuid'] - addresses, port = neutron_api.create_or_update_port( - container, neutron_net_id, requested_network, device_owner, - security_group_ids, set_binding_host=True) - container.addresses = {requested_network['network']: addresses} - - ipv4_address = None - ipv6_address = None - for address in addresses: - if address['version'] == 4: - ipv4_address = address['addr'] - if address['version'] == 6: - ipv6_address = address['addr'] - - endpoint_config = docker.create_endpoint_config( - ipv4_address=ipv4_address, ipv6_address=ipv6_address) - network_config = docker.create_networking_config({ - docker_net_name: endpoint_config}) - - host_config['network_mode'] = docker_net_name - container_kwargs['networking_config'] = network_config - container_kwargs['mac_address'] = port['mac_address'] - def _provision_network(self, context, network_driver, requested_networks): for rq_network in requested_networks: - self._get_or_create_docker_network( - context, network_driver, rq_network['network']) + network_driver.get_or_create_network(context, + rq_network['network']) def _get_secgorup_name(self, container_uuid): return consts.NAME_PREFIX + container_uuid @@ -443,11 +410,8 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, # This network is already setup so skip it continue - docker_net_name = self._get_docker_network_name( - context, network['network']) addrs = network_driver.connect_container_to_network( - container, docker_net_name, network, - security_groups=security_group_ids) + container, network, security_groups=security_group_ids) addresses[network['network']] = addrs return addresses @@ -475,9 +439,8 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, if not container.addresses: return for neutron_net in container.addresses: - docker_net = neutron_net network_driver.disconnect_container_from_network( - container, docker_net, neutron_network_id=neutron_net) + container, neutron_network_id=neutron_net) def _cleanup_exposed_ports(self, neutron_api, container): exposed_ports = {} @@ -1031,21 +994,6 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, def _encode_utf8(self, value): return value.encode('utf-8') - def _get_or_create_docker_network(self, context, network_driver, - neutron_net_id): - docker_net_name = self._get_docker_network_name(context, - neutron_net_id) - docker_networks = network_driver.list_networks(names=[docker_net_name]) - if not docker_networks: - network_driver.create_network(neutron_net_id=neutron_net_id, - name=docker_net_name) - - def _get_docker_network_name(self, context, neutron_net_id): - # Note(kiseok7): neutron_net_id is a unique ID in neutron networks and - # docker networks. - # so it will not be duplicated across projects. - return neutron_net_id - def get_container_name(self, container): return consts.NAME_PREFIX + container.uuid @@ -1146,9 +1094,8 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, with docker_utils.docker_client() as docker: network_driver = zun_network.driver(context, docker_api=docker) - docker_net = self._get_docker_network_name(context, network) - network_driver.disconnect_container_from_network( - container, docker_net, network) + network_driver.disconnect_container_from_network(container, + network) # Only clear network info related to this network # Cannot del container.address directly which will not update @@ -1165,8 +1112,7 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, if container.security_groups: security_group_ids = utils.get_security_group_ids( context, container.security_groups) - network_driver = zun_network.driver(context, - docker_api=docker) + network_driver = zun_network.driver(context, docker_api=docker) network = requested_network['network'] if network in container.addresses: raise exception.ZunException('Container %(container)s has ' @@ -1174,11 +1120,9 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, 'network %(network)s.' % {'container': container.uuid, 'network': network}) - self._get_or_create_docker_network(context, network_driver, - network) - docker_net_name = self._get_docker_network_name(context, network) + network_driver.get_or_create_network(context, network) addrs = network_driver.connect_container_to_network( - container, docker_net_name, requested_network, + container, requested_network, security_groups=security_group_ids) if addrs is None: raise exception.ZunException(_( @@ -1192,13 +1136,8 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver, def create_network(self, context, neutron_net_id): with docker_utils.docker_client() as docker: - network_driver = zun_network.driver(context, - docker_api=docker) - docker_net_name = self._get_docker_network_name( - context, neutron_net_id) - return network_driver.create_network( - neutron_net_id=neutron_net_id, - name=docker_net_name) + network_driver = zun_network.driver(context, docker_api=docker) + return network_driver.create_network(neutron_net_id) def delete_network(self, context, network): with docker_utils.docker_client() as docker: diff --git a/zun/container/driver.py b/zun/container/driver.py index 48f238212..1da3a1bbc 100644 --- a/zun/container/driver.py +++ b/zun/container/driver.py @@ -447,9 +447,6 @@ class ContainerDriver(object): def delete_network(self, context, network): raise NotImplementedError() - def inspect_network(self, network): - raise NotImplementedError() - def pull_image(self, context, repo, tag, **kwargs): raise NotImplementedError() diff --git a/zun/network/kuryr_network.py b/zun/network/kuryr_network.py index 4f375d759..2c8aa4e07 100644 --- a/zun/network/kuryr_network.py +++ b/zun/network/kuryr_network.py @@ -40,7 +40,13 @@ class KuryrNetwork(network.Network): self.neutron_api = neutron.NeutronAPI(context) self.context = context - def create_network(self, name, neutron_net_id): + def get_or_create_network(self, context, neutron_net_id): + docker_net_name = neutron_net_id + docker_networks = self.docker.networks(names=[docker_net_name]) + if not docker_networks: + self.create_network(neutron_net_id) + + def create_network(self, neutron_net_id): """Create a docker network with Kuryr driver. The docker network to be created will be based on the specified @@ -53,6 +59,7 @@ class KuryrNetwork(network.Network): neutron net, retrieving the cidr, gateway of each subnet, and compile the list of parameters for docker.create_network. """ + name = neutron_net_id # find a v4 and/or v6 subnet of the network shared = \ self.neutron_api.get_neutron_network(neutron_net_id)[ @@ -136,7 +143,7 @@ class KuryrNetwork(network.Network): "%(networks)s", {"net_id": network.neutron_net_id, "networks": networks}) - docker_networks = self.list_networks(names=[network.name]) + docker_networks = self.docker.networks(names=[network.name]) LOG.debug("docker networks with name matching '%(name)s': " "%(networks)s", {"name": network.name, @@ -187,26 +194,44 @@ class KuryrNetwork(network.Network): self.docker.remove_network(network.name) network.destroy() - def inspect_network(self, network_name): - return self.docker.inspect_network(network_name) + def process_networking_config(self, container, requested_network, + host_config, container_kwargs, docker, + security_group_ids): + docker_net_name = requested_network['network'] + neutron_net_id = requested_network['network'] + addresses, port = self.neutron_api.create_or_update_port( + container, neutron_net_id, requested_network, DEVICE_OWNER, + security_group_ids, set_binding_host=True) + container.addresses = {requested_network['network']: addresses} - def list_networks(self, **kwargs): - return self.docker.networks(**kwargs) + ipv4_address = None + ipv6_address = None + for address in addresses: + if address['version'] == 4: + ipv4_address = address['addr'] + if address['version'] == 6: + ipv6_address = address['addr'] - def get_device_owner(self): - return DEVICE_OWNER + endpoint_config = docker.create_endpoint_config( + ipv4_address=ipv4_address, ipv6_address=ipv6_address) + network_config = docker.create_networking_config({ + docker_net_name: endpoint_config}) - def connect_container_to_network(self, container, network_name, - requested_network, security_groups=None): + host_config['network_mode'] = docker_net_name + container_kwargs['networking_config'] = network_config + container_kwargs['mac_address'] = port['mac_address'] + + def connect_container_to_network(self, container, requested_network, + security_groups=None): """Connect container to the network This method will create a neutron port, retrieve the ip address(es) of the port, and pass them to docker.connect_container_to_network. """ + network_name = requested_network['network'] container_id = container.container_id - network = self.inspect_network(network_name) - neutron_net_id = network['Options']['neutron.net.uuid'] + neutron_net_id = requested_network['network'] addresses, original_port = self.neutron_api.create_or_update_port( container, neutron_net_id, requested_network, DEVICE_OWNER, security_groups) @@ -259,8 +284,8 @@ class KuryrNetwork(network.Network): LOG.debug('Unable to delete port %s as it no longer ' 'exists.', port_id) - def disconnect_container_from_network(self, container, network_name, - neutron_network_id=None): + def disconnect_container_from_network(self, container, neutron_network_id): + network_name = neutron_network_id container_id = container.container_id addrs_list = [] diff --git a/zun/network/network.py b/zun/network/network.py index 82616d1a2..d96522125 100644 --- a/zun/network/network.py +++ b/zun/network/network.py @@ -37,16 +37,16 @@ class Network(object, metaclass=abc.ABCMeta): def init(self, context, *args, **kwargs): raise NotImplementedError() + def get_or_create_network(self, *args, **kwargs): + raise NotImplementedError() + def create_network(self, *args, **kwargs): raise NotImplementedError() def remove_network(self, network_name, **kwargs): raise NotImplementedError() - def inspect_network(self, network_name, **kwargs): - raise NotImplementedError() - - def list_networks(self, **kwargs): + def process_networking_config(self, *args, **kwargs): raise NotImplementedError() def connect_container_to_network(self, container, network_name, **kwargs): diff --git a/zun/tests/unit/container/docker/test_docker_driver.py b/zun/tests/unit/container/docker/test_docker_driver.py index d373db585..563924886 100644 --- a/zun/tests/unit/container/docker/test_docker_driver.py +++ b/zun/tests/unit/container/docker/test_docker_driver.py @@ -821,21 +821,19 @@ class TestDockerDriver(base.DriverTestCase): def test_network_detach(self, mock_detach): mock_container = mock.MagicMock() self.driver.network_detach(self.context, mock_container, 'network') - mock_detach.assert_called_once_with(mock_container, - 'network', - mock.ANY) + mock_detach.assert_called_once_with(mock_container, 'network') @mock.patch('zun.network.kuryr_network.KuryrNetwork' '.connect_container_to_network') @mock.patch('zun.network.kuryr_network.KuryrNetwork' '.disconnect_container_from_network') @mock.patch('zun.network.kuryr_network.KuryrNetwork' - '.list_networks') - def test_network_attach(self, mock_list, mock_disconnect, mock_connect): + '.get_or_create_network') + def test_network_attach(self, mock_get_or_create, mock_disconnect, + mock_connect): mock_container = mock.Mock() mock_container.security_groups = None mock_container.addresses = {} - mock_list.return_value = {'network': 'network'} requested_network = {'network': 'network', 'port': '', 'fixed_ip': '', @@ -843,7 +841,6 @@ class TestDockerDriver(base.DriverTestCase): self.driver.network_attach(self.context, mock_container, requested_network) mock_connect.assert_called_once_with(mock_container, - 'network', requested_network, security_groups=None) @@ -863,15 +860,14 @@ class TestDockerDriver(base.DriverTestCase): @mock.patch('zun.network.kuryr_network.KuryrNetwork' '.connect_container_to_network') @mock.patch('zun.network.kuryr_network.KuryrNetwork' - '.list_networks') - def test_network_attach_with_security_group(self, mock_list, + '.get_or_create_network') + def test_network_attach_with_security_group(self, mock_get_or_create, mock_connect, mock_get_sec_group_id): test_sec_group_id = '84e3a4c1-c8cd-46b1-a0d9-c8c35f6a32a4' mock_container = mock.Mock() mock_container.security_groups = ['test_sec_group'] mock_container.addresses = {} - mock_list.return_value = {'network': 'network'} mock_get_sec_group_id.return_value = test_sec_group_id requested_network = {'network': 'network', 'port': '', @@ -880,7 +876,6 @@ class TestDockerDriver(base.DriverTestCase): self.driver.network_attach(self.context, mock_container, requested_network) mock_connect.assert_called_once_with(mock_container, - 'network', requested_network, security_groups=test_sec_group_id) diff --git a/zun/tests/unit/network/test_kuryr_network.py b/zun/tests/unit/network/test_kuryr_network.py index d3d34a6b3..31a802654 100644 --- a/zun/tests/unit/network/test_kuryr_network.py +++ b/zun/tests/unit/network/test_kuryr_network.py @@ -191,15 +191,14 @@ class KuryrNetworkTestCase(base.TestCase): self, mock_neutron_api_cls, mock_save, mock_create): self.network_driver.neutron_api.subnets[0].pop('subnetpool_id') mock_neutron_api_cls.return_value = self.network_driver.neutron_api - name = 'test_kuryr_network' neutron_net_id = 'fake-net-id' with mock.patch.object(self.network_driver.docker, 'create_network', return_value={'Id': 'docker-net'} ) as mock_create_network: - network = self.network_driver.create_network(name, neutron_net_id) + network = self.network_driver.create_network(neutron_net_id) self.assertEqual('docker-net', network.network_id) mock_create_network.assert_called_once_with( - name=name, + name=neutron_net_id, driver='kuryr', enable_ipv6=False, ipam={'Config': [{'Subnet': '10.5.0.0/16', 'Gateway': '10.5.0.1'}], @@ -216,15 +215,14 @@ class KuryrNetworkTestCase(base.TestCase): def test_create_network_with_subnetpool( self, mock_neutron_api_cls, mock_save, mock_create): mock_neutron_api_cls.return_value = self.network_driver.neutron_api - name = 'test_kuryr_network' neutron_net_id = 'fake-net-id' with mock.patch.object(self.network_driver.docker, 'create_network', return_value={'Id': 'docker-net'} ) as mock_create_network: - network = self.network_driver.create_network(name, neutron_net_id) + network = self.network_driver.create_network(neutron_net_id) self.assertEqual('docker-net', network.network_id) mock_create_network.assert_called_once_with( - name=name, + name=neutron_net_id, driver='kuryr', enable_ipv6=False, ipam={'Config': [{'Subnet': '10.5.0.0/16', 'Gateway': '10.5.0.1'}], @@ -242,7 +240,6 @@ class KuryrNetworkTestCase(base.TestCase): def test_create_network_already_exist( self, mock_neutron_api_cls, mock_list, mock_save, mock_create): mock_neutron_api_cls.return_value = self.network_driver.neutron_api - name = 'test_kuryr_network' neutron_net_id = 'fake-net-id' docker_net_id = 'docker-net' fake_network = mock.Mock() @@ -253,34 +250,23 @@ class KuryrNetworkTestCase(base.TestCase): with mock.patch.object(self.network_driver.docker, 'networks', return_value=[{'Id': docker_net_id}] ) as mock_list_network: - network = self.network_driver.create_network(name, neutron_net_id) + network = self.network_driver.create_network(neutron_net_id) self.assertEqual(docker_net_id, network.network_id) mock_list.assert_called_once_with( self.context, filters={'neutron_net_id': neutron_net_id}) - mock_list_network.assert_called_once_with(names=[name]) + mock_list_network.assert_called_once_with(names=[neutron_net_id]) def test_remove_network(self): network = mock.Mock(name='c02afe4e-8350-4263-8078') self.network_driver.remove_network(network) network.destroy.assert_called_once_with() - def test_inspect_network(self): - network_name = 'c02afe4e-8350-4263-8078' - expected = {'Name': 'c02afe4e-8350-4263-8078', - 'Options': {'neutron.net.uuid': '1234567'}} - info = self.network_driver.inspect_network(network_name) - self.assertEqual(expected, info) - - def test_list_networks(self): - expected = [{'Name': 'test_network'}] - networks = self.network_driver.list_networks() - self.assertEqual(expected, networks) - def test_connect_container_to_network(self): container = Container(self.context, **utils.get_test_container()) network_name = 'c02afe4e-8350-4263-8078' requested_net = {'ipv4_address': '10.5.0.22', 'port': 'fake-port-id', + 'network': network_name, 'preserve_on_delete': True} expected_address = [{'version': 4, 'addr': '10.5.0.22', 'port': 'fake-port-id', @@ -292,7 +278,7 @@ class KuryrNetworkTestCase(base.TestCase): with mock.patch.object(self.network_driver.docker, 'connect_container_to_network') as mock_connect: address = self.network_driver.connect_container_to_network( - container, network_name, requested_net) + container, requested_net) self.assertEqual(expected_address, address) mock_connect.assert_called_once_with( @@ -306,6 +292,7 @@ class KuryrNetworkTestCase(base.TestCase): container = Container(self.context, **utils.get_test_container()) network_name = 'c02afe4e-8350-4263-8078' requested_net = {'ipv4_address': '10.5.0.22', + 'network': network_name, 'port': 'fake-port-id', 'preserve_on_delete': True} mock_neutron_api_cls.return_value = self.network_driver.neutron_api @@ -317,7 +304,7 @@ class KuryrNetworkTestCase(base.TestCase): mock.Mock(side_effect=exception.DockerError) self.assertRaises(exception.DockerError, self.network_driver.connect_container_to_network, - container, network_name, requested_net) + container, requested_net) new_port = self.network_driver.neutron_api.list_ports( id='fake-port-id')['ports'][0] self.assertEqual('', new_port['device_id']) @@ -327,7 +314,6 @@ class KuryrNetworkTestCase(base.TestCase): 'preserve_on_delete': False}]} container = Container(self.context, **utils.get_test_container( addresses=addresses)) - network_name = 'c02afe4e-8350-4263-8078' ports = self.network_driver.neutron_api.list_ports( id='fake-port-id')['ports'] self.assertEqual(1, len(ports)) @@ -335,9 +321,9 @@ class KuryrNetworkTestCase(base.TestCase): 'disconnect_container_from_network' ) as mock_disconnect: self.network_driver.disconnect_container_from_network( - container, network_name, 'fake-net-id') + container, 'fake-net-id') mock_disconnect.assert_called_once_with( - container.container_id, network_name) + container.container_id, 'fake-net-id') # assert the neutron port is deleted ports = self.network_driver.neutron_api.list_ports( id='fake-port-id')['ports']