Neutron: Check port binding status

Neutron api creates/uses a port which the port vif_type is
'binding_failed' without any verification, this leads to libvirt
driver failing to create instance, and cause vif driver to raise
a wrong exception message.

This patch adds a new exception PortBindingFailed to indicate port binding
failed. If port is newly created, delete it if PortBindingFailed exception
catched.

Closes-Bug: #1390336
Change-Id: I66763ff5293473d1283d55f76dfe33d153abaaec
This commit is contained in:
Eli Qiao
2015-08-13 15:02:40 +08:00
parent 003a2f6fcb
commit 7fc38d0c84
4 changed files with 71 additions and 1 deletions

View File

@@ -782,6 +782,11 @@ class PortNotFree(Invalid):
msg_fmt = _("No free port available for instance %(instance)s.")
class PortBindingFailed(Invalid):
msg_fmt = _("Binding failed for port %(port_id)s, please check neutron "
"logs for more information.")
class FixedIpExists(NovaException):
msg_fmt = _("Fixed ip %(address)s already exists.")

View File

@@ -45,6 +45,7 @@ VIF_TYPE_VROUTER = 'vrouter'
VIF_TYPE_OTHER = 'other'
VIF_TYPE_TAP = 'tap'
VIF_TYPE_MACVTAP = 'macvtap'
VIF_TYPE_BINDING_FAILED = 'binding_failed'
# Constants for dictionary keys in the 'vif_details' field in the VIF
# class

View File

@@ -274,6 +274,7 @@ class API(base_api.NetworkAPI):
:raises PortLimitExceeded: If neutron fails with an OverQuota error.
:raises NoMoreFixedIps: If neutron fails with
IpAddressGenerationFailure error.
:raises: PortBindingFailed: If port binding failed.
"""
try:
if fixed_ip:
@@ -292,7 +293,12 @@ class API(base_api.NetworkAPI):
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']
port = port_client.create_port(port_req_body)
port_id = port['port']['id']
if (port['port'].get('binding:vif_type') ==
network_model.VIF_TYPE_BINDING_FAILED):
port_client.delete_port(port_id)
raise exception.PortBindingFailed(port_id=port_id)
LOG.debug('Successfully created port: %s', port_id,
instance=instance)
return port_id
@@ -441,6 +447,12 @@ class API(base_api.NetworkAPI):
if port.get('device_id'):
raise exception.PortInUse(port_id=request.port_id)
# Make sure the port is usable
if (port.get('binding:vif_type') ==
network_model.VIF_TYPE_BINDING_FAILED):
raise exception.PortBindingFailed(
port_id=request.port_id)
if hypervisor_macs is not None:
if port['mac_address'] not in hypervisor_macs:
LOG.debug("Port %(port)s mac address %(mac)s is "

View File

@@ -3493,6 +3493,58 @@ class TestNeutronv2WithMock(test.TestCase):
mock_unbind.assert_called_once_with(mock.sentinel.ctx, ['2'],
mock_client)
@mock.patch('nova.network.neutronv2.api.API.'
'_check_external_network_attach')
@mock.patch('nova.network.neutronv2.api.API._has_port_binding_extension')
@mock.patch('nova.network.neutronv2.api.API.'
'_populate_neutron_extension_values')
@mock.patch('nova.network.neutronv2.api.API._get_available_networks')
@mock.patch('nova.network.neutronv2.api.get_client')
def test_port_binding_failed_created_port(self, mock_ntrn,
mock_avail_nets,
mock_ext_vals,
mock_has_pbe,
mock_cena):
mock_has_pbe.return_value = True
mock_nc = mock.Mock()
mock_ntrn.return_value = mock_nc
mock_inst = mock.Mock(project_id="proj-1",
availability_zone='zone-1',
uuid='inst-1')
mock_avail_nets.return_value = [{'id': 'net-1'}]
mock_nc.create_port.return_value = {'port': {'id': 'fake_id',
'tenant_id': mock_inst.project_id,
'binding:vif_type': 'binding_failed'}}
self.assertRaises(exception.PortBindingFailed,
self.api.allocate_for_instance,
mock.sentinel.ctx,
mock_inst)
mock_nc.delete_port.assert_called_once_with('fake_id')
@mock.patch('nova.network.neutronv2.api.API._show_port')
@mock.patch('nova.network.neutronv2.api.API._has_port_binding_extension')
@mock.patch('nova.network.neutronv2.api.get_client')
def test_port_binding_failed_with_request(self, mock_ntrn,
mock_has_pbe,
mock_show_port):
mock_has_pbe.return_value = True
mock_nc = mock.Mock()
mock_ntrn.return_value = mock_nc
mock_inst = mock.Mock(project_id="proj-1",
availability_zone='zone-1',
uuid='inst-1')
mock_show_port.return_value = {
'tenant_id': mock_inst.project_id,
'binding:vif_type': 'binding_failed'}
nw_req = objects.NetworkRequestList(
objects = [objects.NetworkRequest(port_id='fake_id')])
self.assertRaises(exception.PortBindingFailed,
self.api.allocate_for_instance,
mock.sentinel.ctx, mock_inst,
requested_networks=nw_req)
class TestNeutronv2ModuleMethods(test.NoDBTestCase):