Merge "NSXv3: Handle address scope change on subnetpool"
This commit is contained in:
commit
80cf48c6ae
@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from neutron.db import _resource_extend as resource_extend
|
from neutron.db import _resource_extend as resource_extend
|
||||||
@ -108,6 +109,12 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
subnetpool = self.get_subnetpool(context, subnet['subnetpool_id'])
|
subnetpool = self.get_subnetpool(context, subnet['subnetpool_id'])
|
||||||
return subnetpool.get('address_scope_id', '')
|
return subnetpool.get('address_scope_id', '')
|
||||||
|
|
||||||
|
def _get_subnetpool_address_scope(self, context, subnetpool_id):
|
||||||
|
if not subnetpool_id:
|
||||||
|
return
|
||||||
|
subnetpool = self.get_subnetpool(context, subnetpool_id)
|
||||||
|
return subnetpool.get('address_scope_id', '')
|
||||||
|
|
||||||
# TODO(asarfaty): the NSX-V3 needs a very similar code too
|
# TODO(asarfaty): the NSX-V3 needs a very similar code too
|
||||||
def _validate_address_scope_for_router_interface(self, context, router_id,
|
def _validate_address_scope_for_router_interface(self, context, router_id,
|
||||||
gw_network_id, subnet_id):
|
gw_network_id, subnet_id):
|
||||||
@ -130,7 +137,7 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
def _find_router_subnets_cidrs(self, context, router_id):
|
def _find_router_subnets_cidrs(self, context, router_id):
|
||||||
"""Retrieve cidrs of subnets attached to the specified router."""
|
"""Retrieve cidrs of subnets attached to the specified router."""
|
||||||
subnets = self._find_router_subnets_and_cidrs(context, router_id)
|
subnets = self._find_router_subnets(context, router_id)
|
||||||
return [subnet['cidr'] for subnet in subnets]
|
return [subnet['cidr'] for subnet in subnets]
|
||||||
|
|
||||||
def _find_router_subnets_cidrs_per_addr_scope(self, context, router_id):
|
def _find_router_subnets_cidrs_per_addr_scope(self, context, router_id):
|
||||||
@ -140,10 +147,11 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
return a list of lists of subnets cidrs belonging to same
|
return a list of lists of subnets cidrs belonging to same
|
||||||
address pool.
|
address pool.
|
||||||
"""
|
"""
|
||||||
subnets = self._find_router_subnets_and_cidrs(context, router_id)
|
subnets = self._find_router_subnets(context, router_id)
|
||||||
cidrs_map = {}
|
cidrs_map = {}
|
||||||
for subnet in subnets:
|
for subnet in subnets:
|
||||||
ads = self._get_subnet_address_scope(context, subnet['id']) or ''
|
ads = self._get_subnetpool_address_scope(
|
||||||
|
context, subnet['subnetpool_id']) or ''
|
||||||
if ads not in cidrs_map:
|
if ads not in cidrs_map:
|
||||||
cidrs_map[ads] = []
|
cidrs_map[ads] = []
|
||||||
cidrs_map[ads].append(subnet['cidr'])
|
cidrs_map[ads].append(subnet['cidr'])
|
||||||
@ -159,7 +167,7 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
device_id=device_id,
|
device_id=device_id,
|
||||||
device_owner=device_owner,).all()
|
device_owner=device_owner,).all()
|
||||||
|
|
||||||
def _find_router_subnets_and_cidrs(self, context, router_id):
|
def _find_router_subnets(self, context, router_id):
|
||||||
"""Retrieve subnets attached to the specified router."""
|
"""Retrieve subnets attached to the specified router."""
|
||||||
ports = self._get_port_by_device_id(context, router_id,
|
ports = self._get_port_by_device_id(context, router_id,
|
||||||
l3_db.DEVICE_OWNER_ROUTER_INTF)
|
l3_db.DEVICE_OWNER_ROUTER_INTF)
|
||||||
@ -169,5 +177,80 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
for ip in port.get('fixed_ips', []):
|
for ip in port.get('fixed_ips', []):
|
||||||
subnet_qry = context.session.query(models_v2.Subnet)
|
subnet_qry = context.session.query(models_v2.Subnet)
|
||||||
subnet = subnet_qry.filter_by(id=ip.subnet_id).one()
|
subnet = subnet_qry.filter_by(id=ip.subnet_id).one()
|
||||||
subnets.append({'id': subnet.id, 'cidr': subnet.cidr})
|
subnets.append({'id': subnet.id, 'cidr': subnet.cidr,
|
||||||
|
'subnetpool_id': subnet.subnetpool_id})
|
||||||
return subnets
|
return subnets
|
||||||
|
|
||||||
|
def _find_router_gw_subnets(self, context, router):
|
||||||
|
"""Retrieve external subnets attached to router GW"""
|
||||||
|
if not router['external_gateway_info']:
|
||||||
|
return []
|
||||||
|
|
||||||
|
subnets = []
|
||||||
|
for fip in router['external_gateway_info']['external_fixed_ips']:
|
||||||
|
subnet = self.get_subnet(context, fip['subnet_id'])
|
||||||
|
subnets.append(subnet)
|
||||||
|
|
||||||
|
return subnets
|
||||||
|
|
||||||
|
def recalculate_snat_rules_for_router(self, context, router, subnets):
|
||||||
|
"""Method to recalculate router snat rules for specific subnets.
|
||||||
|
Invoked when subnetpool address scope changes.
|
||||||
|
Implemented in child plugin classes
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _filter_subnets_by_subnetpool(self, subnets, subnetpool_id):
|
||||||
|
return [subnet for subnet in subnets
|
||||||
|
if subnet['subnetpool_id'] == subnetpool_id]
|
||||||
|
|
||||||
|
def on_subnetpool_address_scope_updated(self, resource, event,
|
||||||
|
trigger, **kwargs):
|
||||||
|
context = kwargs['context']
|
||||||
|
|
||||||
|
routers = self.get_routers(context)
|
||||||
|
subnetpool_id = kwargs['subnetpool_id']
|
||||||
|
elevated_context = context.elevated()
|
||||||
|
LOG.info("Inspecting routers for potential configuration changes "
|
||||||
|
"due to address scope change on subnetpool %s", subnetpool_id)
|
||||||
|
for rtr in routers:
|
||||||
|
subnets = self._find_router_subnets(elevated_context,
|
||||||
|
rtr['id'])
|
||||||
|
gw_subnets = self._find_router_gw_subnets(elevated_context,
|
||||||
|
rtr)
|
||||||
|
|
||||||
|
affected_subnets = self._filter_subnets_by_subnetpool(
|
||||||
|
subnets, subnetpool_id)
|
||||||
|
affected_gw_subnets = self._filter_subnets_by_subnetpool(
|
||||||
|
gw_subnets, subnetpool_id)
|
||||||
|
|
||||||
|
if not affected_subnets and not affected_gw_subnets:
|
||||||
|
# No subnets were affected by address scope change
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (affected_subnets == subnets and
|
||||||
|
affected_gw_subnets == gw_subnets):
|
||||||
|
# All subnets remain under the same address scope
|
||||||
|
# (all router subnets were allocated from subnetpool_id)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TODO(annak): handle east-west FW rules
|
||||||
|
if not rtr['external_gateway_info']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not rtr['external_gateway_info']['enable_snat']:
|
||||||
|
LOG.warning("Due to address scope change on subnetpool "
|
||||||
|
"%(subnetpool)s, uniqueness on interface "
|
||||||
|
"addresses on no-snat router %(router) is no "
|
||||||
|
"longer guaranteed, which may result in faulty "
|
||||||
|
"operation.", {'subnetpool': subnetpool_id,
|
||||||
|
'router': rtr['id']})
|
||||||
|
continue
|
||||||
|
|
||||||
|
if affected_gw_subnets:
|
||||||
|
# GW address scope have changed - we need to revisit snat
|
||||||
|
# rules for all router interfaces
|
||||||
|
affected_subnets = subnets
|
||||||
|
|
||||||
|
self.recalculate_snat_rules_for_router(context, rtr,
|
||||||
|
affected_subnets)
|
||||||
|
@ -3327,7 +3327,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
gw_port = router.gw_port
|
gw_port = router.gw_port
|
||||||
if gw_port and gw_port.get('fixed_ips') and router.enable_snat:
|
if gw_port and gw_port.get('fixed_ips') and router.enable_snat:
|
||||||
snat_ip = gw_port['fixed_ips'][0]['ip_address']
|
snat_ip = gw_port['fixed_ips'][0]['ip_address']
|
||||||
subnets = self._find_router_subnets_and_cidrs(context.elevated(),
|
subnets = self._find_router_subnets(context.elevated(),
|
||||||
router['id'])
|
router['id'])
|
||||||
for subnet in subnets:
|
for subnet in subnets:
|
||||||
|
|
||||||
@ -3335,8 +3335,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
# no need for SNAT
|
# no need for SNAT
|
||||||
gw_address_scope = self._get_network_address_scope(
|
gw_address_scope = self._get_network_address_scope(
|
||||||
context.elevated(), gw_port['network_id'])
|
context.elevated(), gw_port['network_id'])
|
||||||
subnet_address_scope = self._get_subnet_address_scope(
|
subnet_address_scope = self._get_subnetpool_address_scope(
|
||||||
context.elevated(), subnet['id'])
|
context.elevated(), subnet['subnetpool_id'])
|
||||||
if (gw_address_scope and
|
if (gw_address_scope and
|
||||||
gw_address_scope == subnet_address_scope):
|
gw_address_scope == subnet_address_scope):
|
||||||
LOG.info("No need for SNAT rule for router %(router)s "
|
LOG.info("No need for SNAT rule for router %(router)s "
|
||||||
@ -3386,14 +3386,14 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
if gw_address_scope is None:
|
if gw_address_scope is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
subnets = self._find_router_subnets_and_cidrs(context.elevated(),
|
subnets = self._find_router_subnets(context.elevated(),
|
||||||
router['id'])
|
router['id'])
|
||||||
no_nat_cidrs = []
|
no_nat_cidrs = []
|
||||||
for subnet in subnets:
|
for subnet in subnets:
|
||||||
# if the subnets address scope is the same as the gateways:
|
# if the subnets address scope is the same as the gateways:
|
||||||
# we should add it to the rule
|
# we should add it to the rule
|
||||||
subnet_address_scope = self._get_subnet_address_scope(
|
subnet_address_scope = self._get_subnetpool_address_scope(
|
||||||
context.elevated(), subnet['id'])
|
context.elevated(), subnet['subnetpool_id'])
|
||||||
if (gw_address_scope == subnet_address_scope):
|
if (gw_address_scope == subnet_address_scope):
|
||||||
no_nat_cidrs.append(subnet['cidr'])
|
no_nat_cidrs.append(subnet['cidr'])
|
||||||
|
|
||||||
|
@ -195,6 +195,10 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
self.nsxlib.reinitialize_cluster,
|
self.nsxlib.reinitialize_cluster,
|
||||||
resources.PROCESS, events.AFTER_INIT)
|
resources.PROCESS, events.AFTER_INIT)
|
||||||
|
|
||||||
|
registry.subscribe(
|
||||||
|
self.on_subnetpool_address_scope_updated,
|
||||||
|
resources.SUBNETPOOL_ADDRESS_SCOPE, events.AFTER_UPDATE)
|
||||||
|
|
||||||
self._nsx_version = self.nsxlib.get_version()
|
self._nsx_version = self.nsxlib.get_version()
|
||||||
LOG.info("NSX Version: %s", self._nsx_version)
|
LOG.info("NSX Version: %s", self._nsx_version)
|
||||||
|
|
||||||
@ -2796,7 +2800,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
# than the gw
|
# than the gw
|
||||||
gw_address_scope = self._get_network_address_scope(
|
gw_address_scope = self._get_network_address_scope(
|
||||||
context, router.gw_port.network_id)
|
context, router.gw_port.network_id)
|
||||||
subnets = self._find_router_subnets_and_cidrs(context.elevated(),
|
subnets = self._find_router_subnets(context.elevated(),
|
||||||
router_id)
|
router_id)
|
||||||
for subnet in subnets:
|
for subnet in subnets:
|
||||||
self._add_subnet_snat_rule(context, router_id, nsx_router_id,
|
self._add_subnet_snat_rule(context, router_id, nsx_router_id,
|
||||||
@ -2814,8 +2818,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
# if the subnets address scope is the same as the gateways:
|
# if the subnets address scope is the same as the gateways:
|
||||||
# no need for SNAT
|
# no need for SNAT
|
||||||
if gw_address_scope:
|
if gw_address_scope:
|
||||||
subnet_address_scope = self._get_subnet_address_scope(
|
subnet_address_scope = self._get_subnetpool_address_scope(
|
||||||
context, subnet['id'])
|
context, subnet['subnetpool_id'])
|
||||||
if (gw_address_scope == subnet_address_scope):
|
if (gw_address_scope == subnet_address_scope):
|
||||||
LOG.info("No need for SNAT rule for router %(router)s "
|
LOG.info("No need for SNAT rule for router %(router)s "
|
||||||
"and subnet %(subnet)s because they use the "
|
"and subnet %(subnet)s because they use the "
|
||||||
@ -3702,3 +3706,45 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
else:
|
else:
|
||||||
az_name = nsx_az.DEFAULT_NAME
|
az_name = nsx_az.DEFAULT_NAME
|
||||||
net_res[az_ext.AVAILABILITY_ZONES] = [az_name]
|
net_res[az_ext.AVAILABILITY_ZONES] = [az_name]
|
||||||
|
|
||||||
|
def recalculate_snat_rules_for_router(self, context, router, subnets):
|
||||||
|
"""Rrecalculate router snat rules for specific subnets.
|
||||||
|
Invoked when subnetpool address scope changes.
|
||||||
|
"""
|
||||||
|
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
|
||||||
|
router['id'])
|
||||||
|
|
||||||
|
if not router['external_gateway_info']:
|
||||||
|
return
|
||||||
|
|
||||||
|
LOG.info("Recalculating snat rules for router %s", router['id'])
|
||||||
|
fip = router['external_gateway_info']['external_fixed_ips'][0]
|
||||||
|
ext_addr = fip['ip_address']
|
||||||
|
gw_address_scope = self._get_network_address_scope(
|
||||||
|
context, router['external_gateway_info']['network_id'])
|
||||||
|
|
||||||
|
# TODO(annak): improve amount of backend calls by rebuilding all
|
||||||
|
# snat rules when API is available
|
||||||
|
for subnet in subnets:
|
||||||
|
if gw_address_scope:
|
||||||
|
subnet_address_scope = self._get_subnetpool_address_scope(
|
||||||
|
context, subnet['subnetpool_id'])
|
||||||
|
LOG.info("Deleting SNAT rule for %(router)s "
|
||||||
|
"and subnet %(subnet)s",
|
||||||
|
{'router': router['id'],
|
||||||
|
'subnet': subnet['id']})
|
||||||
|
|
||||||
|
# Delete rule for this router/subnet pair if it exists
|
||||||
|
self._routerlib.delete_gw_snat_rule_by_source(
|
||||||
|
nsx_router_id, ext_addr, subnet['cidr'],
|
||||||
|
skip_not_found=True)
|
||||||
|
|
||||||
|
if (gw_address_scope != subnet_address_scope):
|
||||||
|
# subnet is no longer under same address scope with GW
|
||||||
|
LOG.info("Adding SNAT rule for %(router)s "
|
||||||
|
"and subnet %(subnet)s",
|
||||||
|
{'router': router['id'],
|
||||||
|
'subnet': subnet['id']})
|
||||||
|
self._routerlib.add_gw_snat_rule(nsx_router_id, ext_addr,
|
||||||
|
source_net=subnet['cidr'],
|
||||||
|
bypass_firewall=False)
|
||||||
|
@ -892,15 +892,20 @@ class TestL3NatTestCase(L3NatTest,
|
|||||||
int_subnet['subnet']['id'],
|
int_subnet['subnet']['id'],
|
||||||
None)
|
None)
|
||||||
|
|
||||||
def test_router_address_scope_snat_rules(self):
|
def _mock_add_snat_rule(self):
|
||||||
"""Test that if the router interface had the same address scope
|
return mock.patch("vmware_nsxlib.v3.router.RouterLib."
|
||||||
as the gateway - snat rule is not added.
|
"add_gw_snat_rule")
|
||||||
"""
|
|
||||||
# create an external network on one address scope
|
def _mock_del_snat_rule(self):
|
||||||
with self.address_scope(name='as1') as addr_scope, \
|
return mock.patch("vmware_nsxlib.v3.router.RouterLib."
|
||||||
self.network() as ext_net:
|
"delete_gw_snat_rule_by_source")
|
||||||
|
|
||||||
|
def _prepare_external_subnet_on_address_scope(self,
|
||||||
|
ext_net,
|
||||||
|
address_scope):
|
||||||
|
|
||||||
self._set_net_external(ext_net['network']['id'])
|
self._set_net_external(ext_net['network']['id'])
|
||||||
as_id = addr_scope['address_scope']['id']
|
as_id = address_scope['address_scope']['id']
|
||||||
subnet = netaddr.IPNetwork('10.10.10.0/21')
|
subnet = netaddr.IPNetwork('10.10.10.0/21')
|
||||||
subnetpool = self._test_create_subnetpool(
|
subnetpool = self._test_create_subnetpool(
|
||||||
[subnet.cidr], name='sp1',
|
[subnet.cidr], name='sp1',
|
||||||
@ -914,7 +919,12 @@ class TestL3NatTestCase(L3NatTest,
|
|||||||
'tenant_id': ext_net['network']['tenant_id']}}
|
'tenant_id': ext_net['network']['tenant_id']}}
|
||||||
req = self.new_create_request('subnets', data)
|
req = self.new_create_request('subnets', data)
|
||||||
ext_subnet = self.deserialize(self.fmt, req.get_response(self.api))
|
ext_subnet = self.deserialize(self.fmt, req.get_response(self.api))
|
||||||
|
return ext_subnet['subnet']
|
||||||
|
|
||||||
|
def _create_subnet_and_assert_snat_rules(self, subnetpool_id,
|
||||||
|
router_id,
|
||||||
|
assert_snat_deleted=False,
|
||||||
|
assert_snat_added=False):
|
||||||
# create a regular network on the same address scope
|
# create a regular network on the same address scope
|
||||||
with self.network() as net:
|
with self.network() as net:
|
||||||
data = {'subnet': {
|
data = {'subnet': {
|
||||||
@ -926,58 +936,250 @@ class TestL3NatTestCase(L3NatTest,
|
|||||||
int_subnet = self.deserialize(
|
int_subnet = self.deserialize(
|
||||||
self.fmt, req.get_response(self.api))
|
self.fmt, req.get_response(self.api))
|
||||||
|
|
||||||
|
with self._mock_add_snat_rule() as add_nat,\
|
||||||
|
self._mock_del_snat_rule() as delete_nat:
|
||||||
|
# Add the interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'add',
|
||||||
|
router_id,
|
||||||
|
int_subnet['subnet']['id'],
|
||||||
|
None)
|
||||||
|
|
||||||
|
if assert_snat_deleted:
|
||||||
|
delete_nat.assert_called()
|
||||||
|
else:
|
||||||
|
delete_nat.assert_not_called()
|
||||||
|
|
||||||
|
if assert_snat_added:
|
||||||
|
add_nat.assert_called()
|
||||||
|
else:
|
||||||
|
add_nat.assert_not_called()
|
||||||
|
|
||||||
|
def test_router_address_scope_snat_rules(self):
|
||||||
|
"""Test that if the router interface had the same address scope
|
||||||
|
as the gateway - snat rule is not added.
|
||||||
|
"""
|
||||||
|
# create an external network on one address scope
|
||||||
|
with self.address_scope(name='as1') as addr_scope, \
|
||||||
|
self.network() as ext_net:
|
||||||
|
ext_subnet = self._prepare_external_subnet_on_address_scope(
|
||||||
|
ext_net, addr_scope)
|
||||||
|
|
||||||
# create a router with this gateway
|
# create a router with this gateway
|
||||||
with self.router() as r:
|
with self.router() as r:
|
||||||
self._add_external_gateway_to_router(
|
self._add_external_gateway_to_router(
|
||||||
r['router']['id'],
|
r['router']['id'],
|
||||||
ext_subnet['subnet']['network_id'])
|
ext_subnet['network_id'])
|
||||||
|
|
||||||
with mock.patch("vmware_nsxlib.v3.router.RouterLib."
|
# create a regular network on same address scope
|
||||||
"add_gw_snat_rule") as add_nat:
|
# and verify no snat change
|
||||||
# Add the interface
|
as_id = addr_scope['address_scope']['id']
|
||||||
self._router_interface_action(
|
subnet = netaddr.IPNetwork('30.10.10.0/24')
|
||||||
'add',
|
subnetpool = self._test_create_subnetpool(
|
||||||
r['router']['id'],
|
[subnet.cidr], name='sp2',
|
||||||
int_subnet['subnet']['id'],
|
min_prefixlen='24', address_scope_id=as_id)
|
||||||
None)
|
as_id = addr_scope['address_scope']['id']
|
||||||
# make sure snat rules are not added
|
subnetpool_id = subnetpool['subnetpool']['id']
|
||||||
add_nat.assert_not_called()
|
self._create_subnet_and_assert_snat_rules(
|
||||||
|
subnetpool_id, r['router']['id'])
|
||||||
|
|
||||||
# create a regular network on a different address scope
|
# create a regular network on a different address scope
|
||||||
with self.address_scope(name='as2') as addr_scope2, \
|
# and verify snat rules are added
|
||||||
self.network() as net:
|
with self.address_scope(name='as2') as addr_scope2:
|
||||||
as_id2 = addr_scope2['address_scope']['id']
|
as2_id = addr_scope2['address_scope']['id']
|
||||||
subnet2 = netaddr.IPNetwork('20.10.10.0/24')
|
subnet2 = netaddr.IPNetwork('20.10.10.0/24')
|
||||||
subnetpool2 = self._test_create_subnetpool(
|
subnetpool2 = self._test_create_subnetpool(
|
||||||
[subnet2.cidr], name='sp2',
|
[subnet2.cidr], name='sp2',
|
||||||
min_prefixlen='24', address_scope_id=as_id2)
|
min_prefixlen='24', address_scope_id=as2_id)
|
||||||
subnetpool_id2 = subnetpool2['subnetpool']['id']
|
subnetpool2_id = subnetpool2['subnetpool']['id']
|
||||||
data = {'subnet': {
|
|
||||||
'network_id': net['network']['id'],
|
self._create_subnet_and_assert_snat_rules(
|
||||||
'subnetpool_id': subnetpool_id2,
|
subnetpool2_id, r['router']['id'],
|
||||||
'ip_version': 4,
|
assert_snat_added=True)
|
||||||
'tenant_id': net['network']['tenant_id']}}
|
|
||||||
req = self.new_create_request('subnets', data)
|
def _test_router_address_scope_change(self, change_gw=False):
|
||||||
int_subnet = self.deserialize(
|
"""When subnetpool address scope changes, and router that was
|
||||||
self.fmt, req.get_response(self.api))
|
originally under same address scope, results having different
|
||||||
|
address scopes, relevant snat rules are added.
|
||||||
|
"""
|
||||||
|
# create an external network on one address scope
|
||||||
|
with self.address_scope(name='as1') as addr_scope, \
|
||||||
|
self.network() as ext_net:
|
||||||
|
ext_subnet = self._prepare_external_subnet_on_address_scope(
|
||||||
|
ext_net, addr_scope)
|
||||||
|
|
||||||
# create a router with this gateway
|
# create a router with this gateway
|
||||||
with self.router() as r:
|
with self.router() as r:
|
||||||
self._add_external_gateway_to_router(
|
self._add_external_gateway_to_router(
|
||||||
r['router']['id'],
|
r['router']['id'],
|
||||||
ext_subnet['subnet']['network_id'])
|
ext_subnet['network_id'])
|
||||||
|
|
||||||
|
# create a regular network on same address scope
|
||||||
|
# and verify no snat change
|
||||||
|
as_id = addr_scope['address_scope']['id']
|
||||||
|
subnet2 = netaddr.IPNetwork('40.10.10.0/24')
|
||||||
|
subnetpool2 = self._test_create_subnetpool(
|
||||||
|
[subnet2.cidr], name='sp2',
|
||||||
|
min_prefixlen='24', address_scope_id=as_id)
|
||||||
|
subnetpool2_id = subnetpool2['subnetpool']['id']
|
||||||
|
|
||||||
|
self._create_subnet_and_assert_snat_rules(
|
||||||
|
subnetpool2_id, r['router']['id'])
|
||||||
|
|
||||||
|
# change address scope of the first subnetpool
|
||||||
|
with self.address_scope(name='as2') as addr_scope2,\
|
||||||
|
self._mock_add_snat_rule() as add_nat:
|
||||||
|
|
||||||
|
as2_id = addr_scope2['address_scope']['id']
|
||||||
|
data = {'subnetpool': {
|
||||||
|
'address_scope_id': as2_id}}
|
||||||
|
|
||||||
|
if change_gw:
|
||||||
|
subnetpool_to_update = ext_subnet['subnetpool_id']
|
||||||
|
else:
|
||||||
|
subnetpool_to_update = subnetpool2_id
|
||||||
|
|
||||||
|
req = self.new_update_request('subnetpools', data,
|
||||||
|
subnetpool_to_update)
|
||||||
|
req.get_response(self.api)
|
||||||
|
|
||||||
with mock.patch("vmware_nsxlib.v3.router.RouterLib."
|
|
||||||
"add_gw_snat_rule") as add_nat:
|
|
||||||
# Add the interface
|
|
||||||
self._router_interface_action(
|
|
||||||
'add',
|
|
||||||
r['router']['id'],
|
|
||||||
int_subnet['subnet']['id'],
|
|
||||||
None)
|
|
||||||
# make sure snat rules are added
|
|
||||||
add_nat.assert_called_once()
|
add_nat.assert_called_once()
|
||||||
|
|
||||||
|
def test_router_address_scope_change(self):
|
||||||
|
self._test_router_address_scope_change()
|
||||||
|
|
||||||
|
def test_router_address_scope_gw_change(self):
|
||||||
|
self._test_router_address_scope_change(change_gw=True)
|
||||||
|
|
||||||
|
def _test_3leg_router_address_scope_change(self, change_gw=False,
|
||||||
|
change_2gw=False):
|
||||||
|
"""Test address scope change scenarios with router that covers
|
||||||
|
3 address scopes
|
||||||
|
"""
|
||||||
|
# create an external network on one address scope
|
||||||
|
with self.address_scope(name='as1') as as1, \
|
||||||
|
self.address_scope(name='as2') as as2, \
|
||||||
|
self.address_scope(name='as3') as as3, \
|
||||||
|
self.network() as ext_net:
|
||||||
|
ext_subnet = self._prepare_external_subnet_on_address_scope(
|
||||||
|
ext_net, as1)
|
||||||
|
as1_id = as1['address_scope']['id']
|
||||||
|
|
||||||
|
# create a router with this gateway
|
||||||
|
with self.router() as r:
|
||||||
|
self._add_external_gateway_to_router(
|
||||||
|
r['router']['id'],
|
||||||
|
ext_subnet['network_id'])
|
||||||
|
|
||||||
|
# create a regular network on address scope 2
|
||||||
|
# and verify snat change
|
||||||
|
as2_id = as2['address_scope']['id']
|
||||||
|
subnet2 = netaddr.IPNetwork('20.10.10.0/24')
|
||||||
|
subnetpool2 = self._test_create_subnetpool(
|
||||||
|
[subnet2.cidr], name='sp2',
|
||||||
|
min_prefixlen='24', address_scope_id=as2_id)
|
||||||
|
subnetpool2_id = subnetpool2['subnetpool']['id']
|
||||||
|
self._create_subnet_and_assert_snat_rules(
|
||||||
|
subnetpool2_id, r['router']['id'], assert_snat_added=True)
|
||||||
|
|
||||||
|
# create a regular network on address scope 3
|
||||||
|
# verify no snat change
|
||||||
|
as3_id = as3['address_scope']['id']
|
||||||
|
subnet3 = netaddr.IPNetwork('30.10.10.0/24')
|
||||||
|
subnetpool3 = self._test_create_subnetpool(
|
||||||
|
[subnet3.cidr], name='sp2',
|
||||||
|
min_prefixlen='24', address_scope_id=as3_id)
|
||||||
|
subnetpool3_id = subnetpool3['subnetpool']['id']
|
||||||
|
self._create_subnet_and_assert_snat_rules(
|
||||||
|
subnetpool3_id, r['router']['id'], assert_snat_added=True)
|
||||||
|
|
||||||
|
with self._mock_add_snat_rule() as add_nat, \
|
||||||
|
self._mock_del_snat_rule() as del_nat:
|
||||||
|
|
||||||
|
if change_gw:
|
||||||
|
# change address scope of GW subnet
|
||||||
|
subnetpool_to_update = ext_subnet['subnetpool_id']
|
||||||
|
else:
|
||||||
|
subnetpool_to_update = subnetpool2_id
|
||||||
|
|
||||||
|
if change_2gw:
|
||||||
|
# change subnet2 to be in GW address scope
|
||||||
|
target_as = as1_id
|
||||||
|
else:
|
||||||
|
target_as = as3_id
|
||||||
|
|
||||||
|
data = {'subnetpool': {
|
||||||
|
'address_scope_id': target_as}}
|
||||||
|
|
||||||
|
req = self.new_update_request('subnetpools', data,
|
||||||
|
subnetpool_to_update)
|
||||||
|
req.get_response(self.api)
|
||||||
|
|
||||||
|
if change_gw:
|
||||||
|
# The test changed address scope of gw subnet.
|
||||||
|
# Both previous rules should be deleted,
|
||||||
|
# and one new rule for subnet2 should be added
|
||||||
|
del_nat.assert_called()
|
||||||
|
self.assertEqual(2, del_nat.call_count)
|
||||||
|
add_nat.assert_called_once()
|
||||||
|
else:
|
||||||
|
if change_2gw:
|
||||||
|
# The test changed address scope of subnet2 to be
|
||||||
|
# same as GW address scope.
|
||||||
|
# Snat rule for as2 will be deleted. No effect on as3
|
||||||
|
# rule.
|
||||||
|
del_nat.assert_called_once()
|
||||||
|
else:
|
||||||
|
# The test changed address scope of subnet2 to
|
||||||
|
# as3. Affected snat rule should be re-created.
|
||||||
|
del_nat.assert_called_once()
|
||||||
|
add_nat.assert_called_once()
|
||||||
|
|
||||||
|
def test_3leg_router_address_scope_change(self):
|
||||||
|
self._test_3leg_router_address_scope_change()
|
||||||
|
|
||||||
|
def test_3leg_router_address_scope_change_to_gw(self, change_2gw=True):
|
||||||
|
self._test_3leg_router_address_scope_change()
|
||||||
|
|
||||||
|
def test_3leg_router_gw_address_scope_change(self):
|
||||||
|
self._test_3leg_router_address_scope_change(change_gw=True)
|
||||||
|
|
||||||
|
def test_subnetpool_router_address_scope_change_no_effect(self):
|
||||||
|
"""When all router interfaces are allocated from same subnetpool,
|
||||||
|
changing address scope on this subnetpool should not affect snat rules.
|
||||||
|
"""
|
||||||
|
# create an external network on one address scope
|
||||||
|
with self.address_scope(name='as1') as addr_scope, \
|
||||||
|
self.network() as ext_net:
|
||||||
|
ext_subnet = self._prepare_external_subnet_on_address_scope(
|
||||||
|
ext_net, addr_scope)
|
||||||
|
|
||||||
|
# create a router with this gateway
|
||||||
|
with self.router() as r:
|
||||||
|
self._add_external_gateway_to_router(
|
||||||
|
r['router']['id'],
|
||||||
|
ext_subnet['network_id'])
|
||||||
|
|
||||||
|
# create a regular network on same address scope
|
||||||
|
# and verify no snat change
|
||||||
|
self._create_subnet_and_assert_snat_rules(
|
||||||
|
ext_subnet['subnetpool_id'], r['router']['id'])
|
||||||
|
|
||||||
|
with self.address_scope(name='as2') as addr_scope2,\
|
||||||
|
self._mock_add_snat_rule() as add_nat,\
|
||||||
|
self._mock_del_snat_rule() as delete_nat:
|
||||||
|
|
||||||
|
as2_id = addr_scope2['address_scope']['id']
|
||||||
|
# change address scope of the subnetpool
|
||||||
|
data = {'subnetpool': {
|
||||||
|
'address_scope_id': as2_id}}
|
||||||
|
req = self.new_update_request('subnetpools', data,
|
||||||
|
ext_subnet['subnetpool_id'])
|
||||||
|
req.get_response(self.api)
|
||||||
|
|
||||||
|
add_nat.assert_not_called()
|
||||||
|
delete_nat.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
class ExtGwModeTestCase(test_ext_gw_mode.ExtGwModeIntTestCase,
|
class ExtGwModeTestCase(test_ext_gw_mode.ExtGwModeIntTestCase,
|
||||||
L3NatTest):
|
L3NatTest):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user