NsxV3: Router preparation for GW/intf/FIP support

The patch includes these parts:
1. add update resource with retry feature
2. add ext-gw-mode, extraroute extension support
3. add router interface basic implementation
4. add L3, FIP, ext-gw and extraroute unit tests
Change-Id: Ia5c28fd27d253c0938537f1c9a474d7d2f68d715
This commit is contained in:
linb 2015-08-06 09:20:26 +08:00
parent 7bb63f2f8b
commit 37c687cc4e
4 changed files with 502 additions and 62 deletions

View File

@ -23,12 +23,22 @@ from vmware_nsx.neutron.plugins.vmware.nsxlib.v3 import client
LOG = log.getLogger(__name__)
ROUTER_INTF_PORT_NAME = "Tier1-RouterDownLinkPort"
def get_edge_cluster(edge_cluster_uuid):
resource = "edge-clusters/%s" % edge_cluster_uuid
return client.get_resource(resource)
@utils.retry_upon_exception_nsxv3(nsx_exc.StaleRevision)
def update_resource_with_retry(resource, payload):
revised_payload = client.get_resource(resource)
for key_name in payload.keys():
revised_payload[key_name] = payload[key_name]
return client.update_resource(resource, revised_payload)
def create_logical_switch(display_name, transport_zone_id, tags,
replication_mode=nsx_constants.MTEP,
admin_state=True, vlan_id=None):
@ -145,8 +155,8 @@ def update_logical_port(lport_id, name=None, admin_state=None):
return client.update_resource(resource, lport)
def create_logical_router(display_name, tags, edge_cluster_uuid=None,
tier_0=False):
def create_logical_router(display_name, tags,
edge_cluster_uuid=None, tier_0=False):
# TODO(salv-orlando): If possible do not manage edge clusters in the main
# plugin logic.
router_type = (nsx_constants.ROUTER_TYPE_TIER0 if tier_0 else
@ -155,8 +165,6 @@ def create_logical_router(display_name, tags, edge_cluster_uuid=None,
body = {'display_name': display_name,
'router_type': router_type,
'tags': tags}
# TODO(salv-orlando): raise if tier_0 but no edge_cluster_uuid was
# specified
if edge_cluster_uuid:
body['edge_cluster_id'] = edge_cluster_uuid
return client.create_resource(resource, body)
@ -167,6 +175,11 @@ def get_logical_router(lrouter_id):
return client.get_resource(resource)
def update_logical_router(lrouter_id, **kwargs):
resource = 'logical-routers/%s' % lrouter_id
return update_resource_with_retry(resource, kwargs)
def delete_logical_router(lrouter_id):
resource = 'logical-routers/%s/' % lrouter_id
@ -174,21 +187,70 @@ def delete_logical_router(lrouter_id):
return client.delete_resource(resource)
def get_logical_router_port_by_ls_id(logical_switch_id):
resource = 'logical-router-ports?logical_switch_id=%s' % logical_switch_id
router_ports = client.get_resource(resource)
if int(router_ports['result_count']) >= 2:
raise nsx_exc.NsxPluginException(
err_msg=_("Can't support more than one logical router ports "
"on same logical switch %s ") % logical_switch_id)
elif int(router_ports['result_count']) == 1:
return router_ports['results'][0]
else:
err_msg = (_("Logical router link port not found on logical "
"switch %s") % logical_switch_id)
raise nsx_exc.ResourceNotFound(manager=client._get_manager_ip(),
operation=err_msg)
def create_logical_router_port_by_ls_id(logical_router_id,
ls_id,
logical_switch_port_id,
resource_type,
address_groups):
try:
port = get_logical_router_port_by_ls_id(ls_id)
except nsx_exc.ResourceNotFound:
return create_logical_router_port(logical_router_id,
ROUTER_INTF_PORT_NAME,
logical_switch_port_id,
resource_type,
address_groups)
else:
return update_logical_router_port(port['id'], subnets=address_groups)
def create_logical_router_port(logical_router_id,
display_name,
logical_switch_port_id,
resource_type,
cidr_length,
ip_address):
address_groups):
resource = 'logical-router-ports'
body = {'resource_type': resource_type,
body = {'display_name': display_name,
'resource_type': resource_type,
'logical_router_id': logical_router_id,
'subnets': [{"prefix_length": cidr_length,
"ip_addresses": [ip_address]}],
'subnets': address_groups,
'linked_logical_switch_port_id': logical_switch_port_id}
return client.create_resource(resource, body)
def update_logical_router_port_by_ls_id(logical_router_id, ls_id,
**payload):
port = get_logical_router_port_by_ls_id(ls_id)
return update_logical_router_port(port['id'], **payload)
def update_logical_router_port(logical_port_id, **kwargs):
resource = 'logical-router-ports/%s?detach=true' % logical_port_id
return update_resource_with_retry(resource, kwargs)
def delete_logical_router_port_by_ls_id(ls_id):
port = get_logical_router_port_by_ls_id(ls_id)
delete_logical_router_port(port['id'])
def delete_logical_router_port(logical_port_id):
resource = 'logical-router-ports/%s?detach=true' % logical_port_id
client.delete_resource(resource)

View File

@ -43,11 +43,13 @@ from neutron.db import agentschedulers_db
from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db
from neutron.db import extradhcpopt_db
from neutron.db import extraroute_db
from neutron.db import l3_db
from neutron.db import l3_gwmode_db
from neutron.db import models_v2
from neutron.db import portbindings_db
from neutron.db import securitygroups_db
from neutron.i18n import _LE, _LW
from neutron.i18n import _LE, _LI, _LW
from neutron.plugins.common import constants as plugin_const
from neutron.plugins.common import utils as n_utils
@ -65,7 +67,8 @@ LOG = log.getLogger(__name__)
class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
securitygroups_db.SecurityGroupDbMixin,
external_net_db.External_net_db_mixin,
l3_db.L3_NAT_dbonly_mixin,
extraroute_db.ExtraRoute_db_mixin,
l3_gwmode_db.L3_NAT_db_mixin,
portbindings_db.PortBindingMixin,
agentschedulers_db.DhcpAgentSchedulerDbMixin,
extradhcpopt_db.ExtraDhcpOptMixin):
@ -77,9 +80,11 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
supported_extension_aliases = ["quotas",
"binding",
"extra_dhcp_opt",
"ext-gw-mode",
"security-group",
"provider",
"external-net",
"extraroute",
"router"]
def __init__(self):
@ -320,6 +325,8 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
pnet._raise_if_updates_provider_attributes(net_data)
updated_net = super(NsxV3Plugin, self).update_network(context, id,
network)
self._process_l3_update(context, updated_net, network['network'])
self._extend_network_dict_provider(context, updated_net)
if (not self._network_is_external(context, id) and
'name' in net_data or 'admin_state_up' in net_data):
@ -491,6 +498,7 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, port_id)
nsxlib.delete_logical_port(nsx_port_id)
self.disassociate_floatingips(context, port_id)
ret_val = super(NsxV3Plugin, self).delete_port(context, port_id)
return ret_val
@ -532,13 +540,32 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
return updated_port
def _extract_external_gw(self, context, router, is_extract=True):
r = router['router']
gw_info = attributes.ATTR_NOT_SPECIFIED
# First extract the gateway info in case of updating
# gateway before edge is deployed.
if 'external_gateway_info' in r:
gw_info = r.get('external_gateway_info', {})
if is_extract:
del r['external_gateway_info']
network_id = (gw_info.get('network_id') if gw_info
else None)
if network_id:
ext_net = self._get_network(context, network_id)
if not ext_net.external:
msg = (_("Network '%s' is not a valid external network") %
network_id)
raise n_exc.BadRequest(resource='router', msg=msg)
return gw_info
def create_router(self, context, router):
# TODO(berlin): admin_state_up support
gw_info = self._extract_external_gw(context, router, is_extract=True)
tags = utils.build_v3_tags_payload(router['router'])
result = nsxlib.create_logical_router(
display_name=router['router'].get('name', 'a_router_with_no_name'),
tags=tags,
tier_0=True,
edge_cluster_uuid=cfg.CONF.nsx_v3.default_edge_cluster_uuid)
display_name=router['router'].get('name'),
tags=tags)
with context.session.begin():
router = super(NsxV3Plugin, self).create_router(
@ -546,7 +573,21 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
nsx_db.add_neutron_nsx_router_mapping(
context.session, router['id'], result['id'])
return router
if gw_info != attributes.ATTR_NOT_SPECIFIED:
try:
self._update_router_gw_info(context, router['id'], gw_info)
except nsx_exc.ManagerError:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Failed to set gateway info for router "
"being created: %s - removing router"),
router['id'])
self.delete_router(context, router['id'])
LOG.info(_LI("Create router failed while setting external "
"gateway. Router:%s has been removed from "
"DB and backend"),
router['id'])
return self.get_router(context, router['id'])
def delete_router(self, context, router_id):
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
@ -558,12 +599,12 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# passed (and indeed the resource was removed from the Neutron DB
try:
nsxlib.delete_logical_router(nsx_router_id)
except nsx_exc.LogicalRouterNotFound:
except nsx_exc.ResourceNotFound:
# If the logical router was not found on the backend do not worry
# about it. The conditions has already been logged, so there is no
# need to do further logging
pass
except nsx_exc.NsxPluginException:
except nsx_exc.ManagerError:
# if there is a failure in deleting the router do not fail the
# operation, especially since the router object has already been
# removed from the neutron DB. Take corrective steps to ensure the
@ -571,44 +612,92 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# eventually removed.
LOG.warning(_LW("Backend router deletion for neutron router %s "
"failed. The object was however removed from the "
"Neutron datanase"), router_id)
"Neutron database"), router_id)
return ret_val
def update_router(self, context, router_id, router):
# TODO(arosen) - call to backend
return super(NsxV3Plugin, self).update_router(context, id,
router)
# TODO(berlin): admin_state_up support
try:
return super(NsxV3Plugin, self).update_router(context, router_id,
router)
except nsx_exc.ResourceNotFound:
with context.session.begin(subtransactions=True):
router_db = self._get_router(context, router_id)
router_db['status'] = const.NET_STATUS_ERROR
raise nsx_exc.NsxPluginException(
err_msg=(_("logical router %s not found at the backend")
% router_id))
except nsx_exc.ManagerError:
raise nsx_exc.NsxPluginException(
err_msg=(_("Unable to update router %s at the backend")
% router_id))
def _get_router_interface_ports_by_network(
self, context, router_id, network_id):
port_filters = {'device_id': [router_id],
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF],
'network_id': [network_id]}
return self.get_ports(context, filters=port_filters)
def _get_ports_and_address_groups(self, context, router_id, network_id,
exclude_sub_ids=None):
exclude_sub_ids = [] if not exclude_sub_ids else exclude_sub_ids
address_groups = []
ports = self._get_router_interface_ports_by_network(
context, router_id, network_id)
ports = [port for port in ports
if port['fixed_ips'] and
port['fixed_ips'][0]['subnet_id'] not in exclude_sub_ids]
for port in ports:
address_group = {}
gateway_ip = port['fixed_ips'][0]['ip_address']
subnet = self.get_subnet(context,
port['fixed_ips'][0]['subnet_id'])
prefixlen = str(netaddr.IPNetwork(subnet['cidr']).prefixlen)
address_group['ip_addresses'] = [gateway_ip]
address_group['prefix_length'] = prefixlen
address_groups.append(address_group)
return (ports, address_groups)
def add_router_interface(self, context, router_id, interface_info):
# NOTE(arosen): I think there is a bug here since I believe we
# can also get a port or ip here....
subnet = self.get_subnet(context, interface_info['subnet_id'])
port = {'port': {'network_id': subnet['network_id'], 'name': '',
'admin_state_up': True, 'device_id': '',
'device_owner': l3_db.DEVICE_OWNER_ROUTER_INTF,
'mac_address': attributes.ATTR_NOT_SPECIFIED,
'fixed_ips': [{'subnet_id': subnet['id'],
'ip_address': subnet['gateway_ip']}]}}
port = self.create_port(context, port)
_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
# TODO(berlin): disallow multiple subnets attached to different routers
info = super(NsxV3Plugin, self).add_router_interface(
context, router_id, interface_info)
subnet = self.get_subnet(context, info['subnet_ids'][0])
port = self.get_port(context, info['port_id'])
network_id = subnet['network_id']
nsx_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, port['id'])
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
result = nsxlib.create_logical_router_port(
_ports, address_groups = self._get_ports_and_address_groups(
context, router_id, network_id)
nsxlib.create_logical_router_port_by_ls_id(
logical_router_id=nsx_router_id,
ls_id=nsx_net_id,
logical_switch_port_id=nsx_port_id,
resource_type="LogicalRouterDownLinkPort",
cidr_length=24, ip_address=subnet['gateway_ip'])
interface_info['port_id'] = port['id']
del interface_info['subnet_id']
result = super(NsxV3Plugin, self).add_router_interface(
context, router_id, interface_info)
return result
address_groups=address_groups)
return info
def remove_router_interface(self, context, router_id, interface_info):
if 'subnet_id' in interface_info:
subnet = None
subnet_id = None
port_id = None
self._validate_interface_info(interface_info, for_removal=True)
if 'port_id' in interface_info:
port_id = interface_info['port_id']
# find subnet_id - it is need for removing the SNAT rule
port = self._get_port(context, port_id)
if port.get('fixed_ips'):
subnet_id = port['fixed_ips'][0]['subnet_id']
if not (port['device_owner'] in const.ROUTER_INTERFACE_OWNERS
and port['device_id'] == router_id):
raise l3.RouterInterfaceNotFound(router_id=router_id,
port_id=port_id)
elif 'subnet_id' in interface_info:
subnet_id = interface_info['subnet_id']
subnet = self._get_subnet(context, subnet_id)
rport_qry = context.session.query(models_v2.Port)
@ -623,9 +712,30 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
else:
raise l3.RouterInterfaceNotFoundForSubnet(router_id=router_id,
subnet_id=subnet_id)
_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
try:
nsx_net_id, _nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, port_id)
nsxlib.delete_logical_router_port(nsx_port_id)
subnet = self.get_subnet(context, subnet_id)
ports, address_groups = self._get_ports_and_address_groups(
context, router_id, subnet['network_id'],
exclude_sub_ids=[subnet['id']])
nsx_router_id = nsx_db.get_nsx_router_id(
context.session, router_id)
if len(ports) >= 1:
new_using_port_id = ports[0]['id']
_net_id, new_nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, new_using_port_id)
nsxlib.update_logical_router_port_by_ls_id(
nsx_router_id, nsx_net_id,
linked_logical_switch_port_id=new_nsx_port_id,
subnets=address_groups)
else:
nsxlib.delete_logical_router_port_by_ls_id(nsx_net_id)
except nsx_exc.ResourceNotFound:
LOG.error(_LE("router port on router %(router_id)s for net "
"%(net_id)s not found at the backend"),
{'router_id': router_id,
'net_id': subnet['network_id']})
return super(NsxV3Plugin, self).remove_router_interface(
context, router_id, interface_info)

View File

@ -16,10 +16,13 @@
from oslo_utils import uuidutils
from vmware_nsx.neutron.plugins.vmware.common import exceptions as nsx_exc
from vmware_nsx.neutron.plugins.vmware.common import nsx_constants
FAKE_NAME = "fake_name"
DEFAULT_TIER0_ROUTER_UUID = "fake_default_tier0_router_uuid"
FAKE_MANAGER = "fake_manager_ip"
def make_fake_switch(switch_uuid=None, tz_uuid=None, name=FAKE_NAME):
@ -179,22 +182,6 @@ def update_logical_port(lport_id, name=None, admin_state=None):
return lport
def get_edge_cluster(edge_cluster_uuid):
FAKE_CLUSTER = {
"id": edge_cluster_uuid,
"members": [
{"member_index": 0},
{"member_index": 1}]}
return FAKE_CLUSTER
def get_logical_router(lrouter_uuid):
FAKE_LROUTER = {
"id": lrouter_uuid,
"edge_cluster_uuid": uuidutils.generate_uuid()}
return FAKE_LROUTER
def add_rules_in_section(rules, section_id):
for rule in rules:
rule['id'] = uuidutils.generate_uuid()
@ -216,3 +203,135 @@ def update_resource(resource, data):
def delete_resource(resource):
pass
class NsxV3Mock(object):
def __init__(self, default_tier0_router_uuid=DEFAULT_TIER0_ROUTER_UUID):
self.logical_routers = {}
self.logical_router_ports = {}
self.logical_ports = {}
if default_tier0_router_uuid:
self.create_logical_router(
DEFAULT_TIER0_ROUTER_UUID, None,
edge_cluster_uuid="fake_edge_cluster_uuid",
tier_0=True)
def get_edge_cluster(self, edge_cluster_uuid):
FAKE_CLUSTER = {
"id": edge_cluster_uuid,
"members": [
{"member_index": 0},
{"member_index": 1}]}
return FAKE_CLUSTER
def create_logical_router(self, display_name, tags,
edge_cluster_uuid=None,
tier_0=False):
router_type = (nsx_constants.ROUTER_TYPE_TIER0 if tier_0 else
nsx_constants.ROUTER_TYPE_TIER1)
if display_name == DEFAULT_TIER0_ROUTER_UUID:
fake_router_uuid = DEFAULT_TIER0_ROUTER_UUID
else:
fake_router_uuid = uuidutils.generate_uuid()
result = {'display_name': display_name,
'router_type': router_type,
'tags': tags,
'id': fake_router_uuid}
if edge_cluster_uuid:
result['edge_cluster_id'] = edge_cluster_uuid
self.logical_routers[fake_router_uuid] = result
return result
def get_logical_router(self, lrouter_id):
if lrouter_id in self.logical_routers:
return self.logical_routers[lrouter_id]
else:
raise nsx_exc.ResourceNotFound(manager=FAKE_MANAGER,
operation="get_logical_router")
def update_logical_router(self, lrouter_id, **kwargs):
if lrouter_id in self.logical_routers:
payload = self.logical_routers[lrouter_id]
payload.update(kwargs)
return payload
else:
raise nsx_exc.ResourceNotFound(manager=FAKE_MANAGER,
operation="update_logical_router")
def delete_logical_router(self, lrouter_id):
if lrouter_id in self.logical_routers:
del self.logical_routers[lrouter_id]
else:
raise nsx_exc.ResourceNotFound(manager=FAKE_MANAGER,
operation="delete_logical_router")
def get_logical_router_port_by_ls_id(self, logical_switch_id):
router_ports = []
for router_port in self.logical_router_ports.values():
ls_port_id = router_port['linked_logical_switch_port_id']
port = self.get_logical_port(ls_port_id)
if port['logical_switch_id'] == logical_switch_id:
router_ports.append(router_port)
if len(router_ports) >= 2:
raise nsx_exc.NsxPluginException(
err_msg=_("Can't support more than one logical router ports "
"on same logical switch %s ") % logical_switch_id)
elif len(router_ports) == 1:
return router_ports[0]
else:
err_msg = (_("Logical router link port not found on logical "
"switch %s") % logical_switch_id)
raise nsx_exc.ResourceNotFound(manager=FAKE_MANAGER,
operation=err_msg)
def create_logical_port(self, lswitch_id, vif_uuid, tags,
attachment_type=nsx_constants.ATTACHMENT_VIF,
admin_state=True, name=None, address_bindings=None,
parent_name=None, parent_tag=None):
fake_port = create_logical_port(
lswitch_id, vif_uuid, tags,
attachment_type=attachment_type,
admin_state=admin_state, name=name,
address_bindings=address_bindings,
parent_name=parent_name, parent_tag=parent_tag)
fake_port_uuid = fake_port['id']
self.logical_ports[fake_port_uuid] = fake_port
return fake_port
def get_logical_port(self, logical_port_id):
if logical_port_id in self.logical_ports:
return self.logical_ports[logical_port_id]
else:
raise nsx_exc.ResourceNotFound(
manager=FAKE_MANAGER, operation="get_logical_port")
def create_logical_router_port(self, logical_router_id,
display_name,
logical_switch_port_id,
resource_type,
address_groups):
fake_router_port_uuid = uuidutils.generate_uuid()
body = {'id': fake_router_port_uuid,
'display_name': display_name,
'resource_type': resource_type,
'logical_router_id': logical_router_id,
'subnets': address_groups,
'linked_logical_switch_port_id': logical_switch_port_id}
self.logical_router_ports[fake_router_port_uuid] = body
return body
def update_logical_router_port(self, logical_port_id, **kwargs):
if logical_port_id in self.logical_router_ports:
payload = self.logical_router_ports[logical_port_id]
payload.update(kwargs)
return payload
else:
raise nsx_exc.ResourceNotFound(
manager=FAKE_MANAGER, operation="update_logical_router_port")
def delete_logical_router_port(self, logical_port_id):
if logical_port_id in self.logical_router_ports:
del self.logical_router_ports[logical_port_id]
else:
raise nsx_exc.ResourceNotFound(
manager=FAKE_MANAGER, operation="update_logical_router_port")

View File

@ -15,12 +15,26 @@
import mock
from oslo_config import cfg
import six
from neutron.api.v2 import attributes
from neutron import context
from neutron.extensions import external_net
from neutron.extensions import extraroute
from neutron.extensions import l3
from neutron.extensions import l3_ext_gw_mode
from neutron.extensions import providernet as pnet
from neutron import manager
import neutron.tests.unit.db.test_db_base_plugin_v2 as test_plugin
from neutron.tests.unit.extensions import test_extra_dhcp_opt as test_dhcpopts
import neutron.tests.unit.extensions.test_extraroute as test_ext_route
import neutron.tests.unit.extensions.test_l3 as test_l3_plugin
import neutron.tests.unit.extensions.test_l3_ext_gw_mode as test_ext_gw_mode
import neutron.tests.unit.extensions.test_securitygroup as ext_sg
from vmware_nsx.neutron.plugins.vmware.common import utils
from vmware_nsx.neutron.plugins.vmware.nsxlib import v3 as nsxlib
from vmware_nsx.neutron.plugins.vmware.nsxlib.v3 import dfw_api as firewall
from vmware_nsx.neutron.tests.unit import vmware
from vmware_nsx.neutron.tests.unit.vmware import nsx_v3_mocks
PLUGIN_NAME = ('vmware_nsx.neutron.plugins.vmware.'
@ -45,9 +59,6 @@ class NsxPluginV3TestCase(test_plugin.NeutronDbPluginV2TestCase):
nsxlib.delete_logical_port = mock.Mock()
nsxlib.get_logical_port = nsx_v3_mocks.get_logical_port
nsxlib.update_logical_port = nsx_v3_mocks.update_logical_port
# TODO(berlin): fill valid data
nsxlib.get_edge_cluster = nsx_v3_mocks.get_edge_cluster
nsxlib.get_logical_router = nsx_v3_mocks.get_logical_router
firewall.add_rules_in_section = nsx_v3_mocks.add_rules_in_section
firewall.nsclient.create_resource = nsx_v3_mocks.create_resource
firewall.nsclient.update_resource = nsx_v3_mocks.update_resource
@ -56,6 +67,36 @@ class NsxPluginV3TestCase(test_plugin.NeutronDbPluginV2TestCase):
super(NsxPluginV3TestCase, self).setUp(plugin=plugin,
ext_mgr=ext_mgr)
self.v3_mock = nsx_v3_mocks.NsxV3Mock()
nsxlib.get_edge_cluster = self.v3_mock.get_edge_cluster
nsxlib.get_logical_router = self.v3_mock.get_logical_router
def _create_network(self, fmt, name, admin_state_up,
arg_list=None, providernet_args=None, **kwargs):
data = {'network': {'name': name,
'admin_state_up': admin_state_up,
'tenant_id': self._tenant_id}}
# Fix to allow the router:external attribute and any other
# attributes containing a colon to be passed with
# a double underscore instead
kwargs = dict((k.replace('__', ':'), v) for k, v in kwargs.items())
if external_net.EXTERNAL in kwargs:
arg_list = (external_net.EXTERNAL, ) + (arg_list or ())
attrs = kwargs
if providernet_args:
attrs.update(providernet_args)
for arg in (('admin_state_up', 'tenant_id', 'shared') +
(arg_list or ())):
# Arg must be present and not empty
if arg in kwargs and kwargs[arg]:
data['network'][arg] = kwargs[arg]
network_req = self.new_create_request('networks', data, fmt)
if (kwargs.get('set_context') and 'tenant_id' in kwargs):
# create a specific auth context for this request
network_req.environ['neutron.context'] = context.Context(
'', kwargs['tenant_id'])
return network_req.get_response(self.api)
class TestNetworksV2(test_plugin.TestNetworksV2, NsxPluginV3TestCase):
@ -97,3 +138,111 @@ class DHCPOptsTestCase(test_dhcpopts.TestExtraDhcpOpt, NsxPluginV3TestCase):
def setUp(self, plugin=None):
super(test_dhcpopts.ExtraDhcpOptDBTestCase, self).setUp(
plugin=PLUGIN_NAME)
class TestL3ExtensionManager(object):
def get_resources(self):
# Simulate extension of L3 attribute map
# First apply attribute extensions
for key in l3.RESOURCE_ATTRIBUTE_MAP.keys():
l3.RESOURCE_ATTRIBUTE_MAP[key].update(
l3_ext_gw_mode.EXTENDED_ATTRIBUTES_2_0.get(key, {}))
l3.RESOURCE_ATTRIBUTE_MAP[key].update(
extraroute.EXTENDED_ATTRIBUTES_2_0.get(key, {}))
# Finally add l3 resources to the global attribute map
attributes.RESOURCE_ATTRIBUTE_MAP.update(
l3.RESOURCE_ATTRIBUTE_MAP)
return l3.L3.get_resources()
def get_actions(self):
return []
def get_request_extensions(self):
return []
def backup_l3_attribute_map():
"""Return a backup of the original l3 attribute map."""
return dict((res, attrs.copy()) for
(res, attrs) in six.iteritems(l3.RESOURCE_ATTRIBUTE_MAP))
def restore_l3_attribute_map(map_to_restore):
"""Ensure changes made by fake ext mgrs are reverted."""
l3.RESOURCE_ATTRIBUTE_MAP = map_to_restore
class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxPluginV3TestCase):
def _restore_l3_attribute_map(self):
l3.RESOURCE_ATTRIBUTE_MAP = self._l3_attribute_map_bk
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None,
service_plugins=None):
self._l3_attribute_map_bk = backup_l3_attribute_map()
cfg.CONF.set_override('api_extensions_path', vmware.NSXEXT_PATH)
cfg.CONF.set_default('max_routes', 3)
self.addCleanup(restore_l3_attribute_map, self._l3_attribute_map_bk)
ext_mgr = ext_mgr or TestL3ExtensionManager()
super(L3NatTest, self).setUp(
plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins)
plugin_instance = manager.NeutronManager.get_plugin()
self._plugin_name = "%s.%s" % (
plugin_instance.__module__,
plugin_instance.__class__.__name__)
self._plugin_class = plugin_instance.__class__
nsxlib.create_logical_port = self.v3_mock.create_logical_port
nsxlib.create_logical_router = self.v3_mock.create_logical_router
nsxlib.update_logical_router = self.v3_mock.update_logical_router
nsxlib.delete_logical_router = self.v3_mock.delete_logical_router
nsxlib.get_logical_router_port_by_ls_id = (
self.v3_mock.get_logical_router_port_by_ls_id)
nsxlib.create_logical_router_port = (
self.v3_mock.create_logical_router_port)
nsxlib.update_logical_router_port = (
self.v3_mock.update_logical_router_port)
nsxlib.delete_logical_router_port = (
self.v3_mock.delete_logical_router_port)
def _create_l3_ext_network(
self, physical_network=nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID):
name = 'l3_ext_net'
net_type = utils.NetworkTypes.L3_EXT
providernet_args = {pnet.NETWORK_TYPE: net_type,
pnet.PHYSICAL_NETWORK: physical_network}
return self.network(name=name,
router__external=True,
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK))
class TestL3NatTestCase(L3NatTest,
test_l3_plugin.L3NatDBIntTestCase,
NsxPluginV3TestCase,
test_ext_route.ExtraRouteDBTestCaseBase):
def _test_create_l3_ext_network(
self, physical_network=nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID):
name = 'l3_ext_net'
net_type = utils.NetworkTypes.L3_EXT
expected = [('subnets', []), ('name', name), ('admin_state_up', True),
('status', 'ACTIVE'), ('shared', False),
(external_net.EXTERNAL, True),
(pnet.NETWORK_TYPE, net_type),
(pnet.PHYSICAL_NETWORK, physical_network)]
with self._create_l3_ext_network(physical_network) as net:
for k, v in expected:
self.assertEqual(net['network'][k], v)
def test_create_l3_ext_network_with_default_tier0(self):
self._test_create_l3_ext_network()
def test_floatingip_with_invalid_create_port(self):
self._test_floatingip_with_invalid_create_port(self._plugin_name)
class ExtGwModeTestCase(L3NatTest,
test_ext_gw_mode.ExtGwModeIntTestCase):
pass