NSX|V Do not share edges between tenants
Adding a configuration option to prevent DHCP/Router edges sharing between different tenants. Also adding admin utilities for upgrade - redistribute the dhcp edges and router edges if the configuration changed: nsxadmin -r dhcp-binding -o nsx-redistribute nsxadmin -r routers -o nsx-redistribute Change-Id: I0d669c60413172a94ea5fc0beba0035df72c62ac
This commit is contained in:
parent
1222be4620
commit
5d934b23fd
@ -161,6 +161,10 @@ DHCP Bindings
|
||||
|
||||
nsxadmin -r dhcp-binding -o nsx-recreate --property net-id=5253ae45-75b4-4489-8aa1-6a9e1cfa80a6
|
||||
|
||||
- Redistribute networks on dhcp edges (for example when configuration of share_edges_between_tenants changes)::
|
||||
|
||||
nsxadmin -r dhcp-binding -o nsx-redistribute
|
||||
|
||||
Routers
|
||||
~~~~~~~
|
||||
- Recreate a router edge by moving the router/s to other edge/s::
|
||||
@ -171,6 +175,10 @@ Routers
|
||||
|
||||
nsxadmin -r routers -o nsx-recreate --property router-id=8cdd6d06-b457-4cbb-a0b1-41e08ccce287
|
||||
|
||||
- Redistribute shared routers on edges (for example when configuration of share_edges_between_tenants changes)::
|
||||
|
||||
nsxadmin -r routers -o nsx-redistribute
|
||||
|
||||
- Migrate NSXv metadata infrastructure for VDRs - use regular DHCP edges for VDR::
|
||||
|
||||
nsxadmin -r routers -o migrate-vdr-dhcp
|
||||
|
@ -695,6 +695,10 @@ nsxv_opts = [
|
||||
cfg.IntOpt('nsx_transaction_timeout',
|
||||
default=120,
|
||||
help=_("Timeout interval for NSX backend transactions.")),
|
||||
cfg.BoolOpt('share_edges_between_tenants',
|
||||
default=True,
|
||||
help=_("If False, different tenants will not use the same "
|
||||
"DHCP edge or router edge.")),
|
||||
]
|
||||
|
||||
# define the configuration of each NSX-V availability zone.
|
||||
|
@ -375,6 +375,8 @@ class RouterSharedDriver(router_driver.RouterBaseDriver):
|
||||
The router with static routes will be conflict with all other routers.
|
||||
The routers with different gateway will be conflict.
|
||||
The routers with overlapping interface will be conflict.
|
||||
In not share_edges_between_tenants: The routers of different tenants
|
||||
will be in conflict with the router
|
||||
"""
|
||||
# 1. Check gateway
|
||||
# 2. Check subnet interface
|
||||
@ -391,6 +393,7 @@ class RouterSharedDriver(router_driver.RouterBaseDriver):
|
||||
router_dict = {}
|
||||
router_dict['id'] = r['id']
|
||||
router_dict['gateway'] = None
|
||||
router_dict['tenant_id'] = r['tenant_id']
|
||||
for gwp in gw_ports:
|
||||
if gwp['id'] == r['gw_port_id']:
|
||||
try:
|
||||
@ -446,7 +449,12 @@ class RouterSharedDriver(router_driver.RouterBaseDriver):
|
||||
if (conflict_ip_set & ip_set):
|
||||
conflict_routers.append(r['id'])
|
||||
else:
|
||||
available_routers.append(r['id'])
|
||||
if (not cfg.CONF.nsxv.share_edges_between_tenants and
|
||||
src_router_dict['tenant_id'] != r['tenant_id']):
|
||||
# routers of other tenants are conflicting
|
||||
conflict_routers.append(r['id'])
|
||||
else:
|
||||
available_routers.append(r['id'])
|
||||
else:
|
||||
conflict_routers.append(r['id'])
|
||||
|
||||
|
@ -2607,17 +2607,29 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
return conflict_network_ids
|
||||
|
||||
def _get_conflicting_networks_for_subnet(self, context, subnet):
|
||||
network_id = subnet['network_id']
|
||||
"""Return a list if networks IDs conflicting with requested subnet
|
||||
|
||||
The requested subnet cannot be placed on the same DHCP edge as the
|
||||
conflicting networks.
|
||||
A network will be conflicting with the current subnet if:
|
||||
1. overlapping ips
|
||||
2. provider networks with different physical network
|
||||
3. flat provider network with any other flat network
|
||||
4. if not share_edges_between_tenants: networks of different tenants
|
||||
|
||||
"""
|
||||
subnet_net = subnet['network_id']
|
||||
subnet_tenant = subnet['tenant_id']
|
||||
# The DHCP for network with different physical network can not be used
|
||||
# The flat network should be located in different DHCP
|
||||
conflicting_networks = []
|
||||
network_ids = self.get_networks(context.elevated(),
|
||||
fields=['id'])
|
||||
phy_net = nsxv_db.get_network_bindings(context.session, network_id)
|
||||
all_networks = self.get_networks(context.elevated(),
|
||||
fields=['id', 'tenant_id'])
|
||||
phy_net = nsxv_db.get_network_bindings(context.session, subnet_net)
|
||||
if phy_net:
|
||||
binding_type = phy_net[0]['binding_type']
|
||||
phy_uuid = phy_net[0]['phy_uuid']
|
||||
for net_id in network_ids:
|
||||
for net_id in all_networks:
|
||||
p_net = nsxv_db.get_network_bindings(context.session,
|
||||
net_id['id'])
|
||||
if (p_net and binding_type == p_net[0]['binding_type']
|
||||
@ -2625,15 +2637,23 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
conflicting_networks.append(net_id['id'])
|
||||
elif (p_net and phy_uuid != p_net[0]['phy_uuid']):
|
||||
conflicting_networks.append(net_id['id'])
|
||||
|
||||
# get conflicting networks of other tenants
|
||||
if not cfg.CONF.nsxv.share_edges_between_tenants:
|
||||
for another_net in all_networks:
|
||||
if (another_net['id'] != subnet_net and
|
||||
another_net['tenant_id'] != subnet_tenant):
|
||||
conflicting_networks.append(another_net['id'])
|
||||
|
||||
# get all of the subnets on the network, there may be more than one
|
||||
filters = {'network_id': [network_id]}
|
||||
filters = {'network_id': [subnet_net]}
|
||||
subnets = super(NsxVPluginV2, self).get_subnets(context.elevated(),
|
||||
filters=filters)
|
||||
# Query all networks with overlap subnet
|
||||
if cfg.CONF.allow_overlapping_ips:
|
||||
conflicting_networks.extend(
|
||||
self._get_conflict_network_ids_by_overlapping(
|
||||
context, subnets))
|
||||
context.elevated(), subnets))
|
||||
|
||||
conflicting_networks = list(set(conflicting_networks))
|
||||
return conflicting_networks
|
||||
|
@ -202,7 +202,7 @@ def recreate_network_dhcp(context, plugin, edge_manager, old_edge_id, net_id):
|
||||
|
||||
@admin_utils.output_header
|
||||
def nsx_recreate_dhcp_edge(resource, event, trigger, **kwargs):
|
||||
"""Recreate a dhcp edge with all the networks n a new NSXv edge"""
|
||||
"""Recreate a dhcp edge with all the networks on a new NSXv edge"""
|
||||
usage_msg = ("Need to specify edge-id or net-id parameter")
|
||||
if not kwargs.get('property'):
|
||||
LOG.error(usage_msg)
|
||||
@ -250,7 +250,6 @@ def nsx_recreate_dhcp_edge(resource, event, trigger, **kwargs):
|
||||
# Delete the old edge
|
||||
delete_old_dhcp_edge(context, old_edge_id, bindings)
|
||||
|
||||
# This is a regular DHCP edge:
|
||||
# Move all the networks to other (new or existing) edge
|
||||
for net_id in network_ids:
|
||||
recreate_network_dhcp(context, plugin, edge_manager,
|
||||
@ -305,6 +304,47 @@ def nsx_recreate_dhcp_edge_by_net_id(net_id):
|
||||
None, net_id)
|
||||
|
||||
|
||||
@admin_utils.output_header
|
||||
def nsx_redistribute_dhcp_edges(resource, event, trigger, **kwargs):
|
||||
"""If any of the DHCP networks are on a conflicting edge move them"""
|
||||
context = n_context.get_admin_context()
|
||||
with utils.NsxVPluginWrapper() as plugin:
|
||||
nsxv_manager = vcns_driver.VcnsDriver(
|
||||
edge_utils.NsxVCallbacks(plugin))
|
||||
edge_manager = edge_utils.EdgeManager(nsxv_manager, plugin)
|
||||
# go over all DHCP subnets
|
||||
networks = plugin.get_networks(context)
|
||||
for network in networks:
|
||||
network_id = network['id']
|
||||
# Check if the network has a related DHCP edge
|
||||
resource_id = (nsxv_constants.DHCP_EDGE_PREFIX + network_id)[:36]
|
||||
dhcp_edge_binding = nsxv_db.get_nsxv_router_binding(
|
||||
context.session, resource_id)
|
||||
if not dhcp_edge_binding:
|
||||
continue
|
||||
LOG.info("Checking network %s", network_id)
|
||||
edge_id = dhcp_edge_binding['edge_id']
|
||||
availability_zone = plugin.get_network_az_by_net_id(
|
||||
context, network['id'])
|
||||
filters = {'network_id': [network_id], 'enable_dhcp': [True]}
|
||||
subnets = plugin.get_subnets(context, filters=filters)
|
||||
for subnet in subnets:
|
||||
(conflict_edge_ids,
|
||||
available_edge_ids) = edge_manager._get_used_edges(
|
||||
context, subnet, availability_zone)
|
||||
if edge_id in conflict_edge_ids:
|
||||
# move the DHCP to another edge
|
||||
LOG.info("Network %(net)s on DHCP edge %(edge)s is "
|
||||
"conflicting with another network and will be "
|
||||
"moved",
|
||||
{'net': network_id, 'edge': edge_id})
|
||||
edge_manager.remove_network_from_dhcp_edge(
|
||||
context, network_id, edge_id)
|
||||
edge_manager.create_dhcp_edge_service(
|
||||
context, network_id, subnet)
|
||||
break
|
||||
|
||||
|
||||
registry.subscribe(list_missing_dhcp_bindings,
|
||||
constants.DHCP_BINDING,
|
||||
shell.Operations.LIST.value)
|
||||
@ -314,3 +354,6 @@ registry.subscribe(nsx_update_dhcp_edge_binding,
|
||||
registry.subscribe(nsx_recreate_dhcp_edge,
|
||||
constants.DHCP_BINDING,
|
||||
shell.Operations.NSX_RECREATE.value)
|
||||
registry.subscribe(nsx_redistribute_dhcp_edges,
|
||||
constants.DHCP_BINDING,
|
||||
shell.Operations.NSX_REDISTRIBURE.value)
|
||||
|
@ -210,6 +210,40 @@ def migrate_distributed_routers_dhcp(resource, event, trigger, **kwargs):
|
||||
nsxv.update_routes(edge_id, route_obj)
|
||||
|
||||
|
||||
def is_router_conflicting_on_edge(context, driver, router_id):
|
||||
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
||||
if not edge_id:
|
||||
return False
|
||||
(available_routers,
|
||||
conflict_routers) = driver._get_available_and_conflicting_ids(
|
||||
context, router_id)
|
||||
for conf_router in conflict_routers:
|
||||
conf_edge_id = edge_utils.get_router_edge_id(context, conf_router)
|
||||
if conf_edge_id == edge_id:
|
||||
LOG.info("Router %(rtr)s on edge %(edge)s is conflicting with "
|
||||
"another router and will be moved",
|
||||
{'rtr': router_id, 'edge': edge_id})
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@admin_utils.output_header
|
||||
def redistribute_routers(resource, event, trigger, **kwargs):
|
||||
"""If any of the shared routers are on a conflicting edge move them"""
|
||||
context = n_context.get_admin_context()
|
||||
with utils.NsxVPluginWrapper() as plugin:
|
||||
router_driver = plugin._router_managers.get_tenant_router_driver(
|
||||
context, 'shared')
|
||||
routers = plugin.get_routers(context)
|
||||
for router in routers:
|
||||
if (not router.get('distributed', False) and
|
||||
router.get('router_type') == 'shared' and
|
||||
is_router_conflicting_on_edge(
|
||||
context, router_driver, router['id'])):
|
||||
router_driver.detach_router(context, router['id'], router)
|
||||
router_driver.attach_router(context, router['id'], router)
|
||||
|
||||
|
||||
@admin_utils.output_header
|
||||
def list_orphaned_vnics(resource, event, trigger, **kwargs):
|
||||
"""List router orphaned router vnics where the port was deleted"""
|
||||
@ -308,6 +342,10 @@ registry.subscribe(migrate_distributed_routers_dhcp,
|
||||
constants.ROUTERS,
|
||||
shell.Operations.MIGRATE_VDR_DHCP.value)
|
||||
|
||||
registry.subscribe(redistribute_routers,
|
||||
constants.ROUTERS,
|
||||
shell.Operations.NSX_REDISTRIBURE.value)
|
||||
|
||||
registry.subscribe(list_orphaned_vnics,
|
||||
constants.ORPHANED_VNICS,
|
||||
shell.Operations.NSX_LIST.value)
|
||||
|
@ -52,6 +52,7 @@ class Operations(enum.Enum):
|
||||
NSX_UPDATE_DHCP_RELAY = 'nsx-update-dhcp-relay'
|
||||
NSX_UPDATE_IP = 'nsx-update-ip'
|
||||
NSX_RECREATE = 'nsx-recreate'
|
||||
NSX_REDISTRIBURE = 'nsx-redistribute'
|
||||
NSX_REORDER = 'nsx-reorder'
|
||||
NSX_TAG_DEFAULT = 'nsx-tag-default'
|
||||
MIGRATE_TO_DYNAMIC_CRITERIA = 'migrate-to-dynamic-criteria'
|
||||
@ -160,6 +161,7 @@ nsxv_resources = {
|
||||
constants.DHCP_BINDING: Resource(constants.DHCP_BINDING,
|
||||
[Operations.LIST.value,
|
||||
Operations.NSX_UPDATE.value,
|
||||
Operations.NSX_REDISTRIBURE.value,
|
||||
Operations.NSX_RECREATE.value]),
|
||||
constants.NETWORKS: Resource(constants.NETWORKS,
|
||||
[Operations.LIST.value,
|
||||
@ -187,6 +189,7 @@ nsxv_resources = {
|
||||
Operations.STATUS.value]),
|
||||
constants.ROUTERS: Resource(constants.ROUTERS,
|
||||
[Operations.NSX_RECREATE.value,
|
||||
Operations.NSX_REDISTRIBURE.value,
|
||||
Operations.MIGRATE_VDR_DHCP.value]),
|
||||
constants.ORPHANED_VNICS: Resource(constants.ORPHANED_VNICS,
|
||||
[Operations.NSX_LIST.value,
|
||||
|
@ -1866,6 +1866,50 @@ class TestSubnetsV2(NsxVPluginV2TestCase,
|
||||
self.context.session, router_id)['edge_id']
|
||||
self.assertNotEqual(dhcp_server_id, dhcp_server_id_1)
|
||||
|
||||
def test_create_subnets_with_different_tenants_non_shared(self):
|
||||
cfg.CONF.set_override('share_edges_between_tenants', False,
|
||||
group="nsxv")
|
||||
self.mock_create_dhcp_service.stop()
|
||||
# create 2 networks with different tenants
|
||||
with self.network(name='net1', tenant_id='fake1') as net1,\
|
||||
self.network(name='net2', tenant_id='fake2') as net2:
|
||||
# create 2 non-overlapping subnets
|
||||
self._test_create_subnet(network=net1, cidr='10.0.0.0/24')
|
||||
router_id1 = (vcns_const.DHCP_EDGE_PREFIX +
|
||||
net1['network']['id'])[:36]
|
||||
edge1 = nsxv_db.get_nsxv_router_binding(
|
||||
self.context.session, router_id1)['edge_id']
|
||||
|
||||
self._test_create_subnet(network=net2, cidr='20.0.0.0/24')
|
||||
router_id2 = (vcns_const.DHCP_EDGE_PREFIX +
|
||||
net2['network']['id'])[:36]
|
||||
edge2 = nsxv_db.get_nsxv_router_binding(
|
||||
self.context.session, router_id2)['edge_id']
|
||||
# make sure we have 2 separate dhcp edges
|
||||
self.assertNotEqual(edge1, edge2)
|
||||
|
||||
def test_create_subnets_with_different_tenants_shared(self):
|
||||
cfg.CONF.set_override('share_edges_between_tenants', True,
|
||||
group="nsxv")
|
||||
self.mock_create_dhcp_service.stop()
|
||||
# create 2 networks with different tenants
|
||||
with self.network(name='net1', tenant_id='fake1') as net1,\
|
||||
self.network(name='net2', tenant_id='fake2') as net2:
|
||||
# create 2 non-overlapping subnets
|
||||
self._test_create_subnet(network=net1, cidr='10.0.0.0/24')
|
||||
router_id1 = (vcns_const.DHCP_EDGE_PREFIX +
|
||||
net1['network']['id'])[:36]
|
||||
edge1 = nsxv_db.get_nsxv_router_binding(
|
||||
self.context.session, router_id1)['edge_id']
|
||||
|
||||
self._test_create_subnet(network=net2, cidr='20.0.0.0/24')
|
||||
router_id2 = (vcns_const.DHCP_EDGE_PREFIX +
|
||||
net2['network']['id'])[:36]
|
||||
edge2 = nsxv_db.get_nsxv_router_binding(
|
||||
self.context.session, router_id2)['edge_id']
|
||||
# make sure we have both networks on the same dhcp edges
|
||||
self.assertEqual(edge1, edge2)
|
||||
|
||||
def test_create_subnet_ipv6_slaac_with_db_reference_error(self):
|
||||
self.skipTest('Currently not supported')
|
||||
|
||||
@ -5434,6 +5478,29 @@ class TestSharedRouterTestCase(L3NatTest, L3NatTestCaseBase,
|
||||
self.assertIn(r2['router']['id'], conflict_router_ids)
|
||||
self.assertEqual(0, len(available_router_ids))
|
||||
|
||||
def test_get_available_and_conflicting_ids_with_tenants(self):
|
||||
cfg.CONF.set_override('share_edges_between_tenants', False,
|
||||
group="nsxv")
|
||||
with self.router(tenant_id='fake1') as r1,\
|
||||
self.router(tenant_id='fake2') as r2,\
|
||||
self.subnet(cidr='11.0.0.0/24') as s1,\
|
||||
self.subnet(cidr='12.0.0.0/24') as s2:
|
||||
self._router_interface_action('add',
|
||||
r1['router']['id'],
|
||||
s1['subnet']['id'],
|
||||
None)
|
||||
self._router_interface_action('add',
|
||||
r2['router']['id'],
|
||||
s2['subnet']['id'],
|
||||
None)
|
||||
router_driver = (self.plugin_instance._router_managers.
|
||||
get_tenant_router_driver(context, 'shared'))
|
||||
available_router_ids, conflict_router_ids = (
|
||||
router_driver._get_available_and_conflicting_ids(
|
||||
context.get_admin_context(), r1['router']['id']))
|
||||
self.assertIn(r2['router']['id'], conflict_router_ids)
|
||||
self.assertEqual(0, len(available_router_ids))
|
||||
|
||||
def test_migrate_shared_router_to_exclusive(self):
|
||||
with self.router(name='r7') as r1, \
|
||||
self.subnet(cidr='11.0.0.0/24') as s1:
|
||||
|
Loading…
Reference in New Issue
Block a user