Fix duplicate VIP address error reporting

With the transition to supporting provider drivers, the error reporting
for cases where the user requests a VIP address that is already in use
was broken.

This patch corrects that such that the user will get a clear error
message.

Here is an example output from the CLI:
The VIP IP address is already in use: IP address 172.24.4.25 already allocated in subnet fb3df045-07f9-4bd5-a7fb-12b016e5f873 (HTTP 409) (Request-ID: req-cece42f5-6dc2-49f8-a551-c11fd1abeb81)

Closes-Bug: #2052682
Depends-On: https://review.opendev.org/c/openstack/octavia-lib/+/949546
Change-Id: I9ee1913e57a1addd623c993d9c9083fad9f2bb33
This commit is contained in:
Michael Johnson
2025-05-13 00:46:56 +00:00
parent 4db66dd06d
commit e6fb722312
8 changed files with 57 additions and 0 deletions

View File

@@ -127,6 +127,12 @@ class AmphoraProviderDriver(driver_base.ProviderDriver):
try:
vip, add_vips = network_driver.allocate_vip(lb_obj)
except network_base.VIPInUseException as e:
message = str(e)
if getattr(e, 'orig_msg', None) is not None:
message = e.orig_msg
raise exceptions.Conflict(user_fault_string=message,
operator_fault_string=message)
except network_base.AllocateVIPException as e:
message = str(e)
if getattr(e, 'orig_msg', None) is not None:

View File

@@ -51,6 +51,10 @@ def call_provider(provider, driver_method, *args, **kwargs):
try:
return driver_method(*args, **kwargs)
except lib_exceptions.Conflict as e:
LOG.info("Provider '%s' raised a conflict error: %s",
provider, e.operator_fault_string)
raise exceptions.VIPAddressConflict(msg=e.user_fault_string)
except lib_exceptions.DriverError as e:
LOG.exception("Provider '%s' raised a driver error: %s",
provider, e.operator_fault_string)

View File

@@ -440,3 +440,8 @@ class ListenerNoChildren(APIException):
class MemberSRIOVDisabled(APIException):
msg = _('The load balancer flavor does not allow SR-IOV member ports.')
code = 400
class VIPAddressConflict(APIException):
msg = _('The VIP IP address is already in use: %(msg)s')
code = 409

View File

@@ -591,6 +591,15 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
port[constants.FIXED_IPS] = fixed_ips
try:
new_port = self.network_proxy.create_port(**port)
except os_exceptions.ConflictException as e:
message = _('Error creating neutron port on network '
'{network_id} due to {e}.').format(
network_id=load_balancer.vip.network_id, e=repr(e))
raise base.VIPInUseException(
message,
orig_msg=getattr(e, 'details', None),
orig_code=getattr(e, constants.STATUS_CODE, None),
)
except Exception as e:
message = _('Error creating neutron port on network '
'{network_id} due to {e}.').format(

View File

@@ -74,6 +74,19 @@ class TestAmphoraDriver(base.TestRpc):
self.sample_data.provider_vip_dict,
[self.sample_data.provider_additional_vip_dict])
@mock.patch('octavia.common.utils.get_network_driver')
def test_create_vip_port_conflict(self, mock_get_net_driver):
mock_net_driver = mock.MagicMock()
mock_get_net_driver.return_value = mock_net_driver
mock_net_driver.allocate_vip.side_effect = (
network_base.VIPInUseException())
self.assertRaises(exceptions.Conflict,
self.amp_driver.create_vip_port,
self.sample_data.lb_id, self.sample_data.project_id,
self.sample_data.provider_vip_dict,
[self.sample_data.provider_additional_vip_dict])
# Load Balancer
@mock.patch('oslo_messaging.RPCClient.cast')
def test_loadbalancer_create(self, mock_cast):

View File

@@ -39,6 +39,12 @@ class TestUtils(base.TestCase):
"arg1", foo="arg2")
mock_driver_method.assert_called_with("arg1", foo="arg2")
# Test driver raising VIPAddressConflict
mock_driver_method.side_effect = lib_exceptions.Conflict
self.assertRaises(exceptions.VIPAddressConflict,
utils.call_provider, "provider_name",
mock_driver_method)
# Test driver raising DriverError
mock_driver_method.side_effect = lib_exceptions.DriverError
self.assertRaises(exceptions.ProviderDriverError,

View File

@@ -700,6 +700,15 @@ class TestAllowedAddressPairsDriver(base.TestCase):
self.assertRaises(network_base.AllocateVIPException,
self.driver.allocate_vip, fake_lb)
def test_allocate_vip_conflict(self):
fake_lb_vip = data_models.Vip(
subnet_id=t_constants.MOCK_SUBNET_ID)
fake_lb = data_models.LoadBalancer(id='1', vip=fake_lb_vip)
create_port = self.driver.network_proxy.create_port
create_port.side_effect = os_exceptions.ConflictException
self.assertRaises(network_base.VIPInUseException,
self.driver.allocate_vip, fake_lb)
def test_allocate_vip_when_port_creation_fails(self):
fake_lb_vip = data_models.Vip(
subnet_id=t_constants.MOCK_SUBNET_ID)

View File

@@ -0,0 +1,5 @@
---
fixes:
- |
Fixes the error reporting when a user requests a VIP IP address that is
already in use.