NSX|V network creation with availability zones hints

Add support for availability zones hints on networks creation
The network dhcp service will be created on an edge that belongs to the
requested resource pool

Change-Id: I425a7586ff2f5b6ca885fe4fede51c676426d381
This commit is contained in:
Adit Sarfaty 2016-05-18 12:21:59 +03:00
parent b2858f8719
commit d56b3765fb
4 changed files with 126 additions and 21 deletions

View File

@ -148,6 +148,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
"advanced-service-providers",
"subnet_allocation",
"availability_zone",
"network_availability_zone",
"router_availability_zone"]
supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
@ -497,6 +498,10 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
pnet.SEGMENTATION_ID: binding.vlan_id}
for binding in bindings]
# update availability zones
network[az_ext.AVAILABILITY_ZONES] = (
self.get_network_availability_zones(context, network))
def _get_subnet_as_providers(self, context, subnet):
net_id = subnet.get('network_id')
if net_id is None:
@ -720,10 +725,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# is here only for overriding the original api
result = {}
for az in self._availability_zones_data.keys():
# Add this availability zone as a router resource
resource = 'router'
key = (az, resource)
result[key] = True
# Add this availability zone as a router & network resource
for resource in ('router', 'network'):
result[(az, resource)] = True
return result
def _validate_availability_zones_in_obj(self, context, resource_type,
@ -751,6 +755,16 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
raise az_ext.AvailabilityZoneNotFound(
availability_zone=diff.pop())
def get_network_resource_pool(self, context, network_id):
network = self.get_network(context, network_id)
if az_ext.AZ_HINTS in network:
for hint in network[az_ext.AZ_HINTS]:
# For now we use only the first hint
return self.get_res_pool_id_by_name(hint)
# return the default
return cfg.CONF.nsxv.resource_pool_id
def create_network(self, context, network):
net_data = network['network']
tenant_id = net_data['tenant_id']
@ -758,6 +772,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Process the provider network extension
provider_type = self._convert_to_transport_zones_dict(net_data)
self._validate_provider_create(context, net_data)
self._validate_availability_zones_in_obj(context, 'network', net_data)
net_data['id'] = str(uuid.uuid4())
external = net_data.get(ext_net_extn.EXTERNAL)
@ -839,6 +854,19 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
self._process_network_port_security_create(
context, net_data, new_net)
# update the network with the availability zone hints
if az_ext.AZ_HINTS in net_data:
self.validate_availability_zones(context, 'network',
net_data[az_ext.AZ_HINTS])
az_hints = az_ext.convert_az_list_to_string(
net_data[az_ext.AZ_HINTS])
super(NsxVPluginV2, self).update_network(context,
new_net['id'],
{'network': {az_ext.AZ_HINTS: az_hints}})
new_net[az_ext.AZ_HINTS] = az_hints
# still no availability zones until subnets creation
new_net[az_ext.AVAILABILITY_ZONES] = []
# DB Operations for setting the network as external
self._process_l3_create(context, new_net, net_data)
if (net_data.get(mpnet.SEGMENTS) and
@ -1960,16 +1988,37 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
super(NsxVPluginV2, self).delete_router(context, id)
router_driver.delete_router(context, id)
def _get_availability_zone_by_edge(self, context, edge_id):
resource_pool = nsxv_db.get_edge_resource_pool(
context.session, edge_id)
if resource_pool:
av_zone = self.get_res_pool_name_by_id(resource_pool)
return av_zone
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
attr.NETWORKS, ['_extend_availability_zone_hints'])
def _extend_availability_zone_hints(self, net_res, net_db):
net_res[az_ext.AZ_HINTS] = az_ext.convert_az_string_to_list(
net_db[az_ext.AZ_HINTS])
def get_network_availability_zones(self, context, net_db):
"""Return availability zones which a network belongs to."""
resource_id = (vcns_const.DHCP_EDGE_PREFIX + net_db["id"])[:36]
dhcp_edge_binding = nsxv_db.get_nsxv_router_binding(
context.session, resource_id)
if dhcp_edge_binding:
edge_id = dhcp_edge_binding['edge_id']
return [self._get_availability_zone_by_edge(context, edge_id)]
return []
def get_router_availability_zones(self, router):
"""Return availability zones which a router belongs to."""
context = n_context.get_admin_context()
edge_id = self._get_edge_id_by_rtr_id(context, router["id"])
if edge_id:
resource_pool = nsxv_db.get_edge_resource_pool(
context.session, edge_id)
if resource_pool:
av_zone = self.get_res_pool_name_by_id(resource_pool)
return [av_zone]
return [self._get_availability_zone_by_edge(context, edge_id)]
return []
def get_router(self, context, id, fields=None):

View File

@ -740,12 +740,13 @@ class EdgeManager(object):
self.nsxv_manager.delete_edge(
router_id, edge_id, jobdata=jobdata, dist=dist)
def _allocate_dhcp_edge_appliance(self, context, resource_id):
def _allocate_dhcp_edge_appliance(self, context, resource_id, res_pool):
resource_name = (vcns_const.DHCP_EDGE_PREFIX +
_uuid())[:vcns_const.EDGE_NAME_LEN]
self._allocate_edge_appliance(
context, resource_id, resource_name,
appliance_size=vcns_const.SERVICE_SIZE_MAPPING['dhcp'])
appliance_size=vcns_const.SERVICE_SIZE_MAPPING['dhcp'],
res_pool=res_pool)
def _free_dhcp_edge_appliance(self, context, network_id):
router_id = (vcns_const.DHCP_EDGE_PREFIX + network_id)[:36]
@ -989,12 +990,12 @@ class EdgeManager(object):
available_edge_ids.append(x)
return (conflict_edge_ids, available_edge_ids)
def _get_used_edges(self, context, subnet):
def _get_used_edges(self, context, subnet, resource_pool):
"""Returns conflicting and available edges for the subnet."""
conflicting = self.plugin._get_conflicting_networks_for_subnet(
context, subnet)
return self._get_available_edges(context, subnet['network_id'],
conflicting)
conflicting, resource_pool)
def remove_network_from_dhcp_edge(self, context, network_id, edge_id):
old_binding = nsxv_db.get_edge_vnic_binding(
@ -1045,8 +1046,11 @@ class EdgeManager(object):
nsxv_db.allocate_edge_vnic_with_tunnel_index(
context.session, edge_id, network_id)
def allocate_new_dhcp_edge(self, context, network_id, resource_id):
self._allocate_dhcp_edge_appliance(context, resource_id)
def allocate_new_dhcp_edge(self, context, network_id, resource_id,
res_pool):
if not res_pool:
res_pool = cfg.CONF.nsxv.resource_pool_id
self._allocate_dhcp_edge_appliance(context, resource_id, res_pool)
with locking.LockManager.get_lock('nsx-edge-pool'):
new_edge = nsxv_db.get_nsxv_router_binding(context.session,
resource_id)
@ -1062,6 +1066,7 @@ class EdgeManager(object):
If new edge was allocated, return resource_id, else return None
"""
res_pool = self.plugin.get_network_resource_pool(context, network_id)
# Check if the network has one related dhcp edge
resource_id = (vcns_const.DHCP_EDGE_PREFIX + network_id)[:36]
dhcp_edge_binding = nsxv_db.get_nsxv_router_binding(context.session,
@ -1072,7 +1077,8 @@ class EdgeManager(object):
with locking.LockManager.get_lock('nsx-edge-pool'):
edge_id = dhcp_edge_binding['edge_id']
(conflict_edge_ids,
available_edge_ids) = self._get_used_edges(context, subnet)
available_edge_ids) = self._get_used_edges(context, subnet,
res_pool)
LOG.debug("The available edges %s, the conflict edges %s "
"at present is using edge %s",
available_edge_ids, conflict_edge_ids, edge_id)
@ -1106,7 +1112,8 @@ class EdgeManager(object):
else:
with locking.LockManager.get_lock('nsx-edge-pool'):
(conflict_edge_ids,
available_edge_ids) = self._get_used_edges(context, subnet)
available_edge_ids) = self._get_used_edges(context, subnet,
res_pool)
LOG.debug('The available edges %s, the conflict edges %s',
available_edge_ids, conflict_edge_ids)
# There is available one
@ -1124,7 +1131,8 @@ class EdgeManager(object):
allocate_new_edge = True
if allocate_new_edge:
self.allocate_new_dhcp_edge(context, network_id, resource_id)
self.allocate_new_dhcp_edge(context, network_id, resource_id,
res_pool)
# If a new Edge was allocated, return resource_id
return resource_id
@ -1219,7 +1227,6 @@ class EdgeManager(object):
def configure_dhcp_for_vdr_network(
self, context, network_id, vdr_router_id):
# If network is already attached to a DHCP Edge, detach from it
resource_id = (vcns_const.DHCP_EDGE_PREFIX + network_id)[:36]
dhcp_edge_binding = nsxv_db.get_nsxv_router_binding(context.session,
@ -1241,8 +1248,10 @@ class EdgeManager(object):
context, dhcp_edge_id, resource_id, network_id)
else:
# Attach to DHCP Edge
resource_pool = self.plugin.get_network_resource_pool(context,
network_id)
dhcp_edge_id = self.allocate_new_dhcp_edge(
context, network_id, resource_id)
context, network_id, resource_id, resource_pool)
self.plugin.metadata_proxy_handler.configure_router_edge(
resource_id, context)

View File

@ -603,6 +603,46 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase):
self.assertTrue(fake_init_from_policy.called)
self.assertTrue(fake_dvs_update.called)
def test_create_network_with_bad_az_hint(self):
p = manager.NeutronManager.get_plugin()
ctx = context.get_admin_context()
data = {'network': {
'name': 'test-qos',
'tenant_id': self._tenant_id,
'port_security_enabled': False,
'admin_state_up': True,
'shared': False,
'availability_zone_hints': ['bad_hint']
}}
self.assertRaises(n_exc.NeutronException,
p.create_network,
ctx, data)
def test_create_network_with_az_hint(self):
p = manager.NeutronManager.get_plugin()
ctx = context.get_admin_context()
alter_pool_id = 'respool-7'
alter_pool_name = 'rs-7'
p._availability_zones_data = {'default': self.default_res_pool,
alter_pool_name: alter_pool_id}
data = {'network': {
'name': 'test-qos',
'tenant_id': self._tenant_id,
'port_security_enabled': False,
'admin_state_up': True,
'shared': False,
'availability_zone_hints': [alter_pool_name]
}}
# network creation should succeed
net = p.create_network(ctx, data)
self.assertEqual([alter_pool_name],
net['availability_zone_hints'])
# the availability zone is still empty until subnet creation
self.assertEqual([],
net['availability_zones'])
class TestVnicIndex(NsxVPluginV2TestCase,
test_vnic_index.VnicIndexDbTestCase):

View File

@ -84,6 +84,11 @@ class EdgeUtilsTestCaseMixin(testlib_api.SqlTestCase):
resource_pool=binding['resource_pool'])
class DummyPlugin(object):
def get_network_resource_pool(self, context, network_id):
return cfg.CONF.nsxv.resource_pool_id
class EdgeDHCPManagerTestCase(EdgeUtilsTestCaseMixin):
def setUp(self):
@ -115,6 +120,7 @@ class EdgeDHCPManagerTestCase(EdgeUtilsTestCaseMixin):
self._populate_vcns_router_binding(fake_edge_pool)
fake_network = self._create_network()
fake_subnet = self._create_subnet(fake_network['id'])
self.edge_manager.plugin = DummyPlugin()
with mock.patch.object(self.edge_manager,
'_get_used_edges', return_value=([], [])):
self.edge_manager.create_dhcp_edge_service(self.ctx,
@ -125,7 +131,8 @@ class EdgeDHCPManagerTestCase(EdgeUtilsTestCaseMixin):
self.nsxv_manager.update_edge.assert_called_once_with(
resource_id, 'edge-1', mock.ANY, None, jobdata=jobdata,
appliance_size=vcns_const.SERVICE_SIZE_MAPPING['dhcp'],
dist=False, set_errors=True, res_pool=None)
dist=False, set_errors=True,
res_pool=cfg.CONF.nsxv.resource_pool_id)
def test_get_random_available_edge(self):
available_edge_ids = ['edge-1', 'edge-2']