Handle neutron-client conflict

When user tries to add stateless and stateful security
groups on same port, neutron raises SecurityGroupConflict (409),
but nova doesnot handle it and raises InternalServerError (500).

As it appears to be invalid operation from user, so user should get
the message that they are doing wrong.

This changes catches SecurityGroupConflict from neutron
client and raises newly added nova exception
SecurityGroupConnectionStateConflict with 409 error code.

Closes-Bug: #2056195
Change-Id: Ifad28fdd536ff0a4b30e786b2fcbc5a55987a13a
This commit is contained in:
Rajesh Tailor
2024-04-20 15:37:50 +05:30
parent 96268d4e7a
commit 88b661b078
4 changed files with 42 additions and 1 deletions

View File

@@ -441,7 +441,8 @@ class SecurityGroupActionController(wsgi.Controller):
except (exception.SecurityGroupNotFound,
exception.InstanceNotFound) as exp:
raise exc.HTTPNotFound(explanation=exp.format_message())
except exception.NoUniqueMatch as exp:
except (exception.NoUniqueMatch,
exception.SecurityGroupConnectionStateConflict) as exp:
raise exc.HTTPConflict(explanation=exp.format_message())
except exception.SecurityGroupCannotBeApplied as exp:
raise exc.HTTPBadRequest(explanation=exp.format_message())

View File

@@ -1056,6 +1056,12 @@ class SecurityGroupCannotBeApplied(Invalid):
" in order to apply security groups.")
class SecurityGroupConnectionStateConflict(Invalid):
code = 409
msg_fmt = _("Cannot apply both stateful and stateless security groups on "
"the same port.")
class NoUniqueMatch(NovaException):
msg_fmt = _("No Unique Match Found.")
code = 409

View File

@@ -649,6 +649,8 @@ def add_to_instance(context, instance, security_group_name):
except n_exc.NeutronClientException as e:
if e.status_code == 400:
raise exception.SecurityGroupCannotBeApplied(str(e))
elif e.status_code == 409:
raise exception.SecurityGroupConnectionStateConflict(str(e))
else:
raise e
except Exception:

View File

@@ -373,6 +373,38 @@ class TestNeutronDriver(test.NoDBTestCase):
self.mocked_client.update_port.assert_called_once_with(
port_id, {'port': {'security_groups': [sg_id]}})
def test_add_to_instance_with_conflicting_sg(self):
sg1_name = 'sg-stateful'
sg1_id = '85cc3048-abc3-43cc-89b3-377341426ac5'
port_id = 1
port_list = {'ports': [{'id': port_id, 'device_id': uuids.instance,
'fixed_ips': [{'ip_address': '10.0.0.1'}],
'port_security_enabled': True, 'security_groups': []}]}
self.mocked_client.list_ports.return_value = port_list
with mock.patch.object(neutronv20, 'find_resourceid_by_name_or_id',
return_value=sg1_id):
sg_api.add_to_instance(
self.context, objects.Instance(uuid=uuids.instance), sg1_name)
self.mocked_client.list_ports.assert_called_once_with(
device_id=uuids.instance)
self.mocked_client.update_port.assert_called_once_with(
port_id, {'port': {'security_groups': [sg1_id]}})
sg2_name = 'sg-stateless'
sg2_id = '85cc3048-abc3-43cc-89b3-377341426ac5'
port_id = 1
port_list = {'ports': [{'id': port_id, 'device_id': uuids.instance,
'fixed_ips': [{'ip_address': '10.0.0.1'}],
'port_security_enabled': True, 'security_groups': []}]}
self.mocked_client.list_ports.return_value = port_list
self.mocked_client.update_port.side_effect = (
n_exc.Conflict(message='error'))
with mock.patch.object(neutronv20, 'find_resourceid_by_name_or_id',
return_value=sg2_id):
self.assertRaises(exception.SecurityGroupConnectionStateConflict,
sg_api.add_to_instance, self.context,
objects.Instance(uuid=uuids.instance), sg2_name)
def test_add_to_instance_duplicate_sg_name(self):
sg_name = 'web_server'
with mock.patch.object(neutronv20, 'find_resourceid_by_name_or_id',