Add support for VLAN aware VMs in AIM
improve get_request_details to provide the trunk information if needed. Change-Id: I81b0cbb6b1aa12e5a10962005e00e17ea37c78eb
This commit is contained in:
parent
904c5476b8
commit
5beb9c92f5
@ -270,6 +270,12 @@ class LocalAPI(object):
|
||||
raise exc.GroupPolicyDeploymentError()
|
||||
return servicechain_plugin
|
||||
|
||||
@property
|
||||
def _trunk_plugin(self):
|
||||
# REVISIT(rkukura): Need initialization method after all
|
||||
# plugins are loaded to grab and store plugin.
|
||||
return directory.get_plugin('trunk')
|
||||
|
||||
def _create_resource(self, plugin, context, resource, attrs,
|
||||
do_notify=True):
|
||||
# REVISIT(rkukura): Do create.start notification?
|
||||
|
@ -64,6 +64,7 @@ from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import config # noqa
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import db
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import exceptions
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import extension_db
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import trunk_driver
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
DEVICE_OWNER_SNAT_PORT = 'apic:snat-pool'
|
||||
@ -200,6 +201,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
enable_iptables_firewall)
|
||||
local_api.QUEUE_OUT_OF_PROCESS_NOTIFICATIONS = True
|
||||
self._ensure_static_resources()
|
||||
trunk_driver.register()
|
||||
|
||||
def _ensure_static_resources(self):
|
||||
session = db_api.get_writer_session()
|
||||
|
@ -0,0 +1,59 @@
|
||||
# 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_log import log as logging
|
||||
|
||||
from neutron.services.trunk import constants as trunk_consts
|
||||
from neutron.services.trunk.drivers import base
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
|
||||
from opflexagent import constants as ofcst
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
NAME = 'apic_aim'
|
||||
|
||||
SUPPORTED_INTERFACES = (
|
||||
portbindings.VIF_TYPE_OVS,
|
||||
portbindings.VIF_TYPE_VHOST_USER,
|
||||
)
|
||||
|
||||
SUPPORTED_SEGMENTATION_TYPES = (
|
||||
trunk_consts.VLAN,
|
||||
)
|
||||
|
||||
DRIVER = None
|
||||
|
||||
|
||||
class OpflexDriver(base.DriverBase):
|
||||
|
||||
@property
|
||||
def is_loaded(self):
|
||||
try:
|
||||
return NAME in cfg.CONF.ml2.mechanism_drivers
|
||||
except cfg.NoSuchOptError:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def create(cls):
|
||||
return OpflexDriver(NAME,
|
||||
SUPPORTED_INTERFACES,
|
||||
SUPPORTED_SEGMENTATION_TYPES,
|
||||
ofcst.AGENT_TYPE_OPFLEX_OVS)
|
||||
|
||||
|
||||
def register():
|
||||
"""Register the driver."""
|
||||
global DRIVER
|
||||
DRIVER = OpflexDriver.create()
|
||||
LOG.debug('Opflex trunk driver registered')
|
@ -13,6 +13,10 @@
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.common import topics
|
||||
from neutron.db import api as db_api
|
||||
|
||||
from neutron.db import db_base_plugin_common
|
||||
from neutron.objects import base as objects_base
|
||||
from neutron.objects import trunk as trunk_objects
|
||||
from neutron.plugins.ml2 import rpc as ml2_rpc
|
||||
from opflexagent import rpc as o_rpc
|
||||
from oslo_log import log
|
||||
@ -96,7 +100,9 @@ class AIMMappingRPCMixin(ha_ip_db.HAIPOwnerDbMixin):
|
||||
'gbp_details': self._get_gbp_details(context, request,
|
||||
host),
|
||||
'neutron_details': ml2_rpc.RpcCallbacks(
|
||||
None, None).get_device_details(context, **request)}
|
||||
None, None).get_device_details(context, **request),
|
||||
'trunk_details': self._get_trunk_details(context,
|
||||
request, host)}
|
||||
return result
|
||||
except Exception as e:
|
||||
LOG.error("An exception has occurred while requesting device "
|
||||
@ -114,6 +120,42 @@ class AIMMappingRPCMixin(ha_ip_db.HAIPOwnerDbMixin):
|
||||
LOG.debug("APIC ownership update for port %s", p)
|
||||
self._send_port_update_notification(context, p)
|
||||
|
||||
@db_api.retry_db_errors
|
||||
def _get_trunk_details(self, context, request, host):
|
||||
if self._trunk_plugin:
|
||||
device = request.get('device')
|
||||
port_id = self._core_plugin._device_to_port_id(context, device)
|
||||
# Find Trunk associated to this port (if any)
|
||||
trunks = self._trunk_plugin.get_trunks(
|
||||
context, filters={'port_id': [port_id]})
|
||||
subports = None
|
||||
if not trunks:
|
||||
subports = self.retrieve_subports(
|
||||
context, filters={'port_id': [port_id]})
|
||||
if subports:
|
||||
trunks = self._trunk_plugin.get_trunks(
|
||||
context, filters={'id': [subports[0].trunk_id]})
|
||||
if trunks:
|
||||
return {'trunk_id': trunks[0]['id'],
|
||||
'master_port_id': trunks[0]['port_id'],
|
||||
'subports': (
|
||||
[s.to_dict() for s in subports] if subports else
|
||||
self._trunk_plugin.get_subports(
|
||||
context, trunks[0]['id'])['sub_ports'])}
|
||||
|
||||
# NOTE(ivar): for some reason, the Trunk plugin doesn't expose a way to
|
||||
# retrieve a subport starting from the port ID.
|
||||
@db_base_plugin_common.filter_fields
|
||||
def retrieve_subports(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None,
|
||||
page_reverse=False):
|
||||
filters = filters or {}
|
||||
pager = objects_base.Pager(sorts=sorts, limit=limit,
|
||||
page_reverse=page_reverse,
|
||||
marker=marker)
|
||||
return trunk_objects.SubPort.get_objects(context, _pager=pager,
|
||||
**filters)
|
||||
|
||||
# Things you need in order to run this Mixin:
|
||||
# - self._core_plugin: attribute that points to the Neutron core plugin;
|
||||
# - self._is_port_promiscuous(context, port): define whether or not
|
||||
|
@ -82,10 +82,12 @@ class ApiManagerMixin(object):
|
||||
def _create_resource(self, type, expected_res_status=None,
|
||||
is_admin_context=False, **kwargs):
|
||||
plural = cm.get_resource_plural(type)
|
||||
defaults = getattr(cm,
|
||||
'get_create_%s_default_attrs' % type)()
|
||||
defaults_func = getattr(cm, 'get_create_%s_default_attrs' % type,
|
||||
None)
|
||||
defaults = {}
|
||||
if defaults_func:
|
||||
defaults = defaults_func()
|
||||
defaults.update(kwargs)
|
||||
|
||||
data = {type: {'tenant_id': self._tenant_id}}
|
||||
data[type].update(defaults)
|
||||
|
||||
|
@ -41,7 +41,8 @@ class GroupPolicyMappingDbTestCase(tgpdb.GroupPolicyDbTestCase,
|
||||
test_l3.L3NatTestCaseMixin):
|
||||
|
||||
def setUp(self, core_plugin=None, l3_plugin=None, gp_plugin=None,
|
||||
service_plugins=None, sc_plugin=None, qos_plugin=None):
|
||||
service_plugins=None, sc_plugin=None, qos_plugin=None,
|
||||
trunk_plugin=None):
|
||||
if not gp_plugin:
|
||||
gp_plugin = DB_GP_PLUGIN_KLASS
|
||||
if not service_plugins:
|
||||
@ -53,6 +54,8 @@ class GroupPolicyMappingDbTestCase(tgpdb.GroupPolicyDbTestCase,
|
||||
service_plugins['l3_plugin_name'] = l3_plugin or "router"
|
||||
if qos_plugin:
|
||||
service_plugins['qos_plugin_name'] = qos_plugin
|
||||
if trunk_plugin:
|
||||
service_plugins['trunk_plugin_name'] = trunk_plugin
|
||||
super(GroupPolicyMappingDbTestCase, self).setUp(
|
||||
core_plugin=core_plugin, gp_plugin=gp_plugin,
|
||||
service_plugins=service_plugins
|
||||
|
@ -108,7 +108,8 @@ class AIMBaseTestCase(test_nr_base.CommonNeutronBaseTestCase,
|
||||
_extension_path = None
|
||||
|
||||
def setUp(self, policy_drivers=None, core_plugin=None, ml2_options=None,
|
||||
l3_plugin=None, sc_plugin=None, qos_plugin=None, **kwargs):
|
||||
l3_plugin=None, sc_plugin=None, trunk_plugin=None,
|
||||
qos_plugin=None, **kwargs):
|
||||
core_plugin = core_plugin or ML2PLUS_PLUGIN
|
||||
if not l3_plugin:
|
||||
l3_plugin = "apic_aim_l3"
|
||||
@ -137,7 +138,8 @@ class AIMBaseTestCase(test_nr_base.CommonNeutronBaseTestCase,
|
||||
super(AIMBaseTestCase, self).setUp(
|
||||
policy_drivers=policy_drivers, core_plugin=core_plugin,
|
||||
ml2_options=ml2_opts, l3_plugin=l3_plugin,
|
||||
sc_plugin=sc_plugin, qos_plugin=qos_plugin)
|
||||
sc_plugin=sc_plugin, qos_plugin=qos_plugin,
|
||||
trunk_plugin=trunk_plugin)
|
||||
self.db_session = db_api.get_writer_session()
|
||||
self.initialize_db_config(self.db_session)
|
||||
self.l3_plugin = directory.get_plugin(n_constants.L3)
|
||||
@ -5215,3 +5217,59 @@ class TestPerL3PImplicitContractsConfig(TestL2PolicyWithAutoPTG):
|
||||
|
||||
def test_create_not_called(self):
|
||||
self.mock_create.assert_not_called()
|
||||
|
||||
|
||||
class TestVlanAwareVM(AIMBaseTestCase):
|
||||
|
||||
def setUp(self, *args, **kwargs):
|
||||
super(TestVlanAwareVM, self).setUp(trunk_plugin='trunk', **kwargs)
|
||||
|
||||
def _do_test_gbp_details_no_pt(self):
|
||||
network = self._make_network(self.fmt, 'net1', True)
|
||||
with self.subnet(network=network, cidr='1.1.2.0/24') as subnet:
|
||||
with self.port(subnet=subnet) as port:
|
||||
with self.port(subnet=subnet) as subp:
|
||||
port_id = port['port']['id']
|
||||
subp_id = subp['port']['id']
|
||||
trunk = self._create_resource('trunk', port_id=port_id)
|
||||
self.driver._trunk_plugin.add_subports(
|
||||
nctx.get_admin_context(), trunk['trunk']['id'],
|
||||
{'sub_ports': [{'port_id': subp_id,
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': 100}]})
|
||||
self._bind_port_to_host(port_id, 'h1')
|
||||
req_mapping = self.driver.request_endpoint_details(
|
||||
nctx.get_admin_context(),
|
||||
request={'device': 'tap%s' % port_id,
|
||||
'timestamp': 0, 'request_id': 'request_id'},
|
||||
host='h1')
|
||||
self.assertEqual(req_mapping['trunk_details']['trunk_id'],
|
||||
trunk['trunk']['id'])
|
||||
self.assertEqual(
|
||||
req_mapping['trunk_details']['master_port_id'],
|
||||
trunk['trunk']['port_id'])
|
||||
self.assertEqual(
|
||||
req_mapping['trunk_details']['subports'],
|
||||
[{'port_id': subp_id,
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': 100}])
|
||||
# Retrieve the subport
|
||||
self._bind_port_to_host(subp_id, 'h1')
|
||||
req_mapping = self.driver.request_endpoint_details(
|
||||
nctx.get_admin_context(),
|
||||
request={'device': 'tap%s' % subp_id,
|
||||
'timestamp': 0, 'request_id': 'request_id'},
|
||||
host='h1')
|
||||
self.assertEqual(req_mapping['trunk_details']['trunk_id'],
|
||||
trunk['trunk']['id'])
|
||||
self.assertEqual(
|
||||
req_mapping['trunk_details']['master_port_id'],
|
||||
port_id)
|
||||
self.assertEqual(
|
||||
req_mapping['trunk_details']['subports'],
|
||||
[{'port_id': subp_id,
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': 100}])
|
||||
|
||||
def test_trunk_master_port(self):
|
||||
self._do_test_gbp_details_no_pt()
|
||||
|
@ -28,7 +28,7 @@ class ExtensionDriverTestBase(test_plugin.GroupPolicyPluginTestCase):
|
||||
|
||||
def setUp(self, policy_drivers=None, core_plugin=None,
|
||||
l3_plugin=None, ml2_options=None,
|
||||
sc_plugin=None, qos_plugin=None):
|
||||
sc_plugin=None, qos_plugin=None, trunk_plugin=None):
|
||||
config.cfg.CONF.set_override('extension_drivers',
|
||||
self._extension_drivers,
|
||||
group='group_policy')
|
||||
@ -38,7 +38,7 @@ class ExtensionDriverTestBase(test_plugin.GroupPolicyPluginTestCase):
|
||||
super(ExtensionDriverTestBase, self).setUp(
|
||||
core_plugin=core_plugin, l3_plugin=l3_plugin,
|
||||
ml2_options=ml2_options, sc_plugin=sc_plugin,
|
||||
qos_plugin=qos_plugin)
|
||||
qos_plugin=qos_plugin, trunk_plugin=trunk_plugin)
|
||||
|
||||
|
||||
class ExtensionDriverTestCase(ExtensionDriverTestBase):
|
||||
|
@ -63,7 +63,8 @@ def get_status_for_test(self, context):
|
||||
class GroupPolicyPluginTestBase(tgpmdb.GroupPolicyMappingDbTestCase):
|
||||
|
||||
def setUp(self, core_plugin=None, l3_plugin=None, gp_plugin=None,
|
||||
ml2_options=None, sc_plugin=None, qos_plugin=None):
|
||||
ml2_options=None, sc_plugin=None, qos_plugin=None,
|
||||
trunk_plugin=None):
|
||||
if not gp_plugin:
|
||||
gp_plugin = GP_PLUGIN_KLASS
|
||||
ml2_opts = ml2_options or {'mechanism_drivers': ['openvswitch'],
|
||||
@ -75,7 +76,8 @@ class GroupPolicyPluginTestBase(tgpmdb.GroupPolicyMappingDbTestCase):
|
||||
l3_plugin=l3_plugin,
|
||||
gp_plugin=gp_plugin,
|
||||
sc_plugin=sc_plugin,
|
||||
qos_plugin=qos_plugin)
|
||||
qos_plugin=qos_plugin,
|
||||
trunk_plugin=trunk_plugin)
|
||||
|
||||
def _create_l2_policy_on_shared(self, **kwargs):
|
||||
l3p = self.create_l3_policy(shared=True)['l3_policy']
|
||||
|
@ -33,7 +33,8 @@ CORE_PLUGIN = ('gbpservice.neutron.tests.unit.services.grouppolicy.'
|
||||
class CommonNeutronBaseTestCase(test_plugin.GroupPolicyPluginTestBase):
|
||||
|
||||
def setUp(self, policy_drivers=None, core_plugin=None, l3_plugin=None,
|
||||
ml2_options=None, sc_plugin=None, qos_plugin=None):
|
||||
ml2_options=None, sc_plugin=None, qos_plugin=None,
|
||||
trunk_plugin=None):
|
||||
core_plugin = core_plugin or ML2PLUS_PLUGIN
|
||||
policy_drivers = policy_drivers or ['neutron_resources']
|
||||
config.cfg.CONF.set_override('policy_drivers',
|
||||
@ -49,7 +50,8 @@ class CommonNeutronBaseTestCase(test_plugin.GroupPolicyPluginTestBase):
|
||||
l3_plugin=l3_plugin,
|
||||
ml2_options=ml2_options,
|
||||
sc_plugin=sc_plugin,
|
||||
qos_plugin=qos_plugin)
|
||||
qos_plugin=qos_plugin,
|
||||
trunk_plugin=trunk_plugin)
|
||||
res = mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.'
|
||||
'_check_router_needs_rescheduling').start()
|
||||
res.return_value = None
|
||||
|
Loading…
Reference in New Issue
Block a user