]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Moving VLAN Transparency support from core to extension
authorPritesh Kothari <pritesh.kothari@cisco.com>
Wed, 25 Mar 2015 18:34:05 +0000 (11:34 -0700)
committerPritesh Kothari <pritesh.kothari@cisco.com>
Wed, 8 Apr 2015 15:35:13 +0000 (08:35 -0700)
* Moving VLAN Transparency support from core to extension
* Remove the older unit tests and add new corresponding ones

DocImpact
Closes-Bug: #1434667

Change-Id: Ic551475ed7b64aad9627a57abb0df41acc19bfc1

14 files changed:
neutron/api/v2/attributes.py
neutron/db/db_base_plugin_v2.py
neutron/db/vlantransparent_db.py [new file with mode: 0644]
neutron/extensions/vlantransparent.py [new file with mode: 0644]
neutron/plugins/ml2/common/exceptions.py
neutron/plugins/ml2/drivers/mech_linuxbridge.py
neutron/plugins/ml2/drivers/mech_openvswitch.py
neutron/plugins/ml2/managers.py
neutron/plugins/ml2/plugin.py
neutron/tests/retargetable/client_fixtures.py
neutron/tests/unit/api/v2/test_base.py
neutron/tests/unit/db/test_db_base_plugin_v2.py
neutron/tests/unit/extensions/test_providernet.py
neutron/tests/unit/extensions/test_vlantransparent.py [new file with mode: 0644]

index 357f8e7f9f08a32988d727bd12da78671b73825c..61f4d87a9ac68fc26bf272c1a2867b8eee5ed6b6 100644 (file)
@@ -707,9 +707,6 @@ RESOURCE_ATTRIBUTE_MAP = {
                       'validate': {'type:string': TENANT_ID_MAX_LEN},
                       'required_by_policy': True,
                       'is_visible': True},
-        'vlan_transparent': {'allow_post': True, 'allow_put': False,
-                             'convert_to': convert_to_boolean,
-                             'default': False, 'is_visible': True},
         SHARED: {'allow_post': True,
                  'allow_put': True,
                  'default': False,
index 6455e72b2b90d051daf31a37d22b6231d3d2a3c6..d700666ead72ed6c0fb53bc498dcfdae8e83244d 100644 (file)
@@ -844,9 +844,14 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
                'mtu': network.get('mtu', constants.DEFAULT_NETWORK_MTU),
                'status': network['status'],
                'shared': network['shared'],
-               'vlan_transparent': network['vlan_transparent'],
                'subnets': [subnet['id']
                            for subnet in network['subnets']]}
+        # TODO(pritesh): Move vlan_transparent to the extension module.
+        # vlan_transparent here is only added if the vlantransparent
+        # extension is enabled.
+        if ('vlan_transparent' in network and network['vlan_transparent'] !=
+            attributes.ATTR_NOT_SPECIFIED):
+            res['vlan_transparent'] = network['vlan_transparent']
         # Call auxiliary extend functions, if any
         if process_extensions:
             self._apply_dict_extend_functions(
@@ -951,8 +956,13 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
                     'admin_state_up': n['admin_state_up'],
                     'mtu': n.get('mtu', constants.DEFAULT_NETWORK_MTU),
                     'shared': n['shared'],
-                    'vlan_transparent': n.get('vlan_transparent', False),
                     'status': n.get('status', constants.NET_STATUS_ACTIVE)}
+            # TODO(pritesh): Move vlan_transparent to the extension module.
+            # vlan_transparent here is only added if the vlantransparent
+            # extension is enabled.
+            if ('vlan_transparent' in n and n['vlan_transparent'] !=
+                attributes.ATTR_NOT_SPECIFIED):
+                args['vlan_transparent'] = n['vlan_transparent']
             network = models_v2.Network(**args)
             context.session.add(network)
         return self._make_network_dict(network, process_extensions=False)
diff --git a/neutron/db/vlantransparent_db.py b/neutron/db/vlantransparent_db.py
new file mode 100644 (file)
index 0000000..ba024fe
--- /dev/null
@@ -0,0 +1,29 @@
+# Copyright (c) 2015 Cisco Systems, 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 neutron.api.v2 import attributes
+from neutron.db import db_base_plugin_v2
+from neutron.extensions import vlantransparent
+
+
+class Vlantransparent_db_mixin(object):
+    """Mixin class to add vlan transparent methods to db_base_plugin_v2."""
+
+    def _extend_network_dict_vlan_transparent(self, network_res, network_db):
+        network_res[vlantransparent.VLANTRANSPARENT] = (
+            network_db.vlan_transparent)
+        return network_res
+
+    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
+        attributes.NETWORKS, ['_extend_network_dict_vlan_transparent'])
diff --git a/neutron/extensions/vlantransparent.py b/neutron/extensions/vlantransparent.py
new file mode 100644 (file)
index 0000000..4c2d8f9
--- /dev/null
@@ -0,0 +1,75 @@
+# Copyright (c) 2015 Cisco Systems, 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 oslo_config import cfg
+from oslo_log import log as logging
+
+from neutron.api.v2 import attributes
+from neutron.common import exceptions as nexception
+from neutron.i18n import _LI
+
+LOG = logging.getLogger(__name__)
+
+
+class VlanTransparencyDriverError(nexception.NeutronException):
+    """Vlan Transparency not supported by all mechanism drivers."""
+    message = _("Backend does not support VLAN Transparency.")
+
+
+VLANTRANSPARENT = 'vlan_transparent'
+EXTENDED_ATTRIBUTES_2_0 = {
+    'networks': {
+        VLANTRANSPARENT: {'allow_post': True, 'allow_put': False,
+                          'convert_to': attributes.convert_to_boolean,
+                          'default': attributes.ATTR_NOT_SPECIFIED,
+                          'is_visible': True},
+    },
+}
+
+
+def disable_extension_by_config(aliases):
+    if not cfg.CONF.vlan_transparent:
+        if 'vlan-transparent' in aliases:
+            aliases.remove('vlan-transparent')
+        LOG.info(_LI('Disabled vlantransparent extension.'))
+
+
+class Vlantransparent(object):
+    """Extension class supporting vlan transparent networks."""
+
+    @classmethod
+    def get_name(cls):
+        return "Vlantransparent"
+
+    @classmethod
+    def get_alias(cls):
+        return "vlan-transparent"
+
+    @classmethod
+    def get_description(cls):
+        return "Provides Vlan Transparent Networks"
+
+    @classmethod
+    def get_namespace(cls):
+        return "http://docs.openstack.org/ext/vlantransparent/api/v1.0"
+
+    @classmethod
+    def get_updated(cls):
+        return "2015-03-23T09:00:00-00:00"
+
+    def get_extended_resources(self, version):
+        if version == "2.0":
+            return EXTENDED_ATTRIBUTES_2_0
+        else:
+            return {}
index 41f46944f6acf963889dda06aad4dd2a920424e5..ed94b1e1f14072d328d3daa0c24b804798d64652 100644 (file)
@@ -21,8 +21,3 @@ from neutron.common import exceptions
 class MechanismDriverError(exceptions.NeutronException):
     """Mechanism driver call failed."""
     message = _("%(method)s failed.")
-
-
-class VlanTransparencyError(exceptions.NeutronException):
-    """Vlan Transparency not supported by all mechanism drivers."""
-    message = _("Backend does not support VLAN Transparency.")
index 221b2abfab15f3752f3ec6cc82e0d5f61790fbe0..f69b5da4160061255c12fa90588fb21524407720 100644 (file)
@@ -48,3 +48,7 @@ class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
 
     def get_mappings(self, agent):
         return agent['configurations'].get('interface_mappings', {})
+
+    def check_vlan_transparency(self, context):
+        """Linuxbridge driver vlan transparency support."""
+        return True
index 668b3832ec74ba27bdb149fbe770589744e186f5..13128a246ba5b743b71c9319547a15d2b70aa349 100644 (file)
@@ -50,3 +50,7 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
 
     def get_mappings(self, agent):
         return agent['configurations'].get('bridge_mappings', {})
+
+    def check_vlan_transparency(self, context):
+        """Currently Openvswitch driver doesn't support vlan transparency."""
+        return False
index df932c66bb87504980a9f4be647c214892da5bb5..3d31e8d9c7c596fbdf58cb0809c17e985eb26045 100644 (file)
@@ -22,6 +22,7 @@ from neutron.common import exceptions as exc
 from neutron.extensions import multiprovidernet as mpnet
 from neutron.extensions import portbindings
 from neutron.extensions import providernet as provider
+from neutron.extensions import vlantransparent
 from neutron.i18n import _LE, _LI
 from neutron.plugins.ml2.common import exceptions as ml2_exc
 from neutron.plugins.ml2 import db
@@ -297,14 +298,17 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
         """Helper method for checking vlan transparecncy support.
 
         :param context: context parameter to pass to each method call
-        :raises: neutron.plugins.ml2.common.VlanTransparencyError
-        if any mechanism driver doesn't support vlan transparency.
+        :raises: neutron.extensions.vlantransparent.
+        VlanTransparencyDriverError if any mechanism driver doesn't
+        support vlan transparency.
         """
-        if not cfg.CONF.vlan_transparent:
+        if context.current['vlan_transparent'] is None:
             return
-        for driver in self.ordered_mech_drivers:
-            if driver.obj.check_vlan_transparency(context) is False:
-                raise ml2_exc.VlanTransparencyError()
+
+        if context.current['vlan_transparent']:
+            for driver in self.ordered_mech_drivers:
+                if not driver.obj.check_vlan_transparency(context):
+                    raise vlantransparent.VlanTransparencyDriverError()
 
     def _call_on_drivers(self, method_name, context,
                          continue_on_failure=False):
index 7866103ef58e9eb7c8b69602cd7dc3899616802f..66d989caea032b203580316543b63772aacce92e 100644 (file)
@@ -56,12 +56,14 @@ from neutron.db import models_v2
 from neutron.db import netmtu_db
 from neutron.db import quota_db  # noqa
 from neutron.db import securitygroups_rpc_base as sg_db_rpc
+from neutron.db import vlantransparent_db
 from neutron.extensions import allowedaddresspairs as addr_pair
 from neutron.extensions import extra_dhcp_opt as edo_ext
 from neutron.extensions import portbindings
 from neutron.extensions import portsecurity as psec
 from neutron.extensions import providernet as provider
 from neutron.extensions import securitygroup as ext_sg
+from neutron.extensions import vlantransparent
 from neutron.i18n import _LE, _LI, _LW
 from neutron import manager
 from neutron.openstack.common import uuidutils
@@ -90,6 +92,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
                 sg_db_rpc.SecurityGroupServerRpcMixin,
                 agentschedulers_db.DhcpAgentSchedulerDbMixin,
                 addr_pair_db.AllowedAddressPairsMixin,
+                vlantransparent_db.Vlantransparent_db_mixin,
                 extradhcpopt_db.ExtraDhcpOptMixin,
                 netmtu_db.Netmtu_db_mixin):
 
@@ -115,7 +118,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
                                     "dhcp_agent_scheduler",
                                     "multi-provider", "allowed-address-pairs",
                                     "extra_dhcp_opt", "subnet_allocation",
-                                    "net-mtu"]
+                                    "net-mtu", "vlan-transparent"]
 
     @property
     def supported_extension_aliases(self):
@@ -123,6 +126,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
             aliases = self._supported_extension_aliases[:]
             aliases += self.extension_manager.extension_aliases()
             sg_rpc.disable_security_group_extension_by_config(aliases)
+            vlantransparent.disable_extension_by_config(aliases)
             self._aliases = aliases
         return self._aliases
 
index 75d9091a0bb38fbeb4bed7975bbc696ff5c55c98..d284ed605cd2e8a1e2a0c125441f0daa75a8d919 100644 (file)
@@ -94,7 +94,6 @@ class PluginClientFixture(AbstractClientFixture):
         # Supply defaults that are expected to be set by the api
         # framwork
         kwargs.setdefault('admin_state_up', True)
-        kwargs.setdefault('vlan_transparent', False)
         kwargs.setdefault('shared', False)
         data = dict(network=kwargs)
         result = self.plugin.create_network(self.ctx, data)
index a12eaec7aa54d8e7731fca102a2d98e8b85f7745..6630781fe11dc1a5beacae74a14323071ef32ee2 100644 (file)
@@ -766,7 +766,6 @@ class JSONV2TestCase(APIv2TestBase, testlib_api.WebTestCase):
         net_id = _uuid()
         initial_input = {'network': {'name': 'net1', 'tenant_id': _uuid()}}
         full_input = {'network': {'admin_state_up': True,
-                                  'vlan_transparent': False,
                                   'shared': False}}
         full_input['network'].update(initial_input['network'])
 
@@ -801,7 +800,6 @@ class JSONV2TestCase(APIv2TestBase, testlib_api.WebTestCase):
         # tenant_id should be fetched from env
         initial_input = {'network': {'name': 'net1'}}
         full_input = {'network': {'admin_state_up': True,
-                      'vlan_transparent': False,
                       'shared': False, 'tenant_id': tenant_id}}
         full_input['network'].update(initial_input['network'])
 
@@ -1421,8 +1419,7 @@ class ExtensionTestCase(base.BaseTestCase):
         net_id = _uuid()
         initial_input = {'network': {'name': 'net1', 'tenant_id': _uuid(),
                                      'v2attrs:something_else': "abc"}}
-        data = {'network': {'admin_state_up': True, 'shared': False,
-                            'vlan_transparent': False}}
+        data = {'network': {'admin_state_up': True, 'shared': False}}
         data['network'].update(initial_input['network'])
 
         return_value = {'subnets': [], 'status': "ACTIVE",
index 5db1570ef7cada9fe38a1cb3748090d0d7062478..152209c3872c887ac31c524ecc29a8a2670a08d4 100644 (file)
@@ -2242,12 +2242,6 @@ class TestNetworksV2(NeutronDbPluginV2TestCase):
         self.assertEqual(ctx_manager.exception.code,
                          webob.exc.HTTPForbidden.code)
 
-    def test_create_network_vlan_transparent(self):
-        name = "vlan_transparent"
-        cfg.CONF.set_override('vlan_transparent', True)
-        with self.network(name=name, vlan_transparent=True) as net:
-            self.assertEqual(net['network']['vlan_transparent'], True)
-
     def test_update_network(self):
         with self.network() as network:
             data = {'network': {'name': 'a_brand_new_name'}}
index b55eb44d95d45927e8cfdad2eed2e7a0f800282c..a1645e21feffecf9afff5a8e6035aae3575e816d 100644 (file)
@@ -139,7 +139,6 @@ class ProvidernetExtensionTestCase(testlib_api.WebTestCase):
         exp_input = {'network': data}
         exp_input['network'].update({'admin_state_up': True,
                                      'tenant_id': 'an_admin',
-                                     'vlan_transparent': False,
                                      'shared': False})
         instance.create_network.assert_called_with(mock.ANY,
                                                    network=exp_input)
diff --git a/neutron/tests/unit/extensions/test_vlantransparent.py b/neutron/tests/unit/extensions/test_vlantransparent.py
new file mode 100644 (file)
index 0000000..38ffbf6
--- /dev/null
@@ -0,0 +1,105 @@
+# Copyright (c) 2015 Cisco Systems 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 oslo_config import cfg
+from webob import exc as web_exc
+
+from neutron.api.v2 import attributes
+from neutron.db import db_base_plugin_v2
+from neutron.db import vlantransparent_db as vlt_db
+from neutron.extensions import vlantransparent as vlt
+from neutron import quota
+from neutron.tests.unit.db import test_db_base_plugin_v2
+from neutron.tests.unit import testlib_api
+
+
+class VlanTransparentExtensionManager(object):
+
+    def get_resources(self):
+        return []
+
+    def get_actions(self):
+        return []
+
+    def get_request_extensions(self):
+        return []
+
+    def get_extended_resources(self, version):
+        return vlt.get_extended_resources(version)
+
+
+class VlanTransparentExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
+                                         vlt_db.Vlantransparent_db_mixin):
+    """Test plugin to mixin the VLAN transparent extensions."""
+
+    supported_extension_aliases = ["vlan-transparent"]
+
+
+class VlanTransparentExtensionTestCase(test_db_base_plugin_v2.TestNetworksV2):
+    fmt = 'json'
+
+    def setUp(self):
+        plugin = ('neutron.tests.unit.extensions.test_vlantransparent.'
+                  'VlanTransparentExtensionTestPlugin')
+
+        # Save the global RESOURCE_ATTRIBUTE_MAP
+        self.saved_attr_map = {}
+        for resource, attrs in attributes.RESOURCE_ATTRIBUTE_MAP.iteritems():
+            self.saved_attr_map[resource] = attrs.copy()
+
+        # Update the plugin and extensions path
+        self.setup_coreplugin(plugin)
+        cfg.CONF.set_override('allow_pagination', True)
+        cfg.CONF.set_override('allow_sorting', True)
+        ext_mgr = VlanTransparentExtensionManager()
+        self.addCleanup(self._restore_attribute_map)
+        super(VlanTransparentExtensionTestCase, self).setUp(plugin=plugin,
+                                                            ext_mgr=ext_mgr)
+
+        quota.QUOTAS._driver = None
+        cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver',
+                              group='QUOTAS')
+
+    def _restore_attribute_map(self):
+        # Restore the global RESOURCE_ATTRIBUTE_MAP
+        attributes.RESOURCE_ATTRIBUTE_MAP = self.saved_attr_map
+
+    def test_network_create_with_vlan_transparent_attr(self):
+        vlantrans = {'vlan_transparent': True}
+        with self.network(name='net1', **vlantrans) 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(True, res['network'][vlt.VLANTRANSPARENT])
+
+    def test_network_create_with_bad_vlan_transparent_attr(self):
+        vlantrans = {'vlan_transparent': "abc"}
+        with testlib_api.ExpectedException(
+                web_exc.HTTPClientError) as ctx_manager:
+            with self.network(name='net1', **vlantrans):
+                pass
+        self.assertEqual(web_exc.HTTPClientError.code,
+                         ctx_manager.exception.code)
+
+    def test_network_update_with_vlan_transparent_exception(self):
+        with self.network(name='net1') as net:
+            self._update('networks', net['network']['id'],
+                         {'network': {vlt.VLANTRANSPARENT: False}},
+                         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.assertEqual(None, res['network'][vlt.VLANTRANSPARENT])