summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-09-05 16:13:29 +0000
committerGerrit Code Review <review@openstack.org>2013-09-05 16:13:29 +0000
commita435ccb992ac5d00829da7f38c6af40be86d4eda (patch)
treeac832abb91f681cbe8c27b703dff63284213fd85
parent09a26eb069b602340d04a4fee665016dce9908c1 (diff)
parent2d20b87aef5a7d00cb36826b8907032912fb17fc (diff)
downloadnova-2013.2.b3.tar.gz
Merge "Handle port over-quota when allocating network for instance"2013.2.b3
-rw-r--r--nova/exception.py4
-rw-r--r--nova/network/neutronv2/api.py71
-rw-r--r--nova/tests/network/test_neutronv2.py9
3 files changed, 61 insertions, 23 deletions
diff --git a/nova/exception.py b/nova/exception.py
index 535e53e623..8870b2d6cc 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -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.")
diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py
index 367408012f..70e582a950 100644
--- a/nova/network/neutronv2/api.py
+++ b/nova/network/neutronv2/api.py
@@ -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:
diff --git a/nova/tests/network/test_neutronv2.py b/nova/tests/network/test_neutronv2.py
index 3c901c59fc..fda60458c8 100644
--- a/nova/tests/network/test_neutronv2.py
+++ b/nova/tests/network/test_neutronv2.py
@@ -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):