Stop masking VIP allocate neutron errors

Previously the neutron network driver was masking the actual error being
returned from neutron while doing a VIP port allocation.
This patch will pass through the neutron error to the user if it is
an API ready exception giving the user more useful information about the
error neutron is reporting. For example that the requested VIP address is
already allocated.

Change-Id: Ic327b06ff9ccf9ae0c9931a8e6569c20d03c4a79
Closes-Bug: #1714593
Story: 1714593
Task: 5005
This commit is contained in:
Michael Johnson 2017-09-01 16:53:15 -07:00
parent 9df70995be
commit c1afc15863
4 changed files with 58 additions and 3 deletions

View File

@ -199,7 +199,16 @@ class LoadBalancersController(base.BaseController):
def _create_vip_port_if_not_exist(self, load_balancer_db):
"""Create vip port."""
network_driver = utils.get_network_driver()
vip = network_driver.allocate_vip(load_balancer_db)
try:
vip = network_driver.allocate_vip(load_balancer_db)
except Exception as e:
# Convert neutron style exception to octavia style
# if the error was API ready
if e.orig_code is not None:
e.code = e.orig_code
e.message = e.orig_msg
e.msg = e.orig_msg
raise e
return vip
@wsme_pecan.wsexpose(lb_types.LoadBalancerFullRootResponse,

View File

@ -31,11 +31,15 @@ class OctaviaException(Exception):
with the keyword arguments provided to the constructor.
"""
message = _("An unknown exception occurred.")
orig_msg = None
orig_code = None
def __init__(self, *args, **kwargs):
try:
if len(args) > 0:
self.message = args[0]
self.orig_msg = kwargs.get('orig_msg')
self.orig_code = kwargs.get('orig_code')
super(OctaviaException, self).__init__(self.message % kwargs)
self.msg = self.message % kwargs
except Exception:

View File

@ -350,12 +350,15 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
port['port']['fixed_ips'] = [fixed_ip]
try:
new_port = self.neutron_client.create_port(port)
except Exception:
except Exception as e:
message = _('Error creating neutron port on network '
'{network_id}.').format(
network_id=load_balancer.vip.network_id)
LOG.exception(message)
raise base.AllocateVIPException(message)
raise base.AllocateVIPException(
message,
orig_msg=getattr(e, 'message', None),
orig_code=getattr(e, 'status_code', None))
new_port = utils.convert_port_dict_to_model(new_port)
return self._port_to_vip(new_port, load_balancer)

View File

@ -433,6 +433,34 @@ class TestLoadBalancer(base.BaseAPITest):
self.assertEqual(network.id, api_lb.get('vip_network_id'))
self.assertEqual(port.id, api_lb.get('vip_port_id'))
def test_create_neutron_failure(self):
subnet = network_models.Subnet(id=uuidutils.generate_uuid())
network = network_models.Network(id=uuidutils.generate_uuid(),
subnets=[subnet])
port = network_models.Port(id=uuidutils.generate_uuid(),
network_id=network.id)
lb_json = {
'name': 'test1', 'description': 'test1_desc',
'vip_address': '10.0.0.1', 'vip_subnet_id': subnet.id,
'vip_network_id': network.id, 'vip_port_id': port.id,
'admin_state_up': False, 'project_id': self.project_id}
body = self._build_body(lb_json)
with mock.patch(
"octavia.network.drivers.noop_driver.driver.NoopManager"
".get_network") as mock_get_network, mock.patch(
"octavia.network.drivers.noop_driver.driver.NoopManager"
".get_port") as mock_get_port, mock.patch(
"octavia.network.drivers.noop_driver.driver.NoopManager"
".allocate_vip") as mock_allocate_vip:
mock_get_network.return_value = network
mock_get_port.return_value = port
mock_allocate_vip.side_effect = TestNeutronException(
"octavia_msg", "neutron_msg", 409)
response = self.post(self.LBS_PATH, body, status=409)
# Make sure the faultstring contains the neutron error and not
# the octavia error message
self.assertIn("neutron_msg", response.json.get("faultstring"))
def test_create_with_long_name(self):
lb_json = {'name': 'n' * 256,
'vip_subnet_id': uuidutils.generate_uuid(),
@ -2445,3 +2473,14 @@ class TestLoadBalancerGraph(base.BaseAPITest):
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json)
class TestNeutronException(Exception):
def __init__(self, message, orig_msg, orig_code):
self.message = message
self.orig_msg = orig_msg
self.orig_code = orig_code
def __str__(self):
return repr(self.message)