Moving VLAN Transparency support from core to extension

* 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
This commit is contained in:
Pritesh Kothari 2015-03-25 11:34:05 -07:00
parent bc66a04ae4
commit 809e434d2d
14 changed files with 245 additions and 29 deletions

View 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,

View 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)

View File

@ -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'])

View File

@ -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 {}

View 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.")

View 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

View 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

View 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):

View 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

View 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)

View 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",

View 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'}}

View 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)

View File

@ -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])