Don't fail if a provider driver cannot be loaded in Octavia API

Fix an issue that prevents the Octavia API service to be correctly
initialized when it fails to load a provider driver.

When enabled_provider_drivers setting is not correctly configured (ex:
if it contains a non existing driver) or when a provider driver fails
to load, the exception was not caught by the Octavia API service, so the
service was not properly configured and the whole Octavia API was
unreachable.

Now the Octavia API service skips the driver initialization in case of
errors, removes the driver(s) from the enabled list, and the other
provider drivers are functional.

Story 2008710
Task 42044

Depends-on: https://review.opendev.org/c/openstack/octavia/+/964892
Change-Id: I34341e1aaad1524e3e0834309c5f6897f176af53
Signed-off-by: Zachary Raines <zachary.raines@canonical.com>
This commit is contained in:
Gregory Thiemonge
2021-03-11 18:50:06 +01:00
committed by Zachary Raines
parent ffd4fa757f
commit 6fc1abb780
5 changed files with 86 additions and 5 deletions

View File

@@ -44,8 +44,20 @@ def get_pecan_config():
def _init_drivers(): def _init_drivers():
"""Initialize provider drivers.""" """Initialize provider drivers."""
for provider in CONF.api_settings.enabled_provider_drivers: providers_to_remove = []
driver_factory.get_driver(provider) enabled_providers = driver_factory.get_providers()
for provider in enabled_providers:
try:
driver_factory.get_driver(provider)
except Exception:
LOG.exception("Cannot load driver '%s', will remove from "
"service. Please check "
"[api_settings]/enabled_provider_drivers in "
"octavia.conf for correctness.", provider)
providers_to_remove.append(provider)
if providers_to_remove:
driver_factory.remove_providers(providers_to_remove)
def setup_app(pecan_config=None, debug=False, argv=None): def setup_app(pecan_config=None, debug=False, argv=None):

View File

@@ -48,3 +48,14 @@ def get_driver(provider):
provider, str(e)) provider, str(e))
raise exceptions.ProviderNotFound(prov=provider) raise exceptions.ProviderNotFound(prov=provider)
return driver return driver
def remove_providers(providers):
# Presumably these provider drivers failed to load, remove so they do not
# show up in the available list
for provider in providers:
CONF.api_settings.enabled_provider_drivers.pop(provider)
def get_providers():
return CONF.api_settings.enabled_provider_drivers

View File

@@ -13,7 +13,6 @@
# under the License. # under the License.
from octavia_lib.api.drivers import exceptions as lib_exceptions from octavia_lib.api.drivers import exceptions as lib_exceptions
from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from pecan import expose as pecan_expose from pecan import expose as pecan_expose
from pecan import request as pecan_request from pecan import request as pecan_request
@@ -26,7 +25,6 @@ from octavia.api.v2.types import provider as provider_types
from octavia.common import constants from octavia.common import constants
from octavia.common import exceptions from octavia.common import exceptions
CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@@ -46,7 +44,7 @@ class ProviderController(base.BaseController):
self._auth_validate_action(context, context.project_id, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL) constants.RBAC_GET_ALL)
enabled_providers = CONF.api_settings.enabled_provider_drivers enabled_providers = driver_factory.get_providers()
response_list = [ response_list = [
provider_types.ProviderResponse(name=key, description=value) for provider_types.ProviderResponse(name=key, description=value) for
key, value in enabled_providers.items()] key, value in enabled_providers.items()]

View File

@@ -0,0 +1,54 @@
# Copyright 2021 Red Hat, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
from octavia.api import app
from octavia.api.drivers import driver_factory
import octavia.tests.unit.base as base
class TestApp(base.TestCase):
def setUp(self):
super().setUp()
@mock.patch.object(driver_factory, "get_driver")
def test__init_drivers(self, mock_get_driver):
self.CONF = self.useFixture(oslo_fixture.Config(cfg.CONF))
self.CONF.config(
group="api_settings",
enabled_provider_drivers="provider1:desc1,provider2:desc2")
app._init_drivers()
mock_get_driver.assert_any_call("provider1")
mock_get_driver.assert_any_call("provider2")
@mock.patch.object(driver_factory, "get_driver")
def test__init_drivers_with_error(self, mock_get_driver):
self.CONF = self.useFixture(oslo_fixture.Config(cfg.CONF))
self.CONF.config(
group="api_settings",
enabled_provider_drivers="provider1:desc1,provider2:desc2")
mock_get_driver.side_effect = [True, Exception('Internal Error')]
app._init_drivers()
mock_get_driver.assert_any_call("provider1")
mock_get_driver.assert_any_call("provider2")
enabled_providers = driver_factory.get_providers()
self.assertEqual(enabled_providers, {'provider1': 'desc1'})

View File

@@ -0,0 +1,6 @@
---
fixes:
- |
Fix an issue that prevents the Octavia API service to be correctly
initialized when it fails to load a provider driver. It will now
fail gracefully and remove the driver from the enabled list.