'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,
'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(
'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)
--- /dev/null
+# 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'])
--- /dev/null
+# 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 {}
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.")
def get_mappings(self, agent):
return agent['configurations'].get('interface_mappings', {})
+
+ def check_vlan_transparency(self, context):
+ """Linuxbridge driver vlan transparency support."""
+ return True
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
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
"""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):
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
sg_db_rpc.SecurityGroupServerRpcMixin,
agentschedulers_db.DhcpAgentSchedulerDbMixin,
addr_pair_db.AllowedAddressPairsMixin,
+ vlantransparent_db.Vlantransparent_db_mixin,
extradhcpopt_db.ExtraDhcpOptMixin,
netmtu_db.Netmtu_db_mixin):
"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):
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
# 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)
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'])
# 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'])
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",
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'}}
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)
--- /dev/null
+# 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])