Creates provider driver base class and exceptions

This patch creates the provider driver base class and exceptions.

See the "Enable Provider Driver Support" specification [1] for details.

[1] https://docs.openstack.org/octavia/latest/contributor/specs/version1.1/ \
    enable-provider-driver.html

This patch also improves the wording of hacking check O316 for
assertIsInstance.

Change-Id: I4262b9a772c43a702372b8389320858f06ac9720
This commit is contained in:
Michael Johnson 2018-03-30 17:21:12 -07:00
parent 0c6f281ccd
commit 8b94afd4ec
7 changed files with 687 additions and 1 deletions

View File

@ -0,0 +1,11 @@
# 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.

View File

@ -0,0 +1,91 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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 octavia.i18n import _
class DriverError(Exception):
"""Catch all exception that drivers can raise.
This exception includes two strings: The user fault string and the
optional operator fault string. The user fault string,
"user_fault_string", will be provided to the API requester. The operator
fault string, "operator_fault_string", will be logged in the Octavia API
log file for the operator to use when debugging.
:param user_fault_string: String provided to the API requester.
:type user_fault_string: string
:param operator_fault_string: Optional string logged by the Octavia API
for the operator to use when debugging.
:type operator_fault_string: string
"""
user_fault_string = _("An unknown driver error occurred.")
operator_fault_string = _("An unknown driver error occurred.")
def __init__(self, *args, **kwargs):
self.user_fault_string = kwargs.pop('user_fault_string',
self.user_fault_string)
self.operator_fault_string = kwargs.pop('operator_fault_string',
self.operator_fault_string)
super(DriverError, self).__init__(*args, **kwargs)
class NotImplementedError(Exception):
"""Exception raised when a driver does not implement an API function.
:param user_fault_string: String provided to the API requester.
:type user_fault_string: string
:param operator_fault_string: Optional string logged by the Octavia API
for the operator to use when debugging.
:type operator_fault_string: string
"""
user_fault_string = _("This feature is not implemented by the provider.")
operator_fault_string = _("This feature is not implemented by this "
"provider.")
def __init__(self, *args, **kwargs):
self.user_fault_string = kwargs.pop('user_fault_string',
self.user_fault_string)
self.operator_fault_string = kwargs.pop('operator_fault_string',
self.operator_fault_string)
super(NotImplementedError, self).__init__(*args, **kwargs)
class UnsupportedOptionError(Exception):
"""Exception raised when a driver does not support an option.
Provider drivers will validate that they can complete the request -- that
all options are supported by the driver. If the request fails validation,
drivers will raise an UnsupportedOptionError exception. For example, if a
driver does not support a flavor passed as an option to load balancer
create(), the driver will raise an UnsupportedOptionError and include a
message parameter providing an explanation of the failure.
:param user_fault_string: String provided to the API requester.
:type user_fault_string: string
:param operator_fault_string: Optional string logged by the Octavia API
for the operator to use when debugging.
:type operator_fault_string: string
"""
user_fault_string = _("A specified option is not supported by this "
"provider.")
operator_fault_string = _("A specified option is not supported by this "
"provider.")
def __init__(self, *args, **kwargs):
self.user_fault_string = kwargs.pop('user_fault_string',
self.user_fault_string)
self.operator_fault_string = kwargs.pop('operator_fault_string',
self.operator_fault_string)
super(UnsupportedOptionError, self).__init__(*args, **kwargs)

View File

@ -0,0 +1,358 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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 octavia.api.drivers import exceptions
# This class describes the abstraction of a provider driver interface.
# Load balancing provider drivers will implement this interface.
class ProviderDriver(object):
# Load Balancer
def create_vip_port(self, loadbalancer_id, vip_dictionary):
"""Creates a port for a load balancer VIP.
If the driver supports creating VIP ports, the driver will create a
VIP port and return the vip_dictionary populated with the vip_port_id.
If the driver does not support port creation, the driver will raise
a NotImplementedError.
:param loadbalancer_id: ID of loadbalancer.
:type loadbalancer_id: string
:param: vip_dictionary: The VIP dictionary.
:type vip_dictionary: dict
:returns: VIP dictionary with vip_port_id.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support creating
VIP ports.
"""
raise exceptions.NotImplementedError()
def loadbalancer_create(self, loadbalancer):
"""Creates a new load balancer.
:param loadbalancer: The load balancer object.
:type loadbalancer: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support create.
:raises UnsupportedOptionError: The driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
def loadbalancer_delete(self, loadbalancer_id, cascade=False):
"""Deletes a load balancer.
:param loadbalancer_id: ID of the load balancer to delete.
:type loadbalancer_id: string
:param cascade: If True, deletes all child objects (listeners,
pools, etc.) in addition to the load balancer.
:type cascade: bool
:return: Nothing if the delete request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError()
def loadbalancer_failover(self, loadbalancer_id):
"""Performs a fail over of a load balancer.
:param loadbalancer_id: ID of the load balancer to failover.
:type loadbalancer_id: string
:return: Nothing if the failover request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises: NotImplementedError if driver does not support request.
"""
raise exceptions.NotImplementedError()
def loadbalancer_update(self, loadbalancer):
"""Updates a load balancer.
:param loadbalancer: The load balancer object.
:type loadbalancer: object
:return: Nothing if the update request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support request.
:raises UnsupportedOptionError: The driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
# Listener
def listener_create(self, listener):
"""Creates a new listener.
:param listener: The listener object.
:type listener: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
def listener_delete(self, listener_id):
"""Deletes a listener.
:param listener_id: ID of the listener to delete.
:type listener_id: string
:return: Nothing if the delete request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError()
def listener_update(self, listener):
"""Updates a listener.
:param listener: The listener object.
:type listener: object
:return: Nothing if the update request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
# Pool
def pool_create(self, pool):
"""Creates a new pool.
:param pool: The pool object.
:type pool: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
def pool_delete(self, pool_id):
"""Deletes a pool and its members.
:param pool_id: ID of the pool to delete.
:type pool_id: string
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError()
def pool_update(self, pool):
"""Updates a pool.
:param pool: The pool object.
:type pool: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
# Member
def member_create(self, member):
"""Creates a new member for a pool.
:param member: The member object.
:type member: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
def member_delete(self, member_id):
"""Deletes a pool member.
:param member_id: ID of the member to delete.
:type member_id: string
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError()
def member_update(self, member):
"""Updates a pool member.
:param member: The member object.
:type member: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
def member_batch_update(self, members):
"""Creates, updates, or deletes a set of pool members.
:param members: List of member objects.
:type members: list
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
# Health Monitor
def health_monitor_create(self, healthmonitor):
"""Creates a new health monitor.
:param healthmonitor: The health monitor object.
:type healthmonitor: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
def health_monitor_delete(self, healthmonitor_id):
"""Deletes a healthmonitor_id.
:param healthmonitor_id: ID of the monitor to delete.
:type healthmonitor_id: string
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError()
def health_monitor_update(self, healthmonitor):
"""Updates a health monitor.
:param healthmonitor: The health monitor object.
:type healthmonitor: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
# L7 Policy
def l7policy_create(self, l7policy):
"""Creates a new L7 policy.
:param l7policy: The l7policy object.
:type l7policy: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
def l7policy_delete(self, l7policy_id):
"""Deletes an L7 policy.
:param l7policy_id: ID of the L7 policy to delete.
:type l7policy_id: string
:return: Nothing if the delete request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError()
def l7policy_update(self, l7policy):
"""Updates an L7 policy.
:param l7policy: The l7policy object.
:type l7policy: object
:return: Nothing if the update request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
# L7 Rule
def l7rule_create(self, l7rule):
"""Creates a new L7 rule.
:param l7rule: The L7 rule object.
:type l7rule: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
def l7rule_delete(self, l7rule_id):
"""Deletes an L7 rule.
:param l7rule_id: ID of the L7 rule to delete.
:type l7rule_id: string
:return: Nothing if the delete request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError()
def l7rule_update(self, l7rule):
"""Updates an L7 rule.
:param l7rule: The L7 rule object.
:type l7rule: object
:return: Nothing if the update request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()
# Flavor
def get_supported_flavor_metadata(self):
"""Returns a dict of flavor metadata keys supported by this driver.
The returned dictionary will include key/value pairs, 'name' and
'description.'
:returns: The flavor metadata dictionary
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support flavors.
"""
raise exceptions.NotImplementedError()
def validate_flavor(self, flavor_metadata):
"""Validates if driver can support the flavor.
:param flavor_metadata: Dictionary with flavor metadata.
:type flavor_metadata: dict
:return: Nothing if the flavor is valid and supported.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support flavors.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError()

View File

@ -82,7 +82,8 @@ def assert_true_instance(logical_line):
O316 O316
""" """
if assert_trueinst_re.match(logical_line): if assert_trueinst_re.match(logical_line):
yield (0, "O316: assertTrue(isinstance(a, b)) sentences not allowed") yield (0, "O316: assertTrue(isinstance(a, b)) sentences not allowed. "
"Use assertIsInstance instead.")
def assert_equal_or_not_none(logical_line): def assert_equal_or_not_none(logical_line):

View File

@ -0,0 +1,11 @@
# 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.

View File

@ -0,0 +1,57 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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 octavia.api.drivers import exceptions
import octavia.tests.unit.base as base
class TestProviderExceptions(base.TestCase):
def setUp(self):
super(TestProviderExceptions, self).setUp()
self.user_fault_string = 'Bad driver'
self.operator_fault_string = 'Fix bad driver.'
def test_DriverError(self):
driver_error = exceptions.DriverError(
user_fault_string=self.user_fault_string,
operator_fault_string=self.operator_fault_string)
self.assertEqual(self.user_fault_string,
driver_error.user_fault_string)
self.assertEqual(self.operator_fault_string,
driver_error.operator_fault_string)
self.assertIsInstance(driver_error, Exception)
def test_NotImplementedError(self):
not_implemented_error = exceptions.NotImplementedError(
user_fault_string=self.user_fault_string,
operator_fault_string=self.operator_fault_string)
self.assertEqual(self.user_fault_string,
not_implemented_error.user_fault_string)
self.assertEqual(self.operator_fault_string,
not_implemented_error.operator_fault_string)
self.assertIsInstance(not_implemented_error, Exception)
def test_UnsupportedOptionError(self):
unsupported_option_error = exceptions.UnsupportedOptionError(
user_fault_string=self.user_fault_string,
operator_fault_string=self.operator_fault_string)
self.assertEqual(self.user_fault_string,
unsupported_option_error.user_fault_string)
self.assertEqual(self.operator_fault_string,
unsupported_option_error.operator_fault_string)
self.assertIsInstance(unsupported_option_error, Exception)

View File

@ -0,0 +1,157 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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 octavia.api.drivers import exceptions
from octavia.api.drivers import provider_base as driver_base
import octavia.tests.unit.base as base
class TestProviderBase(base.TestCase):
"""Test base methods.
Tests that methods not implemented by the drivers raise
NotImplementedError.
"""
def setUp(self):
super(TestProviderBase, self).setUp()
self.driver = driver_base.ProviderDriver()
def test_create_vip_port(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.create_vip_port,
False, False)
def test_loadbalancer_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.loadbalancer_create,
False)
def test_loadbalancer_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.loadbalancer_delete,
False)
def test_loadbalancer_failover(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.loadbalancer_failover,
False)
def test_loadbalancer_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.loadbalancer_update,
False)
def test_listener_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.listener_create,
False)
def test_listener_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.listener_delete,
False)
def test_listener_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.listener_update,
False)
def test_pool_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.pool_create,
False)
def test_pool_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.pool_delete,
False)
def test_pool_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.pool_update,
False)
def test_member_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.member_create,
False)
def test_member_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.member_delete,
False)
def test_member_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.member_update,
False)
def test_member_batch_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.member_batch_update,
False)
def test_health_monitor_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.health_monitor_create,
False)
def test_health_monitor_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.health_monitor_delete,
False)
def test_health_monitor_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.health_monitor_update,
False)
def test_l7policy_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7policy_create,
False)
def test_l7policy_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7policy_delete,
False)
def test_l7policy_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7policy_update,
False)
def test_l7rule_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7rule_create,
False)
def test_l7rule_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7rule_delete,
False)
def test_l7rule_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7rule_update,
False)
def test_get_supported_flavor_metadata(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.get_supported_flavor_metadata)
def test_validate_flavor(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.validate_flavor,
False)