diff --git a/README.rst b/README.rst
index b9ffd6f7..ff03c8b7 100644
--- a/README.rst
+++ b/README.rst
@@ -248,6 +248,48 @@ The bash script creates the following file if it is missing:
Note the root privilege is required for creating and deleting the veth pairs
with `pyroute2 `_ to run.
+
+How to try out nested-containers locally
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Installing OpenStack running devstack with the desired local.conf file but
+ including the next to make use of OVS-firewall and enabling Trunk Ports::
+
+ [[post-config|/$Q_PLUGIN_CONF_FILE]]
+
+ [DEFAULT]
+ service_plugins=trunk
+
+ [securitygroup]
+ firewall_driver=openvswitch
+
+2. Launch a VM with `Neutron trunk port.
+ `
+
+3. Inside the VM install kuryr and kuryr-libnetwork following the normal
+ installation steps (see above steps at `Installing Kuryr's libnetwork
+ driver`).
+
+4. Reconfigure kuryr inside the VM to point to the neutron server and to use the
+ vlan driver::
+ - Configure `/etc/kuryr/kuryr.conf`::
+
+ [binding]
+ driver = kuryr.lib.binding.drivers.vlan
+ link_iface = eth0 # VM vNIC
+
+ [neutron]
+ auth_url = http://KEYSTONE_SERVER_IP:35357/v3/
+ username = admin
+ user_domain_name = Default
+ password = ADMIN_PASSWORD
+ project_name = service
+ project_domain_name = Default
+ auth_type = password
+
+ - Restart kuryr service inside the VM
+
+
Testing Kuryr
-------------
diff --git a/kuryr_libnetwork/controllers.py b/kuryr_libnetwork/controllers.py
index c2e1d0b7..387c17bb 100644
--- a/kuryr_libnetwork/controllers.py
+++ b/kuryr_libnetwork/controllers.py
@@ -208,22 +208,6 @@ def _create_port(endpoint_id, neutron_network_id, interface_mac, fixed_ips):
return rcvd_port['port']
-def _update_port(port, endpoint_id):
- port['name'] = utils.get_neutron_port_name(endpoint_id)
- try:
- response_port = app.neutron.update_port(
- port['id'],
- {'port': {
- 'name': port['name'],
- 'device_owner': lib_const.DEVICE_OWNER,
- 'device_id': endpoint_id}})
- except n_exceptions.NeutronClientException as ex:
- app.logger.error(_LE("Error happened during updating a "
- "Neutron port: %s"), ex)
- raise
- return response_port['port']
-
-
def _get_fixed_ips_by_interface_cidr(subnets, interface_cidrv4,
interface_cidrv6, fixed_ips):
for subnet in subnets:
@@ -273,7 +257,7 @@ def _create_or_update_port(neutron_network_id, endpoint_id,
interface_mac, fixed_ips)
elif num_port == 1:
port = filtered_ports['ports'][0]
- response_port = _update_port(port, endpoint_id)
+ response_port = app.driver.update_port(port, endpoint_id)
else:
raise n_exceptions.DuplicatedResourceException(
"Multiple ports exist for the cidrs {0} and {1}"
diff --git a/kuryr_libnetwork/port_driver/base.py b/kuryr_libnetwork/port_driver/base.py
index a05e4dbe..fba99013 100644
--- a/kuryr_libnetwork/port_driver/base.py
+++ b/kuryr_libnetwork/port_driver/base.py
@@ -49,3 +49,12 @@ class BaseNestedDriver(driver.Driver):
raise exceptions.KuryrException("Cannot find a Neutron port "
"associated to interface name {0}".format(ifname))
+
+ def get_container_iface_name(self, neutron_port_id):
+ """Returns interface name of a container in the default namespace.
+
+ :param neutron_port_id: The ID of a neutron port as string
+ :returns: interface name as string.
+ """
+ _, container_iface_name = utils.get_veth_pair_names(neutron_port_id)
+ return container_iface_name
diff --git a/kuryr_libnetwork/port_driver/driver.py b/kuryr_libnetwork/port_driver/driver.py
index a371cfcd..1826e5aa 100644
--- a/kuryr_libnetwork/port_driver/driver.py
+++ b/kuryr_libnetwork/port_driver/driver.py
@@ -15,8 +15,14 @@ import six
from oslo_utils import importutils
+from neutronclient.common import exceptions as n_exceptions
+
+from kuryr.lib._i18n import _LE
+from kuryr.lib import constants as lib_const
from kuryr.lib import exceptions
+from kuryr_libnetwork import app
from kuryr_libnetwork import config
+from kuryr_libnetwork import utils as libnet_utils
@six.add_metaclass(abc.ABCMeta)
@@ -106,6 +112,32 @@ class Driver(object):
"""
raise NotImplementedError()
+ def update_port(self, port, endpoint_id):
+ """Updates port information and performs extra driver-specific actions.
+
+ It returns the updated port dictionary after the required actions
+ performed depending on the binding driver.
+
+ :param port: a neutron port dictionary returned from
+ python-neutronclient
+ :param endpoint_id: the ID of the endpoint as string
+ :returns: the updated Neutron port id dictionary as returned by
+ python-neutronclient
+ """
+ port['name'] = libnet_utils.get_neutron_port_name(endpoint_id)
+ try:
+ response_port = app.neutron.update_port(port['id'],
+ {'port': {
+ 'name': port['name'],
+ 'device_owner': lib_const.DEVICE_OWNER,
+ 'device_id': endpoint_id
+ }})
+ except n_exceptions.NeutronClientException as ex:
+ app.logger.error(_LE("Error happened during updating a "
+ "Neutron port: %s"), ex)
+ raise
+ return response_port['port']
+
def __str__(self):
return self.__class__.__name__
diff --git a/kuryr_libnetwork/port_driver/drivers/nested.py b/kuryr_libnetwork/port_driver/drivers/nested.py
index 940a9d48..7b14aa6b 100644
--- a/kuryr_libnetwork/port_driver/drivers/nested.py
+++ b/kuryr_libnetwork/port_driver/drivers/nested.py
@@ -15,7 +15,6 @@ from oslo_log import log
from kuryr.lib._i18n import _LE
from kuryr.lib import binding
-from kuryr.lib.binding.drivers import utils
from kuryr.lib import exceptions
from kuryr_libnetwork import app
@@ -121,15 +120,6 @@ class NestedDriver(base.BaseNestedDriver):
self._remove_from_allowed_address_pairs(vm_port, container_ips)
return binding.port_unbind(endpoint_id, neutron_port)
- def get_container_iface_name(self, neutron_port_id):
- """Returns interface name of a container in the default namespace.
-
- :param neutron_port_id: The ID of a neutron port as string
- :returns: interface name as string
- """
- _, container_iface_name = utils.get_veth_pair_names(neutron_port_id)
- return container_iface_name
-
def _add_to_allowed_address_pairs(self, port, ip_addresses,
mac_address=None):
address_pairs = port['allowed_address_pairs']
diff --git a/kuryr_libnetwork/port_driver/drivers/veth.py b/kuryr_libnetwork/port_driver/drivers/veth.py
index 94892298..204415cf 100644
--- a/kuryr_libnetwork/port_driver/drivers/veth.py
+++ b/kuryr_libnetwork/port_driver/drivers/veth.py
@@ -12,6 +12,7 @@
from kuryr.lib import binding
from kuryr.lib.binding.drivers import utils
+
from kuryr_libnetwork.port_driver import driver
diff --git a/kuryr_libnetwork/port_driver/drivers/vlan.py b/kuryr_libnetwork/port_driver/drivers/vlan.py
new file mode 100644
index 00000000..e3c99074
--- /dev/null
+++ b/kuryr_libnetwork/port_driver/drivers/vlan.py
@@ -0,0 +1,172 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from neutronclient.common import exceptions as n_exceptions
+from oslo_log import log
+
+from kuryr.lib._i18n import _LE
+from kuryr.lib import binding
+from kuryr.lib import exceptions
+from kuryr.lib import segmentation_type_drivers as seg_driver
+
+from kuryr_libnetwork import app
+from kuryr_libnetwork.port_driver import base
+
+LOG = log.getLogger(__name__)
+
+
+class VlanDriver(base.BaseNestedDriver):
+ """Driver for container-in-VM deployments with Trunk Ports."""
+
+ BINDING_DRIVERS = ('vlan',)
+
+ def __init__(self):
+ super(VlanDriver, self).__init__()
+
+ self.port_vlan_dic = {}
+ self.trunk_port = self._get_port_from_host_iface(self.link_iface)
+ self._check_for_vlan_ids()
+
+ def _check_for_vlan_ids(self):
+ """Gathers information about vlans already in use."""
+ for subport in self.trunk_port['trunk_details']['sub_ports']:
+ self.port_vlan_dic[subport['port_id']] = subport['segmentation_id']
+
+ def get_supported_bindings(self):
+ """Returns a tuple of supported binding driver names for the driver.
+
+ :returns: a tuple of strings
+ """
+ return self.BINDING_DRIVERS
+
+ def get_default_network_id(self):
+ """Returns a Neutron network ID as per driver logic, if any.
+
+ :returns: the Neutron network UUID as a string
+ :raises: exceptions.KuryrException
+ """
+ return None
+
+ def update_port(self, port, endpoint_id):
+ segmentation_id = self._get_segmentation_id(port['id'])
+ self._attach_subport(self.trunk_port['trunk_details']['trunk_id'],
+ port['id'],
+ segmentation_id)
+ return super(VlanDriver, self).update_port(port, endpoint_id)
+
+ def create_host_iface(self, endpoint_id, neutron_port, subnets,
+ network=None):
+ """Instantiates a host interface and binds it to the host.
+
+ A host linked interface will be created for the specific Neutron port
+ by delegating to the pre-selected kuryr-lib driver.
+ This driver will attach the port to the trunk port as a subport by
+ using a segmentation id available.
+
+ :param endpoint_id: the ID of the endpoint as string
+ :param neutron_port: the container Neutron port dictionary as returned
+ by python-neutronclient
+ :param subnets: an iterable of all the Neutron subnets which the
+ endpoint is trying to join
+ :param network: the Neutron network which the endpoint is trying
+ to join
+ :returns: the tuple of stdout and stderr returned by
+ processutils.execute invoked
+ with the executable script for binding
+ :raises: exceptions.VethCreationFailure,
+ exceptions.KuryrException,
+ n_exceptions.NeutronClientException,
+ processutils.ProcessExecutionError
+ """
+ container_ips = neutron_port['fixed_ips']
+
+ if not container_ips:
+ raise exceptions.KuryrException(
+ "Neutron port {0} does not have fixed_ips."
+ .format(neutron_port['id']))
+
+ vm_port = self._get_port_from_host_iface(self.link_iface)
+
+ segmentation_id = self._get_segmentation_id(neutron_port['id'])
+
+ _, _, (stdout, stderr) = binding.port_bind(
+ endpoint_id, neutron_port, subnets, network, vm_port,
+ segmentation_id)
+
+ return (stdout, stderr)
+
+ def delete_host_iface(self, endpoint_id, neutron_port):
+ """Deletes a host interface after unbinding it from the host.
+
+ The host Slave interface associated to the Neutron port will be deleted
+ by delegating to the selected kuryr-lib driver.
+ This driver will also remove the subport attached to the trunk port
+ and will release its segmentation id
+
+ :param endpoint_id: the ID of the Docker container as string
+ :param neutron_port: a port dictionary returned from
+ python-neutronclient
+ :returns: the tuple of stdout and stderr returned
+ by processutils.execute invoked with the executable script
+ for unbinding
+ :raises: exceptions.VethDeletionFailure,
+ exceptions.KuryrException,
+ n_exceptions.NeutronClientException,
+ processutils.ProcessExecutionError,
+ """
+ vm_port = self._get_port_from_host_iface(self.link_iface)
+
+ stdout, stderr = binding.port_unbind(endpoint_id, neutron_port)
+
+ subports = [{'port_id': neutron_port['id']}]
+ try:
+ app.neutron.trunk_remove_subports(
+ vm_port['trunk_details']['trunk_id'],
+ {'sub_ports': subports})
+ except n_exceptions.NeutronClientException as ex:
+ app.logger.error(_LE("Error happened during subport deletion "
+ "%(port_id)s: %(ex)s"),
+ {'port_id': neutron_port['id'], 'ex': ex})
+ raise
+ self._release_segmentation_id(neutron_port['id'])
+ return stdout, stderr
+
+ def _attach_subport(self, trunk_id, port_id, segmentation_id):
+ subport = [
+ {
+ 'segmentation_id': segmentation_id,
+ 'port_id': port_id,
+ 'segmentation_type': 'vlan'
+ }
+ ]
+ try:
+ app.neutron.trunk_add_subports(trunk_id, {'sub_ports': subport})
+ except n_exceptions.NeutronClientException as ex:
+ app.logger.error(_LE("Error happened adding subport %(port_id)s "
+ "to trunk port %(trunk_id)s: %(ex)s"),
+ port_id, trunk_id, ex)
+ raise
+
+ def _get_segmentation_id(self, id):
+ if id in self.port_vlan_dic.keys():
+ return self.port_vlan_dic[id]
+ seg_id = seg_driver.allocate_segmentation_id(
+ self.port_vlan_dic.values())
+ self.port_vlan_dic[id] = seg_id
+ return seg_id
+
+ def _release_segmentation_id(self, id):
+ seg_driver.release_segmentation_id(id)
+ del self.port_vlan_dic[id]
+
+ def _get_port_vlan(self, port_id):
+ return self.port_vlan_dic[port_id]
diff --git a/kuryr_libnetwork/tests/unit/base.py b/kuryr_libnetwork/tests/unit/base.py
index ac792369..9d6ee6e0 100644
--- a/kuryr_libnetwork/tests/unit/base.py
+++ b/kuryr_libnetwork/tests/unit/base.py
@@ -158,7 +158,8 @@ class TestKuryrBase(TestCase):
neutron_subnet_v6_id=None,
neutron_subnet_v4_address="192.168.1.2",
neutron_subnet_v6_address="fe80::f816:3eff:fe20:57c4",
- device_owner=None):
+ device_owner=None,
+ neutron_trunk_id=None):
# The following fake response is retrieved from the Neutron doc:
# http://developer.openstack.org/api-ref-networking-v2.html#createPort # noqa
fake_port = {
@@ -188,6 +189,9 @@ class TestKuryrBase(TestCase):
"subnet_id": neutron_subnet_v6_id,
"ip_address": neutron_subnet_v6_address
})
+ if neutron_trunk_id:
+ fake_port['port']['trunk_details'] = {'trunk_id': neutron_trunk_id}
+
return fake_port
@classmethod
diff --git a/kuryr_libnetwork/tests/unit/port_driver/drivers/test_vlan.py b/kuryr_libnetwork/tests/unit/port_driver/drivers/test_vlan.py
new file mode 100644
index 00000000..fd1a9cb3
--- /dev/null
+++ b/kuryr_libnetwork/tests/unit/port_driver/drivers/test_vlan.py
@@ -0,0 +1,319 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+from oslo_utils import uuidutils
+
+from kuryr.lib import binding
+from kuryr.lib.binding.drivers import utils
+from kuryr.lib import constants as lib_const
+from kuryr.lib import exceptions
+from kuryr.lib import segmentation_type_drivers as seg_driver
+from kuryr.lib import utils as lib_utils
+from kuryr_libnetwork.port_driver.drivers import vlan
+from kuryr_libnetwork.tests.unit import base
+from kuryr_libnetwork import utils as libnet_utils
+
+mock_interface = mock.MagicMock()
+
+
+class TestVlanDriver(base.TestKuryrBase):
+ """Unit tests for the VlanDriver port driver"""
+
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._check_for_vlan_ids')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_port_from_host_iface')
+ def test_get_supported_bindings(self, mock_trunk_port, mock_vlan_check):
+ mock_trunk_port.return_value = None
+ mock_vlan_check.return_value = None
+ vlan_driver = vlan.VlanDriver()
+ bindings = vlan_driver.get_supported_bindings()
+ self.assertEqual(bindings, vlan.VlanDriver.BINDING_DRIVERS)
+
+ @mock.patch('kuryr_libnetwork.config.CONF')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._check_for_vlan_ids')
+ @mock.patch.object(binding, 'port_bind')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_segmentation_id')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_port_from_host_iface')
+ def test_create_host_iface(self, mock_get_port_from_host,
+ mock_segmentation_id,
+ mock_port_bind, mock_vlan_check, mock_conf):
+ mock_vlan_check.return_value = None
+ fake_endpoint_id = lib_utils.get_hash()
+ fake_neutron_port_id = uuidutils.generate_uuid()
+ fake_neutron_net_id = uuidutils.generate_uuid()
+ fake_neutron_v4_subnet_id = uuidutils.generate_uuid()
+ fake_neutron_v6_subnet_id = uuidutils.generate_uuid()
+ fake_vm_port_id = uuidutils.generate_uuid()
+
+ fake_neutron_port = self._get_fake_port(
+ fake_endpoint_id, fake_neutron_net_id,
+ fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
+ fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
+ '192.168.1.3', 'fe80::f816:3eff:fe1c:36a9')['port']
+ fake_neutron_port['mac_address'] = 'fa:16:3e:20:57:c3'
+ fake_vm_port = self._get_fake_port(
+ fake_endpoint_id, fake_neutron_net_id,
+ fake_vm_port_id, lib_const.PORT_STATUS_ACTIVE,
+ fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
+ '192.168.1.2', 'fe80::f816:3eff:fe20:57c4')['port']
+ fake_vm_port['allowed_address_pairs'] = [
+ {'ip_address': '192.168.1.2',
+ 'mac_address': fake_vm_port['mac_address']},
+ {'ip_address': 'fe80::f816:3eff:fe20:57c4',
+ 'mac_address': fake_vm_port['mac_address']}]
+
+ fake_subnets = self._get_fake_subnets(
+ fake_endpoint_id, fake_neutron_net_id,
+ fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id)['subnets']
+
+ fake_network = mock.sentinel.binding_network
+ mock_conf.binding.link_iface = 'eth0'
+ fake_exec_response = ('fake_stdout', '')
+ fake_segmentation_id = 1
+ mock_port_bind.return_value = ('fake_host_ifname',
+ 'fake_container_ifname', fake_exec_response)
+ mock_segmentation_id.return_value = fake_segmentation_id
+ mock_get_port_from_host.return_value = fake_vm_port
+
+ vlan_driver = vlan.VlanDriver()
+
+ response = vlan_driver.create_host_iface(fake_endpoint_id,
+ fake_neutron_port, fake_subnets, fake_network)
+
+ mock_get_port_from_host.assert_called_with(
+ mock_conf.binding.link_iface)
+ mock_port_bind.assert_called_with(fake_endpoint_id,
+ fake_neutron_port, fake_subnets, fake_network, fake_vm_port,
+ fake_segmentation_id)
+ mock_segmentation_id.assert_called_with(fake_neutron_port['id'])
+
+ self.assertEqual(response, fake_exec_response)
+
+ @mock.patch('kuryr_libnetwork.config.CONF')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._check_for_vlan_ids')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._release_segmentation_id')
+ @mock.patch.object(binding, 'port_unbind')
+ @mock.patch('kuryr_libnetwork.app.neutron.trunk_remove_subports')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_port_from_host_iface')
+ def test_delete_host_iface(self, mock_get_port_from_host,
+ mock_trunk_remove_subports, mock_port_unbind,
+ mock_release_seg_id, mock_vlan_check,
+ mock_conf):
+ mock_vlan_check.return_value = None
+ fake_endpoint_id = lib_utils.get_hash()
+ fake_neutron_port_id = uuidutils.generate_uuid()
+ fake_neutron_net_id = uuidutils.generate_uuid()
+ fake_neutron_trunk_id = uuidutils.generate_uuid()
+ fake_neutron_v4_subnet_id = uuidutils.generate_uuid()
+ fake_neutron_v6_subnet_id = uuidutils.generate_uuid()
+ fake_vm_port_id = uuidutils.generate_uuid()
+
+ fake_neutron_port = self._get_fake_port(
+ fake_endpoint_id, fake_neutron_net_id, fake_neutron_port_id,
+ lib_const.PORT_STATUS_ACTIVE,
+ fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
+ '192.168.1.3', 'fe80::f816:3eff:fe1c:36a9')['port']
+ fake_neutron_port['mac_address'] = 'fa:16:3e:20:57:c3'
+ fake_vm_port = self._get_fake_port(
+ fake_endpoint_id, fake_neutron_net_id,
+ fake_vm_port_id, lib_const.PORT_STATUS_ACTIVE,
+ fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
+ '192.168.1.2', 'fe80::f816:3eff:fe20:57c4',
+ None, fake_neutron_trunk_id)['port']
+ fake_vm_port['allowed_address_pairs'] = [
+ {'ip_address': '192.168.1.3',
+ 'mac_address': fake_neutron_port['mac_address']},
+ {'ip_address': 'fe80::f816:3eff:fe1c:36a9',
+ 'mac_address': fake_neutron_port['mac_address']}]
+
+ mock_conf.binding.link_iface = 'eth0'
+ fake_unbind_response = ('fake_stdout', '')
+ mock_get_port_from_host.return_value = fake_vm_port
+ mock_port_unbind.return_value = fake_unbind_response
+
+ vlan_driver = vlan.VlanDriver()
+
+ response = vlan_driver.delete_host_iface(fake_endpoint_id,
+ fake_neutron_port)
+
+ mock_get_port_from_host.assert_called_with(
+ mock_conf.binding.link_iface)
+ mock_port_unbind.assert_called_with(fake_endpoint_id,
+ fake_neutron_port)
+ mock_trunk_remove_subports.assert_called_with(fake_neutron_trunk_id,
+ {'sub_ports': [{
+ 'port_id': fake_neutron_port_id
+ }]})
+ mock_release_seg_id.assert_called_with(fake_neutron_port_id)
+
+ self.assertEqual(response, fake_unbind_response)
+
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._check_for_vlan_ids')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_port_from_host_iface')
+ @mock.patch.object(utils, 'get_veth_pair_names',
+ return_value=("fake_host_ifname", "fake_container_name"))
+ def test_get_container_iface_name(self, mock_get_pair_names,
+ mock_trunk_port, mock_vlan_check):
+ mock_trunk_port.return_value = None
+ mock_vlan_check.return_value = None
+ vlan_driver = vlan.VlanDriver()
+ fake_neutron_port_id = uuidutils.generate_uuid()
+ response = vlan_driver.get_container_iface_name(fake_neutron_port_id)
+ mock_get_pair_names.assert_called_with(fake_neutron_port_id)
+ self.assertEqual(response, "fake_container_name")
+
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._check_for_vlan_ids')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_port_from_host_iface')
+ @mock.patch('kuryr_libnetwork.app.neutron.trunk_add_subports')
+ def test_attach_subport(self, mock_trunk_add_subports, mock_trunk_port,
+ mock_vlan_check):
+ mock_trunk_port.return_value = None
+ mock_vlan_check.return_value = None
+
+ fake_neutron_trunk_id = uuidutils.generate_uuid()
+ fake_neutron_port_id = uuidutils.generate_uuid()
+ fake_segmentation_id = 1
+ fake_subport = [
+ {
+ 'segmentation_id': fake_segmentation_id,
+ 'port_id': fake_neutron_port_id,
+ 'segmentation_type': 'vlan'
+ }
+ ]
+
+ vlan_driver = vlan.VlanDriver()
+
+ vlan_driver._attach_subport(fake_neutron_trunk_id,
+ fake_neutron_port_id,
+ fake_segmentation_id)
+ mock_trunk_add_subports.assert_called_with(fake_neutron_trunk_id,
+ {'sub_ports': fake_subport})
+
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._check_for_vlan_ids')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_port_from_host_iface')
+ @mock.patch.object(seg_driver, 'allocate_segmentation_id')
+ def test_get_segmentation_id(self, mock_alloc_seg_id, mock_trunk_port,
+ mock_vlan_check):
+ mock_trunk_port.return_value = None
+ mock_vlan_check.return_value = None
+ fake_neutron_port1_id = uuidutils.generate_uuid()
+ fake_neutron_port2_id = uuidutils.generate_uuid()
+ mock_alloc_seg_id.side_effect = [1, 2]
+
+ vlan_driver = vlan.VlanDriver()
+
+ response = vlan_driver._get_segmentation_id(fake_neutron_port1_id)
+ mock_alloc_seg_id.assert_called_once()
+ self.assertEqual(response, 1)
+
+ mock_alloc_seg_id.reset_mock()
+ response = vlan_driver._get_segmentation_id(fake_neutron_port1_id)
+ mock_alloc_seg_id.assert_not_called()
+ self.assertEqual(response, 1)
+
+ response = vlan_driver._get_segmentation_id(fake_neutron_port2_id)
+ mock_alloc_seg_id.assert_called_once()
+ self.assertEqual(response, 2)
+
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._check_for_vlan_ids')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_port_from_host_iface')
+ @mock.patch('kuryr_libnetwork.app.neutron.update_port')
+ @mock.patch.object(libnet_utils, 'get_neutron_port_name')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._attach_subport')
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_segmentation_id')
+ def test_update_port(self, mock_get_seg_id, mock_attach_subport,
+ mock_get_port_name, mock_update_port,
+ mock_get_port_from_host, mock_vlan_check):
+ fake_endpoint_id = lib_utils.get_hash()
+ fake_neutron_port_id = uuidutils.generate_uuid()
+ fake_neutron_net_id = uuidutils.generate_uuid()
+ fake_neutron_trunk_id = uuidutils.generate_uuid()
+ fake_neutron_v4_subnet_id = uuidutils.generate_uuid()
+ fake_neutron_v6_subnet_id = uuidutils.generate_uuid()
+ fake_vm_port_id = uuidutils.generate_uuid()
+
+ fake_neutron_port = self._get_fake_port(
+ fake_endpoint_id, fake_neutron_net_id,
+ fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
+ fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
+ '192.168.1.3', 'fe80::f816:3eff:fe1c:36a9')['port']
+ fake_neutron_port['mac_address'] = 'fa:16:3e:20:57:c3'
+ fake_vm_port = self._get_fake_port(
+ fake_endpoint_id, fake_neutron_net_id,
+ fake_vm_port_id, lib_const.PORT_STATUS_ACTIVE,
+ fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
+ '192.168.1.2', 'fe80::f816:3eff:fe20:57c4',
+ None, fake_neutron_trunk_id)['port']
+ fake_segmentation_id = 1
+ fake_port_name = 'port1'
+
+ mock_get_seg_id.return_value = fake_segmentation_id
+ mock_get_port_name.return_value = fake_port_name
+ mock_get_port_from_host.return_value = fake_vm_port
+ mock_vlan_check.return_value = None
+
+ vlan_driver = vlan.VlanDriver()
+
+ vlan_driver.update_port(fake_neutron_port, fake_endpoint_id)
+
+ mock_get_seg_id.assert_called_with(fake_neutron_port_id)
+
+ mock_get_port_name.assert_called_with(fake_endpoint_id)
+
+ mock_attach_subport.assert_called_with(fake_neutron_trunk_id,
+ fake_neutron_port_id,
+ fake_segmentation_id)
+
+ mock_update_port.assert_called_with(fake_neutron_port_id,
+ {'port': {
+ 'name': fake_port_name,
+ 'device_owner': lib_const.DEVICE_OWNER,
+ 'device_id': fake_endpoint_id
+ }})
+
+
+class TestVlanDriverFailures(base.TestKuryrFailures):
+ """Unit tests for the VlanDriver port driver failures"""
+
+ @mock.patch('kuryr_libnetwork.port_driver.drivers.vlan'
+ '.VlanDriver._get_port_from_host_iface')
+ def test_create_host_iface(self, mock_get_port_from_host):
+ fake_endpoint_id = lib_utils.get_hash()
+ fake_neutron_port_id = uuidutils.generate_uuid()
+ fake_neutron_net_id = uuidutils.generate_uuid()
+
+ fake_neutron_port = self._get_fake_port(
+ fake_endpoint_id, fake_neutron_net_id,
+ fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE)['port']
+
+ vlan_driver = vlan.VlanDriver()
+ self.assertRaises(exceptions.KuryrException,
+ vlan_driver.create_host_iface, fake_endpoint_id,
+ fake_neutron_port, None)
diff --git a/kuryr_libnetwork/tests/unit/port_driver/test_base.py b/kuryr_libnetwork/tests/unit/port_driver/test_base.py
index 82b70421..90ca7d2d 100644
--- a/kuryr_libnetwork/tests/unit/port_driver/test_base.py
+++ b/kuryr_libnetwork/tests/unit/port_driver/test_base.py
@@ -35,6 +35,9 @@ class TestBaseDriver(d_base.BaseNestedDriver):
def get_supported_bindings(self):
pass
+ def update_port(self, neutron_port_id, endpoint_id):
+ pass
+
@ddt.ddt
class TestBaseNestedDriver(base.TestKuryrBase):
diff --git a/kuryr_libnetwork/tests/unit/test_kuryr.py b/kuryr_libnetwork/tests/unit/test_kuryr.py
index 3c90d01b..613a2f39 100644
--- a/kuryr_libnetwork/tests/unit/test_kuryr.py
+++ b/kuryr_libnetwork/tests/unit/test_kuryr.py
@@ -849,7 +849,7 @@ class TestKuryr(base.TestKuryrBase):
@mock.patch('kuryr_libnetwork.controllers.app.driver.create_host_iface')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_networks')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.show_port')
- @mock.patch('kuryr_libnetwork.controllers.app.neutron.update_port')
+ @mock.patch('kuryr_libnetwork.controllers.app.driver.update_port')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_ports')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets')
@mock.patch('kuryr_libnetwork.controllers.app')
@@ -922,7 +922,7 @@ class TestKuryr(base.TestKuryrBase):
fake_updated_port = fake_port_response['port']
fake_updated_port['name'] = utils.get_neutron_port_name(
fake_docker_endpoint_id)
- mock_update_port.return_value = fake_port_response
+ mock_update_port.return_value = fake_port_response['port']
fake_neutron_subnets = [fake_v4_subnet['subnet'],
fake_v6_subnet['subnet']]
@@ -931,7 +931,6 @@ class TestKuryr(base.TestKuryrBase):
mock_create_host_iface.return_value = fake_create_iface_response
if vif_plug_is_fatal:
- mock_vif.vif_plug_is_fatal = vif_plug_is_fatal
fake_neutron_ports_response_2 = self._get_fake_port(
fake_docker_endpoint_id, fake_neutron_net_id,
fake_port_id, lib_const.PORT_STATUS_ACTIVE,
@@ -958,13 +957,8 @@ class TestKuryr(base.TestKuryrBase):
mock_list_subnets.assert_any_call(
network_id=fake_neutron_net_id, cidr='fe80::/64')
mock_list_ports.assert_called_with(fixed_ips=fake_fixed_ips)
- mock_update_port.assert_called_with(
- fake_updated_port['id'],
- {'port': {
- 'name': fake_updated_port['name'],
- 'device_owner': lib_const.DEVICE_OWNER,
- 'device_id': fake_docker_endpoint_id
- }})
+ mock_update_port.assert_called_with(fake_port_response['port'],
+ fake_docker_endpoint_id)
mock_list_networks.assert_any_call(tags=t)
mock_create_host_iface.assert_called_with(fake_docker_endpoint_id,
fake_updated_port, fake_neutron_subnets,
diff --git a/kuryr_libnetwork/tests/unit/test_kuryr_endpoint.py b/kuryr_libnetwork/tests/unit/test_kuryr_endpoint.py
index 4db45572..2d682e74 100644
--- a/kuryr_libnetwork/tests/unit/test_kuryr_endpoint.py
+++ b/kuryr_libnetwork/tests/unit/test_kuryr_endpoint.py
@@ -105,7 +105,7 @@ class TestKuryrEndpointCreateFailures(base.TestKuryrFailures):
self.assertEqual({'Err': GivenException.message}, decoded_json)
@mock.patch('kuryr_libnetwork.controllers.app.driver.create_host_iface')
- @mock.patch('kuryr_libnetwork.controllers.app.neutron.update_port')
+ @mock.patch('kuryr_libnetwork.controllers.app.driver.update_port')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_ports')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_networks')
@@ -172,7 +172,7 @@ class TestKuryrEndpointCreateFailures(base.TestKuryrFailures):
fake_updated_port = fake_port_response['port']
fake_updated_port['name'] = utils.get_neutron_port_name(
fake_docker_endpoint_id)
- mock_update_port.return_value = fake_port_response
+ mock_update_port.return_value = fake_port_response['port']
fake_neutron_subnets = [fake_v4_subnet['subnet'],
fake_v6_subnet['subnet']]
@@ -195,13 +195,8 @@ class TestKuryrEndpointCreateFailures(base.TestKuryrFailures):
mock.call(cidr='fe80::/64', network_id=fake_neutron_network_id)]
mock_list_subnets.assert_has_calls(expect_calls, any_order=True)
mock_list_ports.assert_called_with(fixed_ips=fake_fixed_ips)
- mock_update_port.assert_called_with(
- fake_updated_port['id'],
- {'port': {
- 'name': fake_updated_port['name'],
- 'device_owner': lib_const.DEVICE_OWNER,
- 'device_id': fake_docker_endpoint_id
- }})
+ mock_update_port.assert_called_with(fake_port_response['port'],
+ fake_docker_endpoint_id)
mock_create_host_iface.assert_called_with(
fake_docker_endpoint_id, fake_updated_port, fake_neutron_subnets,
fake_neutron_network['networks'][0])