From 116052f9dbf400f79cf27daaae7bafa0c7552771 Mon Sep 17 00:00:00 2001 From: vikaschoudhary16 Date: Mon, 14 Nov 2016 07:54:18 +0000 Subject: [PATCH] Nested-Containers: vlan driver Add support to enable isolation of container's traffic within host(nova instances) using vlan segmentations. Partially Implements blueprint containers-in-instances Change-Id: If4800594adfac27a8f30dedac4787d79c8634b65 --- kuryr/lib/binding/__init__.py | 8 ++- kuryr/lib/binding/drivers/ipvlan.py | 4 +- kuryr/lib/binding/drivers/macvlan.py | 4 +- kuryr/lib/binding/drivers/veth.py | 4 +- kuryr/lib/binding/drivers/vlan.py | 56 +++++++++++++++++++ kuryr/lib/constants.py | 4 ++ kuryr/lib/exceptions.py | 16 ++++++ .../lib/segmentation_type_drivers/__init__.py | 42 ++++++++++++++ kuryr/lib/segmentation_type_drivers/vlan.py | 33 +++++++++++ 9 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 kuryr/lib/binding/drivers/vlan.py create mode 100644 kuryr/lib/segmentation_type_drivers/__init__.py create mode 100644 kuryr/lib/segmentation_type_drivers/vlan.py diff --git a/kuryr/lib/binding/__init__.py b/kuryr/lib/binding/__init__.py index 636012e7..8953b973 100644 --- a/kuryr/lib/binding/__init__.py +++ b/kuryr/lib/binding/__init__.py @@ -13,7 +13,8 @@ from oslo_config import cfg from oslo_utils import importutils -def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): +def port_bind(endpoint_id, port, subnets, network=None, vm_port=None, + segmentation_id=None): """Binds the Neutron port to the network interface on the host. :param endpoint_id: the ID of the endpoint as string @@ -27,6 +28,7 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): python-neutronclient. Binding is being done for the port of a container which is running inside this Nova instance (either ipvlan/macvlan or a subport). + :param segmentation_id: ID of the segment for container traffic isolation) :returns: the tuple of the names of the veth pair and the tuple of stdout and stderr returned by processutils.execute invoked with the executable script for binding @@ -34,9 +36,9 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): processutils.ProcessExecutionError """ driver = importutils.import_module(cfg.CONF.binding.driver) - return driver.port_bind(endpoint_id, port, subnets, network=network, - vm_port=vm_port) + vm_port=vm_port, + segmentation_id=segmentation_id) def port_unbind(endpoint_id, neutron_port): diff --git a/kuryr/lib/binding/drivers/ipvlan.py b/kuryr/lib/binding/drivers/ipvlan.py index db7efab4..d6de643d 100644 --- a/kuryr/lib/binding/drivers/ipvlan.py +++ b/kuryr/lib/binding/drivers/ipvlan.py @@ -20,7 +20,8 @@ KIND = 'ipvlan' IPVLAN_MODE_L2 = ifinfmsg.ifinfo.ipvlan_data.modes['IPVLAN_MODE_L2'] -def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): +def port_bind(endpoint_id, port, subnets, network=None, vm_port=None, + segmentation_id=None): """Binds the Neutron port to the network interface on the host. :param endpoint_id: the ID of the endpoint as string @@ -33,6 +34,7 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): :param vm_port: the Nova instance port dictionary, as returned by python-neutronclient. Container is running inside this instance (either ipvlan/macvlan or a subport) + :param segmentation_id: ID of the segment for container traffic isolation) :returns: the tuple of the names of the veth pair and the tuple of stdout and stderr returned by processutils.execute invoked with the executable script for binding diff --git a/kuryr/lib/binding/drivers/macvlan.py b/kuryr/lib/binding/drivers/macvlan.py index 57c421d6..d0883e0d 100644 --- a/kuryr/lib/binding/drivers/macvlan.py +++ b/kuryr/lib/binding/drivers/macvlan.py @@ -19,7 +19,8 @@ KIND = 'macvlan' MACVLAN_MODE_BRIDGE = 'bridge' -def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): +def port_bind(endpoint_id, port, subnets, network=None, vm_port=None, + segmentation_id=None): """Binds the Neutron port to the network interface on the host. :param endpoint_id: the ID of the endpoint as string @@ -32,6 +33,7 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): :param vm_port: the Nova instance port dictionary, as returned by python-neutronclient. Container is running inside instance. + :param segmentation_id: ID of the segment for container traffic isolation) :returns: the tuple of the names of the veth pair and the tuple of stdout and stderr returned by processutils.execute invoked with the executable script for binding diff --git a/kuryr/lib/binding/drivers/veth.py b/kuryr/lib/binding/drivers/veth.py index b08c43de..30edf9d2 100644 --- a/kuryr/lib/binding/drivers/veth.py +++ b/kuryr/lib/binding/drivers/veth.py @@ -32,7 +32,8 @@ VIF_DETAILS_KEY = 'binding:vif_details' VIF_TYPE_KEY = 'binding:vif_type' -def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): +def port_bind(endpoint_id, port, subnets, network=None, vm_port=None, + segmentation_id=None): """Binds the Neutron port to the network interface on the host. :param endpoint_id: the ID of the endpoint as string @@ -46,6 +47,7 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None): python-neutronclient. Container port under binding is running inside this instance (either ipvlan/macvlan or a subport) + :param segmentation_id: ID of the segment for container traffic isolation) :returns: the tuple of the names of the veth pair and the tuple of stdout and stderr returned by processutils.execute invoked with the executable script for binding diff --git a/kuryr/lib/binding/drivers/vlan.py b/kuryr/lib/binding/drivers/vlan.py new file mode 100644 index 00000000..7fa470d2 --- /dev/null +++ b/kuryr/lib/binding/drivers/vlan.py @@ -0,0 +1,56 @@ +# 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. +"""It only supports container-in-vm deployments""" + +from kuryr.lib.binding.drivers import nested +from kuryr.lib.binding.drivers import utils + +KIND = 'vlan' + + +def port_bind(endpoint_id, port, subnets, network=None, + vm_port=None, segmentation_id=None): + """Binds the Neutron port to the network interface on the host. + + :param endpoint_id: the ID of the endpoint as string + :param 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 + :param vm_port: the Nova instance port dictionary, as returned by + python-neutronclient. Container is running inside this + instance (either ipvlan/macvlan or a subport) + :param segmentation_id: ID of the segment for container traffic isolation) + :returns: the tuple of the names of the veth pair and the tuple of stdout + and stderr returned by processutils.execute invoked with the + executable script for binding + :raises: kuryr.common.exceptions.VethCreationFailure, + processutils.ProcessExecutionError + """ + ip = utils.get_ipdb() + port_id = port['id'] + _, devname = utils.get_veth_pair_names(port_id) + link_iface = nested.get_link_iface(vm_port) + with ip.create(ifname=devname, kind=KIND, + link=ip.interfaces[link_iface], + address=port.get(utils.MAC_ADDRESS_KEY), + vlan_id=segmentation_id) as container_iface: + utils._configure_container_iface( + container_iface, subnets, + fixed_ips=port.get(utils.FIXED_IP_KEY)) + + return None, devname, ('', None) + + +port_unbind = nested.port_unbind diff --git a/kuryr/lib/constants.py b/kuryr/lib/constants.py index bf1d0c23..631f48ec 100644 --- a/kuryr/lib/constants.py +++ b/kuryr/lib/constants.py @@ -18,3 +18,7 @@ DEVICE_OWNER = 'kuryr:container' NIC_NAME_LEN = 14 VETH_PREFIX = 'tap' CONTAINER_VETH_PREFIX = 't_c' + +# For VLAN type segmentation +MIN_VLAN_TAG = 1 +MAX_VLAN_TAG = 4094 diff --git a/kuryr/lib/exceptions.py b/kuryr/lib/exceptions.py index eba0f93d..6224c8ab 100644 --- a/kuryr/lib/exceptions.py +++ b/kuryr/lib/exceptions.py @@ -94,3 +94,19 @@ class ExportPortFailure(KuryrException): This exception is thrown when performing Neutron security group failed for an exported port and Kuryr can't proceed the expose further. """ + + +class SegmentationIdAllocationFailure(KuryrException): + """Exception represents when segmentation id could not be allocated. + + This exception is thrown when the segmentaion id for the isolation of + container traffic could not be allocated and Kuryr can't proceed further. + """ + + +class SegmentationDriverBindingDriverCompatibilityFailure(KuryrException): + """Exception represents when no segmentation type driver is loaded. + + This exception is thrown when configured binding driver does not have + a supporting segmentation type driver. + """ diff --git a/kuryr/lib/segmentation_type_drivers/__init__.py b/kuryr/lib/segmentation_type_drivers/__init__.py new file mode 100644 index 00000000..e0835b12 --- /dev/null +++ b/kuryr/lib/segmentation_type_drivers/__init__.py @@ -0,0 +1,42 @@ +# 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 oslo_config import cfg +from oslo_utils import importutils + +from kuryr.lib import exceptions as ex + +BASE_PATH = 'kuryr.lib.segmentation_type_drivers' + +driver_name = cfg.CONF.binding.driver.rsplit('.', 1)[1] + +# REVISIT(vikasc): Need to remove this if check +if driver_name == 'vlan': + seg_driver_path = '.'.join([BASE_PATH, driver_name]) + segmentation_driver = importutils.import_module(seg_driver_path) + driver = segmentation_driver.SegmentationDriver() + + +def allocate_segmentation_id(allocated_ids=set()): + """Allocates a segmentation ID.""" + try: + id = driver.allocate_segmentation_id(allocated_ids) + except NameError: + raise ex.SegmentationDriverBindingDriverCompatibilityFailure + return id + + +def release_segmentation_id(id): + """Releases the segmentation ID.""" + try: + driver.release_segmentation_id(id) + except NameError: + raise ex.SegmentationDriverBindingDriverCompatibilityFailure diff --git a/kuryr/lib/segmentation_type_drivers/vlan.py b/kuryr/lib/segmentation_type_drivers/vlan.py new file mode 100644 index 00000000..2fb7c291 --- /dev/null +++ b/kuryr/lib/segmentation_type_drivers/vlan.py @@ -0,0 +1,33 @@ +# 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 six import moves + +from kuryr.lib import constants as const +from kuryr.lib import exceptions + + +class SegmentationDriver(object): + def __init__(self): + self.available_local_vlans = set(moves.range(const.MIN_VLAN_TAG, + const.MAX_VLAN_TAG + 1)) + + def allocate_segmentation_id(self, allocated_ids=set()): + self.available_local_vlans.difference_update(allocated_ids) + try: + allocated = self.available_local_vlans.pop() + except KeyError: + raise exceptions.segmentationIdAllocationFailure + + return allocated + + def release_segmentation_id(self, id): + self.available_local_vlans.add(id)