diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index d1f39f90d7..f397cbe8ef 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -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. diff --git a/nova/tests/network/test_neutronv2.py b/nova/tests/network/test_neutronv2.py index 04615261cc..ea30b851bf 100644 --- a/nova/tests/network/test_neutronv2.py +++ b/nova/tests/network/test_neutronv2.py @@ -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()