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:
@@ -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.")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user