Generic VIF controller driver
This patch introduces a driver that manages normal Neutron ports to provide VIFs for Kubernetes Pods. Change-Id: Ice32e96e107f7b7331caca3b79c488532710b4a2 Partially-Implements: blueprint kuryr-k8s-integration
This commit is contained in:
parent
634290839a
commit
d6dd891bef
@ -42,6 +42,9 @@ k8s_opts = [
|
||||
cfg.StrOpt('pod_security_groups_driver',
|
||||
help=_("The driver to determine Neutron security groups for pods"),
|
||||
default='default'),
|
||||
cfg.StrOpt('pod_vif_driver',
|
||||
help=_("The driver that provides VIFs for Kubernetes Pods."),
|
||||
default='generic'),
|
||||
]
|
||||
|
||||
neutron_defaults = [
|
||||
|
@ -130,3 +130,73 @@ class PodSecurityGroupsDriver(DriverBase):
|
||||
:return: list containing security groups' IDs
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class PodVIFDriver(DriverBase):
|
||||
"""Manages Neutron ports to provide VIFs for Kubernetes Pods."""
|
||||
|
||||
ALIAS = 'pod_vif'
|
||||
|
||||
@abc.abstractmethod
|
||||
def request_vif(self, pod, project_id, subnets, security_groups):
|
||||
"""Links Neutron port to pod and returns it as VIF object.
|
||||
|
||||
Implementing drivers must ensure the Neutron port satisfying the
|
||||
requested parameters is present and is valid for specified `pod`. It
|
||||
is up to the implementing drivers to either create new ports on each
|
||||
request or reuse available ports when possible.
|
||||
|
||||
Implementing drivers may return a VIF object with its `active` field
|
||||
set to 'False' to indicate that Neutron port requires additional
|
||||
actions to enable network connectivity after VIF is plugged (e.g.
|
||||
setting up OpenFlow and/or iptables rules by OpenVSwitch agent). In
|
||||
that case the Controller will call driver's `activate_vif` method
|
||||
and the CNI plugin will block until it receives activation
|
||||
confirmation from the Controller.
|
||||
|
||||
:param pod: dict containing Kubernetes Pod object
|
||||
:param project_id: OpenStack project ID
|
||||
:param subnets: dict containing subnet mapping as returned by
|
||||
`PodSubnetsDriver.get_subnets`. If multiple entries
|
||||
are present in that mapping, it is guaranteed that
|
||||
all entries have the same value of `Network.id`.
|
||||
:param security_groups: list containing security groups' IDs as
|
||||
returned by
|
||||
`PodSecurityGroupsDriver.get_security_groups`
|
||||
:return: VIF object
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def release_vif(self, pod, vif):
|
||||
"""Unlinks Neutron port corresponding to VIF object from pod.
|
||||
|
||||
Implementing drivers must ensure the port is either deleted or made
|
||||
available for reuse by `PodVIFDriver.request_vif`.
|
||||
|
||||
:param pod: dict containing Kubernetes Pod object
|
||||
:param vif: VIF object as returned by `PodVIFDriver.request_vif`
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def activate_vif(self, pod, vif):
|
||||
"""Updates VIF to become active.
|
||||
|
||||
Implementing drivers should update the specified `vif` object's
|
||||
`active` field to 'True' but must ensure that the corresponding
|
||||
Neutron port is fully configured (i.e. the container using the `vif`
|
||||
can access the requested network resources).
|
||||
|
||||
Implementing drivers may raise `ResourceNotReady` exception to
|
||||
indicate that port activation should be retried later which will
|
||||
cause `activate_vif` to be called again with the same arguments.
|
||||
|
||||
This method may be called before, after or while the VIF is being
|
||||
plugged by the CNI plugin.
|
||||
|
||||
:param pod: dict containing Kubernetes Pod object
|
||||
:param vif: VIF object as returned by `PodVIFDriver.request_vif`
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
100
kuryr_kubernetes/controller/drivers/generic_vif.py
Normal file
100
kuryr_kubernetes/controller/drivers/generic_vif.py
Normal file
@ -0,0 +1,100 @@
|
||||
# Copyright (c) 2016 Mirantis, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 kuryr.lib._i18n import _LE
|
||||
from kuryr.lib import constants as kl_const
|
||||
from neutronclient.common import exceptions as n_exc
|
||||
from oslo_log import log as logging
|
||||
|
||||
from kuryr_kubernetes import clients
|
||||
from kuryr_kubernetes.controller.drivers import base
|
||||
from kuryr_kubernetes import exceptions as k_exc
|
||||
from kuryr_kubernetes import os_vif_util as ovu
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GenericPodVIFDriver(base.PodVIFDriver):
|
||||
"""Manages normal Neutron ports to provide VIFs for Kubernetes Pods."""
|
||||
|
||||
def request_vif(self, pod, project_id, subnets, security_groups):
|
||||
neutron = clients.get_neutron_client()
|
||||
|
||||
rq = self._get_port_request(pod, project_id, subnets, security_groups)
|
||||
port = neutron.create_port(rq).get('port')
|
||||
vif_plugin = self._get_vif_plugin(port)
|
||||
|
||||
return ovu.neutron_to_osvif_vif(vif_plugin, port, subnets)
|
||||
|
||||
def release_vif(self, pod, vif):
|
||||
neutron = clients.get_neutron_client()
|
||||
|
||||
try:
|
||||
neutron.delete_port(vif.id)
|
||||
except n_exc.PortNotFoundClient:
|
||||
LOG.debug('Unable to release port %s as it no longer exists.',
|
||||
vif.id)
|
||||
|
||||
def activate_vif(self, pod, vif):
|
||||
if vif.active:
|
||||
return
|
||||
|
||||
neutron = clients.get_neutron_client()
|
||||
port = neutron.show_port(vif.id).get('port')
|
||||
|
||||
if port['status'] != kl_const.PORT_STATUS_ACTIVE:
|
||||
raise k_exc.ResourceNotReady(vif)
|
||||
|
||||
vif.active = True
|
||||
|
||||
def _get_port_request(self, pod, project_id, subnets, security_groups):
|
||||
port_req_body = {'project_id': project_id,
|
||||
'name': self._get_port_name(pod),
|
||||
'network_id': self._get_network_id(subnets),
|
||||
'fixed_ips': ovu.osvif_to_neutron_fixed_ips(subnets),
|
||||
'device_owner': kl_const.DEVICE_OWNER,
|
||||
'device_id': self._get_device_id(pod),
|
||||
'admin_state_up': True,
|
||||
'binding:host_id': self._get_host_id(pod)}
|
||||
|
||||
if security_groups:
|
||||
port_req_body['security_groups'] = security_groups
|
||||
|
||||
return {'port': port_req_body}
|
||||
|
||||
def _get_vif_plugin(self, port):
|
||||
return port.get('binding:vif_type')
|
||||
|
||||
def _get_network_id(self, subnets):
|
||||
ids = ovu.osvif_to_neutron_network_ids(subnets)
|
||||
|
||||
if len(ids) != 1:
|
||||
raise k_exc.IntegrityError(_LE(
|
||||
"Subnet mapping %(subnets)s is not valid: %(num_networks)s "
|
||||
"unique networks found") % {
|
||||
'subnets': subnets,
|
||||
'num_networks': len(ids)})
|
||||
|
||||
return ids[0]
|
||||
|
||||
def _get_port_name(self, pod):
|
||||
return pod['metadata']['name']
|
||||
|
||||
def _get_device_id(self, pod):
|
||||
return pod['metadata']['uid']
|
||||
|
||||
def _get_host_id(self, pod):
|
||||
return pod['spec']['nodeName']
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from kuryr.lib._i18n import _LE
|
||||
|
||||
|
||||
class K8sClientException(Exception):
|
||||
pass
|
||||
@ -22,5 +24,11 @@ class IntegrityError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class ResourceNotReady(Exception):
|
||||
def __init__(self, resource):
|
||||
super(ResourceNotReady, self).__init__(_LE("Resource not ready: %r")
|
||||
% resource)
|
||||
|
||||
|
||||
def format_msg(exception):
|
||||
return "%s: %s" % (exception.__class__.__name__, exception)
|
||||
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
from kuryr.lib._i18n import _LE
|
||||
from kuryr.lib.binding.drivers import utils as kl_utils
|
||||
from kuryr.lib import constants as kl_const
|
||||
@ -261,3 +263,31 @@ def neutron_to_osvif_vif(vif_plugin, neutron_port, subnets):
|
||||
_VIF_MANAGERS[vif_plugin] = mgr
|
||||
|
||||
return mgr.driver(vif_plugin, neutron_port, subnets)
|
||||
|
||||
|
||||
def osvif_to_neutron_fixed_ips(subnets):
|
||||
fixed_ips = []
|
||||
|
||||
for subnet_id, network in six.iteritems(subnets):
|
||||
ips = []
|
||||
if len(network.subnets.objects) > 1:
|
||||
raise k_exc.IntegrityError(_LE(
|
||||
"Network object for subnet %(subnet_id)s is invalid, "
|
||||
"must contain a single subnet, but %(num_subnets)s found") % {
|
||||
'subnet_id': subnet_id,
|
||||
'num_subnets': len(network.subnets.objects)})
|
||||
|
||||
for subnet in network.subnets.objects:
|
||||
if subnet.obj_attr_is_set('ips'):
|
||||
ips.extend([str(ip.address) for ip in subnet.ips.objects])
|
||||
if ips:
|
||||
fixed_ips.extend([{'subnet_id': subnet_id, 'ip_address': ip}
|
||||
for ip in ips])
|
||||
else:
|
||||
fixed_ips.append({'subnet_id': subnet_id})
|
||||
|
||||
return fixed_ips
|
||||
|
||||
|
||||
def osvif_to_neutron_network_ids(subnets):
|
||||
return list(set(net.id for net in six.itervalues(subnets)))
|
||||
|
@ -0,0 +1,232 @@
|
||||
# Copyright (c) 2016 Mirantis, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 kuryr.lib import constants as kl_const
|
||||
from neutronclient.common import exceptions as n_exc
|
||||
|
||||
from kuryr_kubernetes.controller.drivers import generic_vif
|
||||
from kuryr_kubernetes import exceptions as k_exc
|
||||
from kuryr_kubernetes.tests import base as test_base
|
||||
from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix
|
||||
|
||||
|
||||
class GenericPodVIFDriver(test_base.TestCase):
|
||||
|
||||
@mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif')
|
||||
def test_request_vif(self, m_to_vif):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||
|
||||
pod = mock.sentinel.pod
|
||||
project_id = mock.sentinel.project_id
|
||||
subnets = mock.sentinel.subnets
|
||||
security_groups = mock.sentinel.security_groups
|
||||
port = mock.sentinel.port
|
||||
port_request = mock.sentinel.port_request
|
||||
vif = mock.sentinel.vif
|
||||
vif_plugin = mock.sentinel.vif_plugin
|
||||
|
||||
m_to_vif.return_value = vif
|
||||
m_driver._get_port_request.return_value = port_request
|
||||
m_driver._get_vif_plugin.return_value = vif_plugin
|
||||
neutron.create_port.return_value = {'port': port}
|
||||
|
||||
self.assertEqual(vif, cls.request_vif(m_driver, pod, project_id,
|
||||
subnets, security_groups))
|
||||
|
||||
m_driver._get_port_request.assert_called_once_with(
|
||||
pod, project_id, subnets, security_groups)
|
||||
neutron.create_port.assert_called_once_with(port_request)
|
||||
m_driver._get_vif_plugin.assert_called_once_with(port)
|
||||
m_to_vif.assert_called_once_with(vif_plugin, port, subnets)
|
||||
|
||||
def test_release_vif(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||
|
||||
pod = mock.sentinel.pod
|
||||
vif = mock.Mock()
|
||||
|
||||
cls.release_vif(m_driver, pod, vif)
|
||||
|
||||
neutron.delete_port.assert_called_once_with(vif.id)
|
||||
|
||||
def test_release_vif_not_found(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||
|
||||
pod = mock.sentinel.pod
|
||||
vif = mock.Mock()
|
||||
neutron.delete_port.side_effect = n_exc.PortNotFoundClient
|
||||
|
||||
cls.release_vif(m_driver, pod, vif)
|
||||
|
||||
neutron.delete_port.assert_called_once_with(vif.id)
|
||||
|
||||
def test_activate_vif(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||
|
||||
pod = mock.sentinel.pod
|
||||
vif = mock.Mock()
|
||||
vif.active = False
|
||||
port = mock.MagicMock()
|
||||
|
||||
port.__getitem__.return_value = kl_const.PORT_STATUS_ACTIVE
|
||||
neutron.show_port.return_value = {'port': port}
|
||||
|
||||
cls.activate_vif(m_driver, pod, vif)
|
||||
|
||||
neutron.show_port.assert_called_once_with(vif.id)
|
||||
self.assertTrue(vif.active)
|
||||
|
||||
def test_activate_vif_active(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||
|
||||
pod = mock.sentinel.pod
|
||||
vif = mock.Mock()
|
||||
vif.active = True
|
||||
|
||||
cls.activate_vif(m_driver, pod, vif)
|
||||
|
||||
neutron.show_port.assert_not_called()
|
||||
|
||||
def test_activate_vif_not_ready(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||
|
||||
pod = mock.sentinel.pod
|
||||
vif = mock.Mock()
|
||||
vif.active = False
|
||||
port = mock.MagicMock()
|
||||
|
||||
port.__getitem__.return_value = kl_const.PORT_STATUS_DOWN
|
||||
neutron.show_port.return_value = {'port': port}
|
||||
|
||||
self.assertRaises(k_exc.ResourceNotReady, cls.activate_vif,
|
||||
m_driver, pod, vif)
|
||||
|
||||
def _test_get_port_request(self, m_to_fips, security_groups):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
|
||||
pod = mock.sentinel.pod
|
||||
project_id = mock.sentinel.project_id
|
||||
subnets = mock.sentinel.subnets
|
||||
port_name = mock.sentinel.port_name
|
||||
network_id = mock.sentinel.project_id
|
||||
fixed_ips = mock.sentinel.fixed_ips
|
||||
device_id = mock.sentinel.device_id
|
||||
host_id = mock.sentinel.host_id
|
||||
|
||||
m_driver._get_port_name.return_value = port_name
|
||||
m_driver._get_network_id.return_value = network_id
|
||||
m_to_fips.return_value = fixed_ips
|
||||
m_driver._get_device_id.return_value = device_id
|
||||
m_driver._get_host_id.return_value = host_id
|
||||
|
||||
expected = {'port': {'project_id': project_id,
|
||||
'name': port_name,
|
||||
'network_id': network_id,
|
||||
'fixed_ips': fixed_ips,
|
||||
'device_owner': kl_const.DEVICE_OWNER,
|
||||
'device_id': device_id,
|
||||
'admin_state_up': True,
|
||||
'binding:host_id': host_id}}
|
||||
|
||||
if security_groups:
|
||||
expected['port']['security_groups'] = security_groups
|
||||
|
||||
ret = cls._get_port_request(m_driver, pod, project_id, subnets,
|
||||
security_groups)
|
||||
|
||||
self.assertEqual(expected, ret)
|
||||
m_driver._get_port_name.assert_called_once_with(pod)
|
||||
m_driver._get_network_id.assert_called_once_with(subnets)
|
||||
m_to_fips.assert_called_once_with(subnets)
|
||||
m_driver._get_device_id.assert_called_once_with(pod)
|
||||
m_driver._get_host_id.assert_called_once_with(pod)
|
||||
|
||||
@mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_fixed_ips')
|
||||
def test_get_port_request(self, m_to_fips):
|
||||
security_groups = mock.sentinel.security_groups
|
||||
self._test_get_port_request(m_to_fips, security_groups)
|
||||
|
||||
@mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_fixed_ips')
|
||||
def test_get_port_request_no_sg(self, m_to_fips):
|
||||
security_groups = []
|
||||
self._test_get_port_request(m_to_fips, security_groups)
|
||||
|
||||
def test_get_vif_plugin(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
vif_plugin = mock.sentinel.vif_plugin
|
||||
port = {'binding:vif_type': vif_plugin}
|
||||
|
||||
self.assertEqual(vif_plugin, cls._get_vif_plugin(m_driver, port))
|
||||
|
||||
@mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_network_ids')
|
||||
def test_get_network_id(self, m_to_net_ids):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
subnets = mock.sentinel.subnets
|
||||
network_id = mock.sentinel.network_id
|
||||
m_to_net_ids.return_value = [network_id]
|
||||
|
||||
self.assertEqual(network_id, cls._get_network_id(m_driver, subnets))
|
||||
m_to_net_ids.assert_called_once_with(subnets)
|
||||
|
||||
@mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_network_ids')
|
||||
def test_get_network_id_invalid(self, m_to_net_ids):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
subnets = mock.sentinel.subnets
|
||||
m_to_net_ids.return_value = []
|
||||
|
||||
self.assertRaises(k_exc.IntegrityError, cls._get_network_id, m_driver,
|
||||
subnets)
|
||||
|
||||
def test_get_port_name(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
pod_name = mock.sentinel.pod_name
|
||||
pod = {'metadata': {'name': pod_name}}
|
||||
|
||||
self.assertEqual(pod_name, cls._get_port_name(m_driver, pod))
|
||||
|
||||
def test_get_device_id(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
pod_uid = mock.sentinel.pod_uid
|
||||
pod = {'metadata': {'uid': pod_uid}}
|
||||
|
||||
self.assertEqual(pod_uid, cls._get_device_id(m_driver, pod))
|
||||
|
||||
def test_get_host_id(self):
|
||||
cls = generic_vif.GenericPodVIFDriver
|
||||
m_driver = mock.Mock(spec=cls)
|
||||
node = mock.sentinel.pod_uid
|
||||
pod = {'spec': {'nodeName': node}}
|
||||
|
||||
self.assertEqual(node, cls._get_host_id(m_driver, pod))
|
@ -15,7 +15,10 @@
|
||||
|
||||
import mock
|
||||
|
||||
from os_vif.objects import fixed_ip as osv_fixed_ip
|
||||
from os_vif.objects import network as osv_network
|
||||
from os_vif.objects import route as osv_route
|
||||
from os_vif.objects import subnet as osv_subnet
|
||||
from oslo_config import cfg as o_cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
@ -352,3 +355,70 @@ class TestOSVIFUtils(test_base.TestCase):
|
||||
|
||||
self.assertRaises(k_exc.IntegrityError, ovu._make_vif_subnet,
|
||||
subnets, subnet_id)
|
||||
|
||||
def test_osvif_to_neutron_network_ids(self):
|
||||
id_a = mock.sentinel.id_a
|
||||
id_b = mock.sentinel.id_b
|
||||
net1 = mock.Mock()
|
||||
net1.id = id_a
|
||||
net2 = mock.Mock()
|
||||
net2.id = id_b
|
||||
net3 = mock.Mock()
|
||||
net3.id = id_a
|
||||
subnets = {1: net1, 2: net2, 3: net3}
|
||||
|
||||
ret = ovu.osvif_to_neutron_network_ids(subnets)
|
||||
self.assertEqual(2, len(ret))
|
||||
self.assertIn(id_a, ret)
|
||||
self.assertIn(id_b, ret)
|
||||
|
||||
def test_osvif_to_neutron_fixed_ips(self):
|
||||
ip11 = '1.1.1.1'
|
||||
ip12 = '2.2.2.2'
|
||||
ip3 = '3.3.3.3'
|
||||
subnet_id_1 = uuidutils.generate_uuid()
|
||||
subnet_id_2 = uuidutils.generate_uuid()
|
||||
subnet_id_3 = uuidutils.generate_uuid()
|
||||
|
||||
subnet_1 = osv_subnet.Subnet(ips=osv_fixed_ip.FixedIPList(
|
||||
objects=[osv_fixed_ip.FixedIP(address=ip11),
|
||||
osv_fixed_ip.FixedIP(address=ip12)]))
|
||||
subnet_2 = osv_subnet.Subnet()
|
||||
subnet_3 = osv_subnet.Subnet(ips=osv_fixed_ip.FixedIPList(
|
||||
objects=[osv_fixed_ip.FixedIP(address=ip3)]))
|
||||
|
||||
net1 = osv_network.Network(subnets=osv_subnet.SubnetList(
|
||||
objects=[subnet_1]))
|
||||
net2 = osv_network.Network(subnets=osv_subnet.SubnetList(
|
||||
objects=[subnet_2]))
|
||||
net3 = osv_network.Network(subnets=osv_subnet.SubnetList(
|
||||
objects=[subnet_3]))
|
||||
|
||||
subnets = {subnet_id_1: net1, subnet_id_2: net2, subnet_id_3: net3}
|
||||
|
||||
expected = [{'subnet_id': subnet_id_1, 'ip_address': ip11},
|
||||
{'subnet_id': subnet_id_1, 'ip_address': ip12},
|
||||
{'subnet_id': subnet_id_2},
|
||||
{'subnet_id': subnet_id_3, 'ip_address': ip3}]
|
||||
|
||||
ret = ovu.osvif_to_neutron_fixed_ips(subnets)
|
||||
|
||||
def _sort_key(e):
|
||||
return (e.get('subnet_id'), e.get('ip_address'))
|
||||
|
||||
self.assertEqual(sorted(expected, key=_sort_key),
|
||||
sorted(ret, key=_sort_key))
|
||||
|
||||
def test_osvif_to_neutron_fixed_ips_invalid(self):
|
||||
subnet_id = uuidutils.generate_uuid()
|
||||
|
||||
subnet_1 = osv_subnet.Subnet()
|
||||
subnet_2 = osv_subnet.Subnet()
|
||||
|
||||
net = osv_network.Network(subnets=osv_subnet.SubnetList(
|
||||
objects=[subnet_1, subnet_2]))
|
||||
|
||||
subnets = {subnet_id: net}
|
||||
|
||||
self.assertRaises(k_exc.IntegrityError,
|
||||
ovu.osvif_to_neutron_fixed_ips, subnets)
|
||||
|
@ -38,6 +38,9 @@ kuryr_kubernetes.controller.drivers.pod_subnets =
|
||||
kuryr_kubernetes.controller.drivers.pod_security_groups =
|
||||
default = kuryr_kubernetes.controller.drivers.default_security_groups:DefaultPodSecurityGroupsDriver
|
||||
|
||||
kuryr_kubernetes.controller.drivers.pod_vif =
|
||||
generic = kuryr_kubernetes.controller.drivers.generic_vif:GenericPodVIFDriver
|
||||
|
||||
[files]
|
||||
packages =
|
||||
kuryr_kubernetes
|
||||
|
Loading…
Reference in New Issue
Block a user