diff --git a/vmware_nsx/common/nsxv_constants.py b/vmware_nsx/common/nsxv_constants.py index efa4ed8666..9243cdd2bd 100644 --- a/vmware_nsx/common/nsxv_constants.py +++ b/vmware_nsx/common/nsxv_constants.py @@ -20,6 +20,9 @@ XLARGE = 'xlarge' QUADLARGE = 'quadlarge' +SHARED = "shared" +EXCLUSIVE = "exclusive" + # Edge type SERVICE_EDGE = 'service' VDR_EDGE = 'vdr' diff --git a/vmware_nsx/extensions/routersize.py b/vmware_nsx/extensions/routersize.py new file mode 100644 index 0000000000..1e29f4aece --- /dev/null +++ b/vmware_nsx/extensions/routersize.py @@ -0,0 +1,56 @@ +# Copyright 2015 VMware, 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 import extensions +from neutron.api.v2 import attributes + + +ROUTER_SIZE = 'router_size' +EXTENDED_ATTRIBUTES_2_0 = { + 'routers': { + ROUTER_SIZE: {'allow_post': True, 'allow_put': False, + 'validate': {'type:values': ['compact', 'large', + 'xlarge', 'quadlarge']}, + 'default': attributes.ATTR_NOT_SPECIFIED, + 'is_visible': True}, + } +} + + +class Routersize(extensions.ExtensionDescriptor): + """Extension class supporting router size.""" + + @classmethod + def get_name(cls): + return "Router Size" + + @classmethod + def get_alias(cls): + return "nsxv-router-size" + + @classmethod + def get_description(cls): + return "Enables configuration of NSXv Edge Size" + + @classmethod + def get_updated(cls): + return "2015-9-22T10:00:00-00:00" + + def get_required_extensions(self): + return ["router"] + + def get_extended_resources(self, version): + if version == "2.0": + return EXTENDED_ATTRIBUTES_2_0 + return {} diff --git a/vmware_nsx/plugins/nsx_v/drivers/abstract_router_driver.py b/vmware_nsx/plugins/nsx_v/drivers/abstract_router_driver.py index cc8abf7fbb..ec761434ef 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/abstract_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/abstract_router_driver.py @@ -26,7 +26,8 @@ class RouterAbstractDriver(object): pass @abc.abstractmethod - def create_router(self, context, lrouter): + def create_router(self, context, lrouter, appliance_size=None, + allow_metadata=True): pass @abc.abstractmethod diff --git a/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py b/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py index 3a64b93549..b0d055a614 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py @@ -86,7 +86,8 @@ class RouterDistributedDriver(router_driver.RouterBaseDriver): router_id, routes, newnexthop, gateway_vnic_index=internal_vnic_index) - def create_router(self, context, lrouter, allow_metadata=True): + def create_router(self, context, lrouter, appliance_size=None, + allow_metadata=True): self.edge_manager.create_lrouter(context, lrouter, dist=True) def update_router(self, context, router_id, router): diff --git a/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py b/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py index f230998f5f..ef42f2400b 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py @@ -32,8 +32,10 @@ class RouterExclusiveDriver(router_driver.RouterBaseDriver): def get_type(self): return "exclusive" - def create_router(self, context, lrouter, allow_metadata=True): - self.edge_manager.create_lrouter(context, lrouter, dist=False) + def create_router(self, context, lrouter, appliance_size=None, + allow_metadata=True): + self.edge_manager.create_lrouter( + context, lrouter, dist=False, appliance_size=appliance_size) if allow_metadata: self.plugin.metadata_proxy_handler.configure_router_edge( lrouter['id']) diff --git a/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py b/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py index 9de5463b52..9ebadf0c53 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py @@ -41,7 +41,8 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): def get_type(self): return "shared" - def create_router(self, context, lrouter, allow_metadata=True): + def create_router(self, context, lrouter, + appliance_size=None, allow_metadata=True): pass def update_router(self, context, router_id, router): diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index ea0c7c8945..460ebcbab8 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -55,6 +55,7 @@ from vmware_nsx.common import config # noqa from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import locking from vmware_nsx.common import nsx_constants +from vmware_nsx.common import nsxv_constants from vmware_nsx.common import utils as c_utils from vmware_nsx.db import ( routertype as rt_rtr) @@ -65,6 +66,7 @@ from vmware_nsx.extensions import ( advancedserviceproviders as as_providers) from vmware_nsx.extensions import ( vnicindex as ext_vnic_idx) +from vmware_nsx.extensions import routersize from vmware_nsx.plugins.nsx_v import managers from vmware_nsx.plugins.nsx_v import md_proxy as nsx_v_md_proxy from vmware_nsx.plugins.nsx_v.vshield.common import ( @@ -77,6 +79,7 @@ from vmware_nsx.plugins.nsx_v.vshield import vcns_driver LOG = logging.getLogger(__name__) PORTGROUP_PREFIX = 'dvportgroup' +ROUTER_SIZE = routersize.ROUTER_SIZE class NsxVPluginV2(agents_db.AgentDbMixin, @@ -103,6 +106,7 @@ class NsxVPluginV2(agents_db.AgentDbMixin, "router", "security-group", "nsxv-router-type", + "nsxv-router-size", "vnic-index", "advanced-service-providers"] @@ -1347,23 +1351,47 @@ class NsxVPluginV2(agents_db.AgentDbMixin, raise n_exc.BadRequest(resource='router', msg=msg) return gw_info + def _validate_router_size(self, router): + # Check if router-size is specified. router-size can only be specified + # for a exclusive non-distributed router; else raise a BadRequest + # exception. + r = router['router'] + if r.get(ROUTER_SIZE) != attr.ATTR_NOT_SPECIFIED: + if r.get('router_type') == nsxv_constants.SHARED: + msg = _("Cannot specify router-size for shared router") + raise n_exc.BadRequest(resource="router", msg=msg) + elif r.get('distributed') is True: + msg = _("Cannot specify router-size for distributed router") + raise n_exc.BadRequest(resource="router", msg=msg) + elif r.get(ROUTER_SIZE) == attr.ATTR_NOT_SPECIFIED: + if r.get('router_type') == nsxv_constants.EXCLUSIVE: + r[ROUTER_SIZE] = nsxv_constants.COMPACT + def create_router(self, context, router, allow_metadata=True): + self._validate_router_size(router) + r = router['router'] + self._decide_router_type(context, r) # First extract the gateway info in case of updating # gateway before edge is deployed. # TODO(berlin): admin_state_up and routes update - r = router['router'] gw_info = self._extract_external_gw(context, router) - self._decide_router_type(context, r) lrouter = super(NsxVPluginV2, self).create_router(context, router) with context.session.begin(subtransactions=True): router_db = self._get_router(context, lrouter['id']) self._process_nsx_router_create(context, router_db, r) try: router_driver = self._get_router_driver(context, router_db) - router_driver.create_router( - context, lrouter, - allow_metadata=(allow_metadata and - self.metadata_proxy_handler)) + if router_driver.get_type() == nsxv_constants.EXCLUSIVE: + router_driver.create_router( + context, lrouter, + appliance_size=r.get(ROUTER_SIZE), + allow_metadata=(allow_metadata and + self.metadata_proxy_handler)) + else: + router_driver.create_router( + context, lrouter, + allow_metadata=(allow_metadata and + self.metadata_proxy_handler)) if gw_info != attr.ATTR_NOT_SPECIFIED: router_driver._update_router_gw_info( context, lrouter['id'], gw_info) @@ -1413,6 +1441,10 @@ class NsxVPluginV2(agents_db.AgentDbMixin, router = super(NsxVPluginV2, self).get_router(context, id, fields) if router.get("distributed") and 'router_type' in router: del router['router_type'] + if router.get("router_type") == nsxv_constants.EXCLUSIVE: + binding = nsxv_db.get_nsxv_router_binding(context.session, + router.get("id")) + router[ROUTER_SIZE] = binding.get("appliance_size") return router def _get_external_attachment_info(self, context, router): diff --git a/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py b/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py index 78550f2685..49d7584c8a 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py +++ b/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py @@ -641,12 +641,14 @@ class EdgeManager(object): router_id = (vcns_const.DHCP_EDGE_PREFIX + network_id)[:36] self._free_edge_appliance(context, router_id) - def create_lrouter(self, context, lrouter, lswitch=None, dist=False): + def create_lrouter( + self, context, lrouter, lswitch=None, dist=False, + appliance_size=vcns_const.SERVICE_SIZE_MAPPING['router']): """Create an edge for logical router support.""" router_name = lrouter['name'] + '-' + lrouter['id'] self._allocate_edge_appliance( context, lrouter['id'], router_name, - appliance_size=vcns_const.SERVICE_SIZE_MAPPING['router'], + appliance_size=appliance_size, dist=dist) def delete_lrouter(self, context, router_id, dist=False): diff --git a/vmware_nsx/tests/unit/vmware/test_nsx_v_plugin.py b/vmware_nsx/tests/unit/vmware/test_nsx_v_plugin.py index afe36adaab..cf2c6ff4e1 100644 --- a/vmware_nsx/tests/unit/vmware/test_nsx_v_plugin.py +++ b/vmware_nsx/tests/unit/vmware/test_nsx_v_plugin.py @@ -47,6 +47,8 @@ import webob.exc from vmware_nsx.common import nsx_constants from vmware_nsx.db import nsxv_db +from vmware_nsx.extensions import ( + routersize as router_size) from vmware_nsx.extensions import ( routertype as router_type) from vmware_nsx.extensions import ( @@ -1159,6 +1161,8 @@ class TestL3ExtensionManager(object): dist_router.EXTENDED_ATTRIBUTES_2_0.get(key, {})) l3.RESOURCE_ATTRIBUTE_MAP[key].update( router_type.EXTENDED_ATTRIBUTES_2_0.get(key, {})) + l3.RESOURCE_ATTRIBUTE_MAP[key].update( + router_size.EXTENDED_ATTRIBUTES_2_0.get(key, {})) # Finally add l3 resources to the global attribute map attributes.RESOURCE_ATTRIBUTE_MAP.update( l3.RESOURCE_ATTRIBUTE_MAP) @@ -1626,6 +1630,18 @@ class TestExclusiveRouterTestCase(L3NatTest, L3NatTestCaseBase, def test_router_create_with_gwinfo_and_l3_ext_net_with_vlan(self): self._test_router_create_with_gwinfo_and_l3_ext_net(444) + def test_router_create_with_different_sizes(self): + data = {'router': { + 'tenant_id': 'whatever', + 'name': 'test_router', + 'router_type': 'exclusive'}} + for size in ['compact', 'large', 'xlarge', 'quadlarge']: + data['router']['router_size'] = size + router_req = self.new_create_request('routers', data, self.fmt) + res = router_req.get_response(self.ext_api) + router = self.deserialize(self.fmt, res) + self.assertEqual(size, router['router']['router_size']) + def test_router_add_gateway_invalid_network_returns_404(self): # NOTE(salv-orlando): This unit test has been overriden # as the nsx plugin support the ext_gw_mode extension @@ -2298,6 +2314,19 @@ class TestSharedRouterTestCase(L3NatTest, L3NatTestCaseBase, self.plugin_instance.edge_manager.get_routers_on_same_edge( context.get_admin_context(), router['router']['id'])) + def test_router_create_with_size_fail_at_backend(self): + data = {'router': { + 'tenant_id': 'whatever', + 'router_type': 'shared', + 'router_size': 'large'}} + router_req = self.new_create_request('routers', data, self.fmt) + res = router_req.get_response(self.ext_api) + router = self.deserialize(self.fmt, res) + msg = ('Bad router request: ' + 'Cannot specify router-size for shared router') + self.assertEqual("BadRequest", router['NeutronError']['type']) + self.assertEqual(msg, router['NeutronError']['message']) + def test_router_create_with_gwinfo_with_no_edge(self): with self._create_l3_ext_network() as net: with self.subnet(network=net, enable_dhcp=False) as s: