Merge "Optimize validate_networks to query neutron only when needed"
This commit is contained in:
commit
e03a2c84cb
|
@ -602,7 +602,8 @@ class API(base_api.NetworkAPI):
|
|||
ports_needed_per_instance = 1
|
||||
|
||||
else:
|
||||
net_ids = []
|
||||
instance_on_net_ids = []
|
||||
net_ids_requested = []
|
||||
|
||||
for (net_id, _i, port_id) in requested_networks:
|
||||
if port_id:
|
||||
|
@ -624,46 +625,50 @@ class API(base_api.NetworkAPI):
|
|||
net_id = port['network_id']
|
||||
else:
|
||||
ports_needed_per_instance += 1
|
||||
net_ids_requested.append(net_id)
|
||||
|
||||
if net_id in net_ids:
|
||||
if net_id in instance_on_net_ids:
|
||||
raise exception.NetworkDuplicated(network_id=net_id)
|
||||
net_ids.append(net_id)
|
||||
instance_on_net_ids.append(net_id)
|
||||
|
||||
# Now check to see if all requested networks exist
|
||||
nets = self._get_available_networks(context,
|
||||
context.project_id, net_ids,
|
||||
neutron=neutron)
|
||||
for net in nets:
|
||||
if not net.get('subnets'):
|
||||
raise exception.NetworkRequiresSubnet(
|
||||
network_uuid=net['id'])
|
||||
if net_ids_requested:
|
||||
nets = self._get_available_networks(
|
||||
context, context.project_id, net_ids_requested,
|
||||
neutron=neutron)
|
||||
|
||||
if len(nets) != len(net_ids):
|
||||
requsted_netid_set = set(net_ids)
|
||||
returned_netid_set = set([net['id'] for net in nets])
|
||||
lostid_set = requsted_netid_set - returned_netid_set
|
||||
id_str = ''
|
||||
for _id in lostid_set:
|
||||
id_str = id_str and id_str + ', ' + _id or _id
|
||||
raise exception.NetworkNotFound(network_id=id_str)
|
||||
for net in nets:
|
||||
if not net.get('subnets'):
|
||||
raise exception.NetworkRequiresSubnet(
|
||||
network_uuid=net['id'])
|
||||
|
||||
if len(nets) != len(net_ids_requested):
|
||||
requested_netid_set = set(net_ids_requested)
|
||||
returned_netid_set = set([net['id'] for net in nets])
|
||||
lostid_set = requested_netid_set - returned_netid_set
|
||||
id_str = ''
|
||||
for _id in lostid_set:
|
||||
id_str = id_str and id_str + ', ' + _id or _id
|
||||
raise exception.NetworkNotFound(network_id=id_str)
|
||||
|
||||
# Note(PhilD): Ideally Nova would create all required ports as part of
|
||||
# network validation, but port creation requires some details
|
||||
# from the hypervisor. So we just check the quota and return
|
||||
# how many of the requested number of instances can be created
|
||||
|
||||
ports = neutron.list_ports(tenant_id=context.project_id)['ports']
|
||||
quotas = neutron.show_quota(tenant_id=context.project_id)['quota']
|
||||
if quotas.get('port') == -1:
|
||||
# Unlimited Port Quota
|
||||
return num_instances
|
||||
else:
|
||||
free_ports = quotas.get('port') - len(ports)
|
||||
ports_needed = ports_needed_per_instance * num_instances
|
||||
if free_ports >= ports_needed:
|
||||
if ports_needed_per_instance:
|
||||
ports = neutron.list_ports(tenant_id=context.project_id)['ports']
|
||||
quotas = neutron.show_quota(tenant_id=context.project_id)['quota']
|
||||
if quotas.get('port') == -1:
|
||||
# Unlimited Port Quota
|
||||
return num_instances
|
||||
else:
|
||||
return free_ports // ports_needed_per_instance
|
||||
free_ports = quotas.get('port') - len(ports)
|
||||
ports_needed = ports_needed_per_instance * num_instances
|
||||
if free_ports >= ports_needed:
|
||||
return num_instances
|
||||
else:
|
||||
return free_ports // ports_needed_per_instance
|
||||
return num_instances
|
||||
|
||||
def _get_instance_uuids_by_ip(self, context, address):
|
||||
"""Retrieve instance uuids associated with the given ip address.
|
||||
|
|
|
@ -1269,16 +1269,6 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||
(None, None, port_b['id'])]
|
||||
self.moxed_client.show_port(port_a['id']).AndReturn({'port': port_a})
|
||||
self.moxed_client.show_port(port_b['id']).AndReturn({'port': port_b})
|
||||
|
||||
search_opts = {'id': [port_a['network_id'], port_b['network_id']]}
|
||||
self.moxed_client.list_networks(
|
||||
**search_opts).AndReturn({'networks': self.nets2})
|
||||
self.moxed_client.list_ports(tenant_id='my_tenantid').AndReturn(
|
||||
{'ports': []})
|
||||
self.moxed_client.show_quota(
|
||||
tenant_id='my_tenantid').AndReturn(
|
||||
{'quota': {'port': 50}})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api = neutronapi.API()
|
||||
|
@ -1305,6 +1295,44 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||
requested_networks, 1)
|
||||
self.assertEqual(max_count, 0)
|
||||
|
||||
def test_validate_networks_with_ports_and_networks(self):
|
||||
# Test validation for a request for one instance needing
|
||||
# one port allocated via nova with another port being passed in.
|
||||
port_b = self.port_data2[1]
|
||||
port_b['device_id'] = None
|
||||
port_b['device_owner'] = None
|
||||
requested_networks = [('my_netid1', 'test', None),
|
||||
(None, None, port_b['id'])]
|
||||
self.moxed_client.show_port(port_b['id']).AndReturn({'port': port_b})
|
||||
ids = ['my_netid1']
|
||||
self.moxed_client.list_networks(
|
||||
id=mox.SameElementsAs(ids)).AndReturn(
|
||||
{'networks': self.nets1})
|
||||
self.moxed_client.list_ports(tenant_id='my_tenantid').AndReturn(
|
||||
{'ports': self.port_data2})
|
||||
self.moxed_client.show_quota(
|
||||
tenant_id='my_tenantid').AndReturn(
|
||||
{'quota': {'port': 5}})
|
||||
self.mox.ReplayAll()
|
||||
api = neutronapi.API()
|
||||
max_count = api.validate_networks(self.context,
|
||||
requested_networks, 1)
|
||||
self.assertEqual(max_count, 1)
|
||||
|
||||
def test_validate_networks_one_port_and_no_networks(self):
|
||||
# Test that show quota is not called if no networks are
|
||||
# passed in and only ports.
|
||||
port_b = self.port_data2[1]
|
||||
port_b['device_id'] = None
|
||||
port_b['device_owner'] = None
|
||||
requested_networks = [(None, None, port_b['id'])]
|
||||
self.moxed_client.show_port(port_b['id']).AndReturn({'port': port_b})
|
||||
self.mox.ReplayAll()
|
||||
api = neutronapi.API()
|
||||
max_count = api.validate_networks(self.context,
|
||||
requested_networks, 1)
|
||||
self.assertEqual(max_count, 1)
|
||||
|
||||
def test_validate_networks_some_quota(self):
|
||||
# Test validation for a request for two instance needing
|
||||
# two ports each, where the quota is 5 and 2 ports are in use
|
||||
|
@ -1348,10 +1376,6 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||
self.assertEqual(max_count, 2)
|
||||
|
||||
def test_validate_networks_no_quota_but_ports_supplied(self):
|
||||
# Test validation for a request for one instance needing
|
||||
# two ports, where the quota is 2 and 2 ports are in use
|
||||
# but the request includes a port to be used
|
||||
# => instances which can be created = 1
|
||||
port_a = self.port_data3[0]
|
||||
port_a['fixed_ips'] = {'ip_address': '10.0.0.2',
|
||||
'subnet_id': 'subnet_id'}
|
||||
|
@ -1366,15 +1390,6 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||
self.moxed_client.show_port(port_a['id']).AndReturn({'port': port_a})
|
||||
self.moxed_client.show_port(port_b['id']).AndReturn({'port': port_b})
|
||||
|
||||
search_opts = {'id': [port_a['network_id'], port_b['network_id']]}
|
||||
self.moxed_client.list_networks(
|
||||
**search_opts).AndReturn({'networks': self.nets2})
|
||||
self.moxed_client.list_ports(tenant_id='my_tenantid').AndReturn(
|
||||
{'ports': self.port_data2})
|
||||
self.moxed_client.show_quota(
|
||||
tenant_id='my_tenantid').AndReturn(
|
||||
{'quota': {'port': 2}})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api = neutronapi.API()
|
||||
|
|
Loading…
Reference in New Issue