From e20ef3fa86cd61c20559441fb47eba62515885f2 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Mon, 9 Dec 2024 15:13:45 +0100 Subject: [PATCH] QinQ API extension implementation in the Neutron server This patch implements 'qinq' API extension in the Neutron server. It means that this new attribute is now added for the "vlan" networks to the network dict and returned through the API. This patch also adds validation that both "vlan_transparent" and "qinq" aren't enabled for the same network at the same time as this is not supported. This patch adds all the necessary bits to store value of the new attribute in the Neutron DB and to add support for it to the Network OVO. Finally it also adds check_vlan_qinq() method to all mechanism drivers which are in-tree. For now all of them declare that QinQ vlans are not supported. Related-Bug: #1915151 Change-Id: I427edfd580eb06aa4f6904f90ff28cf8b5267397 --- neutron/conf/common.py | 4 + ...5c_add_vlan_qinq_column_to_the_network_.py | 35 +++++ .../alembic_migrations/versions/EXPAND_HEAD | 2 +- neutron/db/models_v2.py | 1 + neutron/db/qinq_db.py | 27 ++++ neutron/extensions/qinq.py | 48 +++++++ neutron/objects/network.py | 6 +- .../plugins/ml2/drivers/l2pop/mech_driver.py | 4 + .../macvtap/mech_driver/mech_macvtap.py | 4 + .../mech_sriov/mech_driver/mech_driver.py | 4 + .../mech_driver/mech_openvswitch.py | 4 + .../drivers/ovn/mech_driver/mech_driver.py | 4 + neutron/plugins/ml2/managers.py | 15 ++ neutron/plugins/ml2/plugin.py | 27 +++- neutron/tests/unit/extensions/test_qinq.py | 132 ++++++++++++++++++ neutron/tests/unit/objects/test_network.py | 5 + neutron/tests/unit/objects/test_objects.py | 2 +- .../plugins/ml2/drivers/mechanism_logger.py | 5 + 18 files changed, 322 insertions(+), 7 deletions(-) create mode 100644 neutron/db/migration/alembic_migrations/versions/2025.1/expand/ad80a9f07c5c_add_vlan_qinq_column_to_the_network_.py create mode 100644 neutron/db/qinq_db.py create mode 100644 neutron/extensions/qinq.py create mode 100644 neutron/tests/unit/extensions/test_qinq.py diff --git a/neutron/conf/common.py b/neutron/conf/common.py index bbe9d61b98f..be221bb27ec 100644 --- a/neutron/conf/common.py +++ b/neutron/conf/common.py @@ -128,6 +128,10 @@ core_opts = [ cfg.BoolOpt('vlan_transparent', default=False, help=_('If True, then allow plugins that support it to ' 'create VLAN transparent networks.')), + cfg.BoolOpt('vlan_qinq', default=False, + help=_('If True, then allow plugins that support it to ' + 'create VLAN transparent networks using 0x8a88 ' + 'ethertype.')), cfg.BoolOpt('filter_validation', default=True, help=_('If True, then allow plugins to decide ' 'whether to perform validations on filter parameters. ' diff --git a/neutron/db/migration/alembic_migrations/versions/2025.1/expand/ad80a9f07c5c_add_vlan_qinq_column_to_the_network_.py b/neutron/db/migration/alembic_migrations/versions/2025.1/expand/ad80a9f07c5c_add_vlan_qinq_column_to_the_network_.py new file mode 100644 index 00000000000..349c0aa7f2a --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/2025.1/expand/ad80a9f07c5c_add_vlan_qinq_column_to_the_network_.py @@ -0,0 +1,35 @@ +# Copyright 2024 OpenStack Foundation +# +# 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 alembic import op +import sqlalchemy as sa + + +# Add qinq column to the Network table +# +# Revision ID: ad80a9f07c5c +# Revises: 5bcb7b31ec7d +# Create Date: 2024-12-09 11:27:41.108660 + +# revision identifiers, used by Alembic. +revision = 'ad80a9f07c5c' +down_revision = '5bcb7b31ec7d' + + +def upgrade(): + op.add_column( + 'networks', + sa.Column('qinq', sa.Boolean(), server_default=None) + ) diff --git a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD index c8434b3c3ad..2665ab596fe 100644 --- a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD +++ b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD @@ -1 +1 @@ -5bcb7b31ec7d +ad80a9f07c5c diff --git a/neutron/db/models_v2.py b/neutron/db/models_v2.py index 943e566dc24..e9cdf6cee6a 100644 --- a/neutron/db/models_v2.py +++ b/neutron/db/models_v2.py @@ -321,6 +321,7 @@ class Network(standard_attr.HasStandardAttributes, model_base.BASEV2, status = sa.Column(sa.String(16)) admin_state_up = sa.Column(sa.Boolean) vlan_transparent = sa.Column(sa.Boolean, nullable=True) + qinq = sa.Column(sa.Boolean, nullable=True) rbac_entries = orm.relationship(rbac_db_models.NetworkRBAC, backref=orm.backref('network', load_on_pending=True), diff --git a/neutron/db/qinq_db.py b/neutron/db/qinq_db.py new file mode 100644 index 00000000000..4c0cb4c0904 --- /dev/null +++ b/neutron/db/qinq_db.py @@ -0,0 +1,27 @@ +# Copyright (c) 2024 Red Hat, Inc. +# +# 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 neutron_lib.api.definitions import network as net_def +from neutron_lib.api.definitions import qinq as qinq_def +from neutron_lib.db import resource_extend + + +@resource_extend.has_resource_extenders +class Vlanqinq_db_mixin: + """Mixin class to add vlan QinQ methods to db_base_plugin_v2.""" + + @staticmethod + @resource_extend.extends([net_def.COLLECTION_NAME]) + def _extend_network_dict_vlan_qinq(network_res, network_db): + network_res[qinq_def.QINQ_FIELD] = network_db.qinq + return network_res diff --git a/neutron/extensions/qinq.py b/neutron/extensions/qinq.py new file mode 100644 index 00000000000..0c7b5dd5418 --- /dev/null +++ b/neutron/extensions/qinq.py @@ -0,0 +1,48 @@ +# Copyright (c) 2024 Red Hat, Inc. +# +# 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 neutron_lib.api.definitions import qinq as apidef +from neutron_lib.api import extensions as api_extensions +from neutron_lib.api import validators +from oslo_config import cfg +from oslo_log import log as logging + +LOG = logging.getLogger(__name__) + + +def _disable_extension_by_config(aliases): + if not cfg.CONF.vlan_qinq: + if apidef.ALIAS in aliases: + aliases.remove(apidef.ALIAS) + LOG.info('Disabled VLAN QinQ extension.') + + +def get_qinq(network): + """Get the value of vlan_qinq from a network if set. + + :param network: The network dict to retrieve the value of vlan_qinq + from. + :returns: The value of vlan_qinq from the network dict if set in + the dict, otherwise False is returned. + """ + return (network[apidef.QINQ_FIELD] + if (apidef.QINQ_FIELD in network and + validators.is_attr_set(network[apidef.QINQ_FIELD])) + else False) + + +class Qinq(api_extensions.APIExtensionDescriptor): + """Extension class supporting vlan QinQ networks.""" + + api_definition = apidef diff --git a/neutron/objects/network.py b/neutron/objects/network.py index 0f2f5a6e6af..1e472a85aa8 100644 --- a/neutron/objects/network.py +++ b/neutron/objects/network.py @@ -200,7 +200,8 @@ class ExternalNetwork(base.NeutronDbObject): class Network(rbac_db.NeutronRbacObject): # Version 1.0: Initial version # Version 1.1: Changed 'mtu' to be not nullable - VERSION = '1.1' + # Version 1.2: Added 'qinq' field + VERSION = '1.2' rbac_db_cls = NetworkRBAC db_model = models_v2.Network @@ -212,6 +213,7 @@ class Network(rbac_db.NeutronRbacObject): 'status': obj_fields.StringField(nullable=True), 'admin_state_up': obj_fields.BooleanField(nullable=True), 'vlan_transparent': obj_fields.BooleanField(nullable=True), + 'qinq': obj_fields.BooleanField(nullable=True), # TODO(ihrachys): consider converting to a field of stricter type 'availability_zone_hints': obj_fields.ListOfStringsField( nullable=True), @@ -337,6 +339,8 @@ class Network(rbac_db.NeutronRbacObject): # mtu will not be nullable after raise exception.IncompatibleObjectVersion( objver=target_version, objname=self.__class__.__name__) + if _target_version < (1, 2): + primitive.pop('qinq', None) @base.NeutronObjectRegistry.register diff --git a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py index 5447cf60d10..262e960ef28 100644 --- a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py +++ b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py @@ -60,6 +60,10 @@ class L2populationMechanismDriver(api.MechanismDriver): """L2population driver vlan transparency support.""" return True + def check_vlan_qinq(self, context): + """L2population driver doesn't support vlan transparency.""" + return False + def _get_ha_port_agents_fdb( self, context, network_id, router_id): other_fdb_ports = {} diff --git a/neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py b/neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py index 88c160f9ff5..f52ef12e048 100644 --- a/neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py +++ b/neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py @@ -60,6 +60,10 @@ class MacvtapMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Macvtap driver vlan transparency support.""" return False + def check_vlan_qinq(self, context): + """Currently Macvtap driver doesn't support QinQ vlan.""" + return False + def _is_live_migration(self, context): # We cannot just check if # context.original['host_id'] != context.current['host_id'] diff --git a/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py b/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py index ae6bd2db8e3..ca471b35c04 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py @@ -201,6 +201,10 @@ class SriovNicSwitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """SR-IOV driver vlan transparency support.""" return True + def check_vlan_qinq(self, context): + """Currently SR-IOV driver doesn't support QinQ vlan.""" + return False + def _get_vif_details(self, segment): network_type = segment[api.NETWORK_TYPE] if network_type == constants.TYPE_FLAT: diff --git a/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py b/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py index 91f9a318181..f5b1dab582a 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py +++ b/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py @@ -113,6 +113,10 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Currently Openvswitch driver doesn't support vlan transparency.""" return False + def check_vlan_qinq(self, context): + """Currently Openvswitch driver doesn't support QinQ vlan.""" + return False + def bind_port(self, context): vnic_type = context.current.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py index 36e1a4d2692..eac8d984f5b 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py @@ -225,6 +225,10 @@ class OVNMechanismDriver(api.MechanismDriver): return (context.current.get(provider_net.NETWORK_TYPE) in vlan_transparency_network_types) + def check_vlan_qinq(self, context): + """OVN driver vlan QinQ support.""" + return False + def _setup_vif_port_bindings(self): self.supported_vnic_types = ovn_const.OVN_SUPPORTED_VNIC_TYPES self.vif_details = { diff --git a/neutron/plugins/ml2/managers.py b/neutron/plugins/ml2/managers.py index 9d38cca16e2..0fd18256cd5 100644 --- a/neutron/plugins/ml2/managers.py +++ b/neutron/plugins/ml2/managers.py @@ -23,6 +23,7 @@ from neutron_lib.db import api as db_api from neutron_lib import exceptions as exc from neutron_lib.exceptions import multiprovidernet as mpnet_exc from neutron_lib.exceptions import placement as place_exc +from neutron_lib.exceptions import vlanqinq as qinq_exc from neutron_lib.exceptions import vlantransparent as vlan_exc from neutron_lib.plugins.ml2 import api from oslo_config import cfg @@ -469,6 +470,19 @@ class MechanismManager(stevedore.named.NamedExtensionManager): if not driver.obj.check_vlan_transparency(context): raise vlan_exc.VlanTransparencyDriverError() + def _check_vlan_qinq(self, context): + """Helper method for checking vlan qinq support. + + :param context: context parameter to pass to each method call + :raises: neutron_lib.exceptions.qinq. + VlanQinqDriverError if any mechanism driver doesn't + support vlan transparency. + """ + if context.current.get('qinq'): + for driver in self.ordered_mech_drivers: + if not driver.obj.check_vlan_qinq(context): + raise qinq_exc.VlanQinqDriverError() + def start_driver_rpc_listeners(self): servers = [] for driver in self.ordered_mech_drivers: @@ -529,6 +543,7 @@ class MechanismManager(stevedore.named.NamedExtensionManager): that all mechanism drivers are called in this case. """ self._check_vlan_transparency(context) + self._check_vlan_qinq(context) self._call_on_drivers("create_network_precommit", context, raise_db_retriable=True) diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index a18b3c66f1b..90b7692114e 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -52,6 +52,7 @@ from neutron_lib.api.definitions import port_security as psec from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings_extended as pbe_ext from neutron_lib.api.definitions import provider_net +from neutron_lib.api.definitions import qinq as qinq_apidef from neutron_lib.api.definitions import quota_check_limit from neutron_lib.api.definitions import rbac_address_groups as rbac_ag_apidef from neutron_lib.api.definitions import rbac_address_scope @@ -128,12 +129,14 @@ from neutron.db import extradhcpopt_db from neutron.db.models import securitygroup as sg_models from neutron.db import models_v2 from neutron.db import provisioning_blocks +from neutron.db import qinq_db from neutron.db import securitygroups_rpc_base as sg_db_rpc from neutron.db import segments_db from neutron.db import subnet_service_type_mixin from neutron.db import vlantransparent_db from neutron.extensions import dhcpagentscheduler as dhcp_ext from neutron.extensions import filter_validation +from neutron.extensions import qinq from neutron.extensions import quota_check_limit_default from neutron.extensions import security_groups_default_rules as \ sg_default_rules_ext @@ -186,7 +189,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, extradhcpopt_db.ExtraDhcpOptMixin, address_scope_db.AddressScopeDbMixin, subnet_service_type_mixin.SubnetServiceTypeMixin, - address_group_db.AddressGroupDbMixin): + address_group_db.AddressGroupDbMixin, + qinq_db.Vlanqinq_db_mixin): """Implement the Neutron L2 abstractions using modules. @@ -253,6 +257,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, sg_default_rules_ext.ALIAS, sg_rules_default_sg.ALIAS, subnet_ext_net_def.ALIAS, + qinq_apidef.ALIAS, ] # List of agent types for which all binding_failed ports should try to be @@ -268,6 +273,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, vlantransparent._disable_extension_by_config(aliases) filter_validation._disable_extension_by_config(aliases) dhcp_ext.disable_extension_by_config(aliases) + qinq._disable_extension_by_config(aliases) self._aliases = self._filter_extensions_by_mech_driver(aliases) return self._aliases @@ -1212,10 +1218,23 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, self.type_manager.extend_network_dict_provider(context, result) # Update the transparent vlan if configured + is_vlan_transparent = None if extensions.is_extension_supported(self, 'vlan-transparent'): - vlt = vlan_apidef.get_vlan_transparent(net_data) - net_db['vlan_transparent'] = vlt - result['vlan_transparent'] = vlt + is_vlan_transparent = vlan_apidef.get_vlan_transparent( + net_data) + net_db['vlan_transparent'] = is_vlan_transparent + result['vlan_transparent'] = is_vlan_transparent + # Update the vlan QinQ if configured + qinq_value = None + if extensions.is_extension_supported(self, qinq_apidef.ALIAS): + qinq_value = qinq.get_qinq(net_data) + net_db['qinq'] = qinq_value + result['qinq'] = qinq_value + # QinQ and vlan_transparent can't be both set to True + if is_vlan_transparent and qinq_value: + msg = _("Attributes 'vlan_transparent' and 'qinq' can not be " + "set to True for the same network.") + raise exc.BadRequest(resource='network', msg=msg) az_hints = utils.get_az_hints(net_data) if az_hints: self.validate_availability_zones(context, 'network', az_hints) diff --git a/neutron/tests/unit/extensions/test_qinq.py b/neutron/tests/unit/extensions/test_qinq.py new file mode 100644 index 00000000000..753710d1816 --- /dev/null +++ b/neutron/tests/unit/extensions/test_qinq.py @@ -0,0 +1,132 @@ +# Copyright (c) 2024 Red Hat, Inc. +# +# 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 neutron_lib.api.definitions import provider_net +from neutron_lib.api.definitions import qinq as qinq_apidef +from neutron_lib.api.definitions import vlantransparent as vlan_apidef +from oslo_config import cfg +from webob import exc as web_exc + +from neutron.db import qinq_db +from neutron.plugins.ml2 import plugin as ml2_plugin +from neutron.tests.common import test_db_base_plugin_v2 +from neutron.tests.unit import testlib_api + + +class QinqExtensionTestPlugin(ml2_plugin.Ml2Plugin, + qinq_db.Vlanqinq_db_mixin): + """Test plugin to mixin the VLAN transparent extensions.""" + + supported_extension_aliases = [provider_net.ALIAS, + qinq_apidef.ALIAS, + vlan_apidef.ALIAS] + + +class QinqExtensionTestCase(test_db_base_plugin_v2.TestNetworksV2): + fmt = 'json' + + def setUp(self): + plugin = ('neutron.tests.unit.extensions.test_qinq.' + 'QinqExtensionTestPlugin') + + cfg.CONF.set_override('network_vlan_ranges', 'datacentre', + group='ml2_type_vlan') + super().setUp(plugin=plugin) + + def test_create_network_with_qinq_attr(self): + arg_list = ( + qinq_apidef.QINQ_FIELD), + net_kwargs = { + qinq_apidef.QINQ_FIELD: True + } + with self.network(name='net1', as_admin=True, + arg_list=arg_list, **net_kwargs) as net: + req = self.new_show_request('networks', net['network']['id']) + res = self.deserialize(self.fmt, req.get_response(self.api)) + self.assertEqual(net['network']['name'], + res['network']['name']) + self.assertTrue(res['network'][qinq_apidef.QINQ_FIELD]) + + def test_create_network_with_bad_qinq_attr(self): + arg_list = ( + qinq_apidef.QINQ_FIELD), + net_kwargs = { + qinq_apidef.QINQ_FIELD: 'this is not boolean value', + } + with testlib_api.ExpectedException( + web_exc.HTTPClientError) as ctx_manager: + with self.network(name='net1', as_admin=True, + arg_list=arg_list, **net_kwargs): + pass + self.assertEqual(web_exc.HTTPClientError.code, + ctx_manager.exception.code) + + def test_network_update_with_qinq_exception(self): + arg_list = ( + qinq_apidef.QINQ_FIELD), + net_kwargs = { + qinq_apidef.QINQ_FIELD: False, + } + with self.network(name='net1', as_admin=True, + arg_list=arg_list, **net_kwargs) as net: + self._update('networks', net['network']['id'], + {'network': {qinq_apidef.QINQ_FIELD: True}}, + web_exc.HTTPBadRequest.code) + req = self.new_show_request('networks', net['network']['id']) + res = self.deserialize(self.fmt, req.get_response(self.api)) + self.assertEqual(net['network']['name'], + res['network']['name']) + self.assertFalse(res['network'][qinq_apidef.QINQ_FIELD]) + + def _test_create_network_qinq_and_transparent_vlan(self, qinq_value, vlt): + arg_list = ( + qinq_apidef.QINQ_FIELD, + vlan_apidef.VLANTRANSPARENT) + net_kwargs = { + qinq_apidef.QINQ_FIELD: qinq_value, + vlan_apidef.VLANTRANSPARENT: vlt, + } + # Both vlan_transparent and qinq can't be set for the same network + if qinq_value and vlt: + with testlib_api.ExpectedException( + web_exc.HTTPClientError) as ctx_manager: + with self.network(name='net1', as_admin=True, + arg_list=arg_list, **net_kwargs): + pass + self.assertEqual(web_exc.HTTPBadRequest.code, + ctx_manager.exception.code) + return + + # In any other case it should work fine + with self.network(name='net1', as_admin=True, + arg_list=arg_list, **net_kwargs) as net: + req = self.new_show_request('networks', net['network']['id']) + res = self.deserialize(self.fmt, req.get_response(self.api)) + self.assertEqual(net['network']['name'], + res['network']['name']) + self.assertEqual(qinq_value, + res['network'][qinq_apidef.QINQ_FIELD]) + self.assertEqual(vlt, res['network'][vlan_apidef.VLANTRANSPARENT]) + + def test_create_network_qinq_disabled_transparent_vlan_enabled(self): + self._test_create_network_qinq_and_transparent_vlan(False, True) + + def test_create_network_qinq_disabled_transparent_vlan_disabled(self): + self._test_create_network_qinq_and_transparent_vlan(False, False) + + def test_create_network_qinq_enabled_transparent_vlan_disabled(self): + self._test_create_network_qinq_and_transparent_vlan(True, False) + + def test_create_network_qinq_enabled_transparent_vlan_enabled(self): + self._test_create_network_qinq_and_transparent_vlan(True, True) diff --git a/neutron/tests/unit/objects/test_network.py b/neutron/tests/unit/objects/test_network.py index 865a417de26..d2a01c165e9 100644 --- a/neutron/tests/unit/objects/test_network.py +++ b/neutron/tests/unit/objects/test_network.py @@ -260,6 +260,11 @@ class NetworkDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual('bar.com', obj.dns_domain) + def test_v1_2_to_v1_1_drops_qinq_attribute(self): + network_obj = self._make_object(self.obj_fields[0]) + network_v1_1 = network_obj.obj_to_primitive(target_version='1.1') + self.assertNotIn('qinq', network_v1_1['versioned_object.data']) + class SegmentHostMappingIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py index 701263af174..6b392d590e7 100644 --- a/neutron/tests/unit/objects/test_objects.py +++ b/neutron/tests/unit/objects/test_objects.py @@ -65,7 +65,7 @@ object_data = { 'MeteringLabelRule': '2.0-0ad09894c62e1ce6e868f725158959ba', 'Log': '1.0-6391351c0f34ed34375a19202f361d24', 'NDPProxy': '1.0-a6597d9caac3bb0d63f943f82e4dda8c', - 'Network': '1.1-c3e9ecc0618ee934181d91b143a48901', + 'Network': '1.2-0221c921b40f11b237e6a274984f238a', 'NetworkDhcpAgentBinding': '1.1-d9443c88809ffa4c45a0a5a48134b54a', 'NetworkDNSDomain': '1.0-420db7910294608534c1e2e30d6d8319', 'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3', diff --git a/neutron/tests/unit/plugins/ml2/drivers/mechanism_logger.py b/neutron/tests/unit/plugins/ml2/drivers/mechanism_logger.py index 6ac6cf38ce0..ca08f2f1932 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mechanism_logger.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mechanism_logger.py @@ -96,6 +96,11 @@ class LoggerMechanismDriver(api.MechanismDriver): self._log_diff_call("check_vlan_transparency", context) return True + def check_vlan_qinq(self, context): + self._log_network_call("check_vlan_qinq", context) + self._log_diff_call("check_vlan_qinq", context) + return True + def _log_subnet_call(self, method_name, context): LOG.info("%(method)s called with subnet settings %(current)s " "(original settings %(original)s)",