Merge "Handle port over-quota when allocating network for instance"
This commit is contained in:
commit
a435ccb992
|
@ -1118,6 +1118,10 @@ class SecurityGroupLimitExceeded(QuotaError):
|
|||
msg_fmt = _("Maximum number of security groups or rules exceeded")
|
||||
|
||||
|
||||
class PortLimitExceeded(QuotaError):
|
||||
msg_fmt = _("Maximum number of ports exceeded")
|
||||
|
||||
|
||||
class AggregateError(NovaException):
|
||||
msg_fmt = _("Aggregate %(aggregate_id)s: action '%(action)s' "
|
||||
"caused an error: %(reason)s.")
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import time
|
||||
|
||||
from neutronclient.common import exceptions as neutron_client_exc
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova.compute import flavors
|
||||
|
@ -143,6 +144,52 @@ class API(base.Base):
|
|||
|
||||
return nets
|
||||
|
||||
def _create_port(self, port_client, instance, network_id, port_req_body,
|
||||
fixed_ip=None, security_group_ids=None,
|
||||
available_macs=None, dhcp_opts=None):
|
||||
"""Attempts to create a port for the instance on the given network.
|
||||
|
||||
:param port_client: The client to use to create the port.
|
||||
:param instance: Create the port for the given instance.
|
||||
:param network_id: Create the port on the given network.
|
||||
:param port_req_body: Pre-populated port request. Should have the
|
||||
device_id, device_owner, and any required neutron extension values.
|
||||
:param fixed_ip: Optional fixed IP to use from the given network.
|
||||
:param security_group_ids: Optional list of security group IDs to
|
||||
apply to the port.
|
||||
:param available_macs: Optional set of available MAC addresses to use.
|
||||
:param dhcp_opts: Optional DHCP options.
|
||||
:returns: ID of the created port.
|
||||
:raises PortLimitExceeded: If neutron fails with an OverQuota error.
|
||||
"""
|
||||
try:
|
||||
if fixed_ip:
|
||||
port_req_body['port']['fixed_ips'] = [{'ip_address': fixed_ip}]
|
||||
port_req_body['port']['network_id'] = network_id
|
||||
port_req_body['port']['admin_state_up'] = True
|
||||
port_req_body['port']['tenant_id'] = instance['project_id']
|
||||
if security_group_ids:
|
||||
port_req_body['port']['security_groups'] = security_group_ids
|
||||
if available_macs is not None:
|
||||
if not available_macs:
|
||||
raise exception.PortNotFree(
|
||||
instance=instance['display_name'])
|
||||
mac_address = available_macs.pop()
|
||||
port_req_body['port']['mac_address'] = mac_address
|
||||
if dhcp_opts is not None:
|
||||
port_req_body['port']['extra_dhcp_opts'] = dhcp_opts
|
||||
port_id = port_client.create_port(port_req_body)['port']['id']
|
||||
LOG.debug(_('Successfully created port: %s') % port_id,
|
||||
instance=instance)
|
||||
return port_id
|
||||
except neutron_client_exc.NeutronClientException as e:
|
||||
LOG.exception(_('Neutron error creating port on network %s') %
|
||||
network_id, instance=instance)
|
||||
# NOTE(mriedem): OverQuota in neutron is a 409
|
||||
if e.status_code == 409:
|
||||
raise exception.PortLimitExceeded()
|
||||
raise
|
||||
|
||||
@refresh_cache
|
||||
def allocate_for_instance(self, context, instance, **kwargs):
|
||||
"""Allocate network resources for the instance.
|
||||
|
@ -281,26 +328,10 @@ class API(base.Base):
|
|||
port_client.update_port(port['id'], port_req_body)
|
||||
touched_port_ids.append(port['id'])
|
||||
else:
|
||||
fixed_ip = fixed_ips.get(network_id)
|
||||
if fixed_ip:
|
||||
port_req_body['port']['fixed_ips'] = [{'ip_address':
|
||||
fixed_ip}]
|
||||
port_req_body['port']['network_id'] = network_id
|
||||
port_req_body['port']['admin_state_up'] = True
|
||||
port_req_body['port']['tenant_id'] = instance['project_id']
|
||||
if security_group_ids:
|
||||
port_req_body['port']['security_groups'] = (
|
||||
security_group_ids)
|
||||
if available_macs is not None:
|
||||
if not available_macs:
|
||||
raise exception.PortNotFree(
|
||||
instance=instance['display_name'])
|
||||
mac_address = available_macs.pop()
|
||||
port_req_body['port']['mac_address'] = mac_address
|
||||
if dhcp_opts is not None:
|
||||
port_req_body['port']['extra_dhcp_opts'] = dhcp_opts
|
||||
created_port_ids.append(
|
||||
port_client.create_port(port_req_body)['port']['id'])
|
||||
created_port_ids.append(self._create_port(
|
||||
port_client, instance, network_id,
|
||||
port_req_body, fixed_ips.get(network_id),
|
||||
security_group_ids, available_macs, dhcp_opts))
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
for port_id in touched_port_ids:
|
||||
|
|
|
@ -725,13 +725,16 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||
self.moxed_client.create_port(
|
||||
MyComparator(port_req_body)).AndReturn({'port': port})
|
||||
else:
|
||||
NeutronOverQuota = exceptions.NeutronClientException(
|
||||
message="Quota exceeded for resources: ['port']",
|
||||
status_code=409)
|
||||
self.moxed_client.create_port(
|
||||
MyComparator(port_req_body)).AndRaise(
|
||||
Exception("fail to create port"))
|
||||
MyComparator(port_req_body)).AndRaise(NeutronOverQuota)
|
||||
index += 1
|
||||
self.moxed_client.delete_port('portid_' + self.nets2[0]['id'])
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(NEUTRON_CLIENT_EXCEPTION, api.allocate_for_instance,
|
||||
self.assertRaises(exception.PortLimitExceeded,
|
||||
api.allocate_for_instance,
|
||||
self.context, self.instance)
|
||||
|
||||
def test_allocate_for_instance_ex2(self):
|
||||
|
|
Loading…
Reference in New Issue