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:
@@ -127,6 +127,12 @@ class AmphoraProviderDriver(driver_base.ProviderDriver):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
vip, add_vips = network_driver.allocate_vip(lb_obj)
|
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:
|
except network_base.AllocateVIPException as e:
|
||||||
message = str(e)
|
message = str(e)
|
||||||
if getattr(e, 'orig_msg', None) is not None:
|
if getattr(e, 'orig_msg', None) is not None:
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ def call_provider(provider, driver_method, *args, **kwargs):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return driver_method(*args, **kwargs)
|
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:
|
except lib_exceptions.DriverError as e:
|
||||||
LOG.exception("Provider '%s' raised a driver error: %s",
|
LOG.exception("Provider '%s' raised a driver error: %s",
|
||||||
provider, e.operator_fault_string)
|
provider, e.operator_fault_string)
|
||||||
|
|||||||
@@ -440,3 +440,8 @@ class ListenerNoChildren(APIException):
|
|||||||
class MemberSRIOVDisabled(APIException):
|
class MemberSRIOVDisabled(APIException):
|
||||||
msg = _('The load balancer flavor does not allow SR-IOV member ports.')
|
msg = _('The load balancer flavor does not allow SR-IOV member ports.')
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
|
|
||||||
|
class VIPAddressConflict(APIException):
|
||||||
|
msg = _('The VIP IP address is already in use: %(msg)s')
|
||||||
|
code = 409
|
||||||
|
|||||||
@@ -591,6 +591,15 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
|
|||||||
port[constants.FIXED_IPS] = fixed_ips
|
port[constants.FIXED_IPS] = fixed_ips
|
||||||
try:
|
try:
|
||||||
new_port = self.network_proxy.create_port(**port)
|
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:
|
except Exception as e:
|
||||||
message = _('Error creating neutron port on network '
|
message = _('Error creating neutron port on network '
|
||||||
'{network_id} due to {e}.').format(
|
'{network_id} due to {e}.').format(
|
||||||
|
|||||||
@@ -74,6 +74,19 @@ class TestAmphoraDriver(base.TestRpc):
|
|||||||
self.sample_data.provider_vip_dict,
|
self.sample_data.provider_vip_dict,
|
||||||
[self.sample_data.provider_additional_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
|
# Load Balancer
|
||||||
@mock.patch('oslo_messaging.RPCClient.cast')
|
@mock.patch('oslo_messaging.RPCClient.cast')
|
||||||
def test_loadbalancer_create(self, mock_cast):
|
def test_loadbalancer_create(self, mock_cast):
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ class TestUtils(base.TestCase):
|
|||||||
"arg1", foo="arg2")
|
"arg1", foo="arg2")
|
||||||
mock_driver_method.assert_called_with("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
|
# Test driver raising DriverError
|
||||||
mock_driver_method.side_effect = lib_exceptions.DriverError
|
mock_driver_method.side_effect = lib_exceptions.DriverError
|
||||||
self.assertRaises(exceptions.ProviderDriverError,
|
self.assertRaises(exceptions.ProviderDriverError,
|
||||||
|
|||||||
@@ -700,6 +700,15 @@ class TestAllowedAddressPairsDriver(base.TestCase):
|
|||||||
self.assertRaises(network_base.AllocateVIPException,
|
self.assertRaises(network_base.AllocateVIPException,
|
||||||
self.driver.allocate_vip, fake_lb)
|
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):
|
def test_allocate_vip_when_port_creation_fails(self):
|
||||||
fake_lb_vip = data_models.Vip(
|
fake_lb_vip = data_models.Vip(
|
||||||
subnet_id=t_constants.MOCK_SUBNET_ID)
|
subnet_id=t_constants.MOCK_SUBNET_ID)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Fixes the error reporting when a user requests a VIP IP address that is
|
||||||
|
already in use.
|
||||||
Reference in New Issue
Block a user