From: Doug Wiegley Date: Wed, 18 Jun 2014 20:16:06 +0000 (-0500) Subject: LBaaS new object model logging no-op driver X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=53617ccf47c08f05fc27578a59d1b3e4b3a8488a;p=openstack-build%2Fneutron-build.git LBaaS new object model logging no-op driver Implements: bp/lbaas-objmodel-driver-changes Change-Id: I39dccb93c6f9bd3264afbf0acc55a56fc6fd05a1 --- diff --git a/etc/neutron.conf b/etc/neutron.conf index 31977a874..9bccc86ec 100644 --- a/etc/neutron.conf +++ b/etc/neutron.conf @@ -477,3 +477,5 @@ service_provider=VPN:openswan:neutron.services.vpn.service_drivers.ipsec.IPsecVP # service_provider=VPN:cisco:neutron.services.vpn.service_drivers.cisco_ipsec.CiscoCsrIPsecVPNDriver:default # Uncomment the line below to use Embrane heleos as Load Balancer service provider. # service_provider=LOADBALANCER:Embrane:neutron.services.loadbalancer.drivers.embrane.driver.EmbraneLbaas:default +# Uncomment the following line to test the LBaaS v2 API _WITHOUT_ a real backend +# service_provider = LOADBALANCER:LoggingNoop:neutron.services.loadbalancer.drivers.logging_noop.driver.LoggingNoopLoadBalancerDriver:default diff --git a/neutron/services/loadbalancer/drivers/abstract_driver.py b/neutron/services/loadbalancer/drivers/abstract_driver.py index 61123c307..64854fe43 100644 --- a/neutron/services/loadbalancer/drivers/abstract_driver.py +++ b/neutron/services/loadbalancer/drivers/abstract_driver.py @@ -18,6 +18,14 @@ import abc import six +# +# DEPRECATION WARNING. THIS ABSTRACT DRIVER IS FOR THE LBAAS V1 OBJECT +# MODEL AND SHOULD NO LONGER BE USED TO CREATE DRIVERS. +# +# PLEASE REFER TO driver_base.py and driver_mixins.py for the newest +# lbaas driver base classes. +# + @six.add_metaclass(abc.ABCMeta) class LoadBalancerAbstractDriver(object): diff --git a/neutron/services/loadbalancer/drivers/driver_base.py b/neutron/services/loadbalancer/drivers/driver_base.py new file mode 100644 index 000000000..8ed1f3d6d --- /dev/null +++ b/neutron/services/loadbalancer/drivers/driver_base.py @@ -0,0 +1,87 @@ +# Copyright 2014 A10 Networks +# +# 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 neutron.db.loadbalancer import loadbalancer_db as lb_db +from neutron.services.loadbalancer.drivers import driver_mixins + + +class NotImplementedManager(object): + """Helper class to make any subclass of LBAbstractDriver explode if it + is missing any of the required object managers. + """ + + def create(self, context, obj): + raise NotImplementedError() + + def update(self, context, old_obj, obj): + raise NotImplementedError() + + def delete(self, context, obj): + raise NotImplementedError() + + +class LoadBalancerBaseDriver(object): + """LBaaSv2 object model drivers should subclass LBAbstractDriver, and + initialize the following manager classes to create, update, and delete + the various load balancer objects. + """ + + load_balancer = NotImplementedManager() + listener = NotImplementedManager() + pool = NotImplementedManager() + member = NotImplementedManager() + health_monitor = NotImplementedManager() + + def __init__(self, plugin): + self.plugin = plugin + + +class BaseLoadBalancerManager(driver_mixins.BaseRefreshMixin, + driver_mixins.BaseStatsMixin, + driver_mixins.BaseStatusUpdateMixin, + driver_mixins.BaseManagerMixin): + + def __init__(self, driver): + super(BaseLoadBalancerManager, self).__init__(driver) + # TODO(dougw), use lb_db.LoadBalancer when v2 lbaas + # TODO(dougw), get rid of __init__() in StatusHelperManager, and + # the if is not None clauses; after fixing this next line, + # it can become a mandatory variable for that subclass. + self.model_class = None + + +class BaseListenerManager(driver_mixins.BaseManagerMixin): + pass + + +class BasePoolManager(driver_mixins.BaseStatusUpdateMixin, + driver_mixins.BaseManagerMixin): + + def __init__(self, driver): + super(BasePoolManager, self).__init__(driver) + self.model_class = lb_db.Pool + + +class BaseMemberManager(driver_mixins.BaseStatusUpdateMixin, + driver_mixins.BaseManagerMixin): + + def __init__(self, driver): + super(BaseMemberManager, self).__init__(driver) + self.model_class = lb_db.Member + + +class BaseHealthMonitorManager( + driver_mixins.BaseHealthMonitorStatusUpdateMixin, + driver_mixins.BaseManagerMixin): + pass diff --git a/neutron/services/loadbalancer/drivers/driver_mixins.py b/neutron/services/loadbalancer/drivers/driver_mixins.py new file mode 100644 index 000000000..3afc921d6 --- /dev/null +++ b/neutron/services/loadbalancer/drivers/driver_mixins.py @@ -0,0 +1,85 @@ +# Copyright 2014 A10 Networks +# +# 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. + +import abc +import six + +from neutron.plugins.common import constants + + +@six.add_metaclass(abc.ABCMeta) +class BaseManagerMixin(object): + + def __init__(self, driver): + self.driver = driver + + @abc.abstractmethod + def create(self, context, obj): + pass + + @abc.abstractmethod + def update(self, context, obj_old, obj): + pass + + @abc.abstractmethod + def delete(self, context, obj): + pass + + +@six.add_metaclass(abc.ABCMeta) +class BaseRefreshMixin(object): + + @abc.abstractmethod + def refresh(self, context, obj): + pass + + +@six.add_metaclass(abc.ABCMeta) +class BaseStatsMixin(object): + + @abc.abstractmethod + def stats(self, context, obj): + pass + + +class BaseStatusUpdateMixin(object): + + # Status update helpers + # Note: You must set self.model_class to an appropriate neutron model + # in your base manager class. + + def active(self, context, model_id): + if self.model_class is not None: + self.driver.plugin.update_status(context, self.model_class, + model_id, constants.ACTIVE) + + def failed(self, context, model_id): + if self.model_class is not None: + self.driver.plugin.update_status(context, self.model_class, + model_id, constants.ERROR) + + +class BaseHealthMonitorStatusUpdateMixin(object): + + def active(self, context, health_monitor_id, pool_id): + self.driver.plugin.update_pool_health_monitor(context, + health_monitor_id, + pool_id, + constants.ACTIVE) + + def failed(self, context, health_monitor_id, pool_id): + self.driver.plugin.update_pool_health_monitor(context, + health_monitor_id, + pool_id, + constants.ERROR) diff --git a/neutron/services/loadbalancer/drivers/logging_noop/__init__.py b/neutron/services/loadbalancer/drivers/logging_noop/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutron/services/loadbalancer/drivers/logging_noop/driver.py b/neutron/services/loadbalancer/drivers/logging_noop/driver.py new file mode 100644 index 000000000..e4bd6516d --- /dev/null +++ b/neutron/services/loadbalancer/drivers/logging_noop/driver.py @@ -0,0 +1,106 @@ +# Copyright 2014, Doug Wiegley (dougwig), A10 Networks +# +# 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 neutron.openstack.common import log as logging +from neutron.services.loadbalancer.drivers import driver_base + +LOG = logging.getLogger(__name__) + + +class LoggingNoopLoadBalancerDriver(driver_base.LoadBalancerBaseDriver): + + def __init__(self, plugin): + self.plugin = plugin + + # Each of the major LBaaS objects in the neutron database + # need a corresponding manager/handler class. + # + # Put common things that are shared across the entire driver, like + # config or a rest client handle, here. + # + # This function is executed when neutron-server starts. + + self.load_balancer = LoggingNoopLoadBalancerManager(self) + self.listener = LoggingNoopListenerManager(self) + self.pool = LoggingNoopPoolManager(self) + self.member = LoggingNoopMemberManager(self) + self.health_monitor = LoggingNoopHealthMonitorManager(self) + + +class LoggingNoopCommonManager(object): + + def create(self, context, obj): + LOG.debug("LB %s no-op, create %s", self.__class__.__name__, obj.id) + self.active(context, obj.id) + + def update(self, context, old_obj, obj): + LOG.debug("LB %s no-op, update %s", self.__class__.__name__, obj.id) + self.active(context, obj.id) + + def delete(self, context, obj): + LOG.debug("LB %s no-op, delete %s", self.__class__.__name__, obj.id) + + +class LoggingNoopLoadBalancerManager(LoggingNoopCommonManager, + driver_base.BaseLoadBalancerManager): + + def refresh(self, context, lb_obj, force=False): + # This is intended to trigger the backend to check and repair + # the state of this load balancer and all of its dependent objects + LOG.debug("LB pool refresh %s, force=%s", lb_obj.id, force) + + def stats(self, context, lb_obj): + LOG.debug("LB stats %s", lb_obj.id) + return { + "bytes_in": 0, + "bytes_out": 0, + "active_connections": 0, + "total_connections": 0 + } + + +class LoggingNoopListenerManager(LoggingNoopCommonManager, + driver_base.BaseListenerManager): + + def create(self, context, obj): + LOG.debug("LB listener no-op, create %s", self.__class__.__name__, + obj.id) + + def update(self, context, old_obj, obj): + LOG.debug("LB listener no-op, update %s", self.__class__.__name__, + obj.id) + + +class LoggingNoopPoolManager(LoggingNoopCommonManager, + driver_base.BasePoolManager): + pass + + +class LoggingNoopMemberManager(LoggingNoopCommonManager, + driver_base.BaseMemberManager): + pass + + +class LoggingNoopHealthMonitorManager(LoggingNoopCommonManager, + driver_base.BaseHealthMonitorManager): + + def create(self, context, obj): + LOG.debug("LB health monitor no-op, create %s", + self.__class__.__name__, obj.id) + self.active(context, obj.id, obj.id) + + def update(self, context, old_obj, obj): + LOG.debug("LB health monitor no-op, update %s", + self.__class__.__name__, obj.id) + self.active(context, obj.id, obj.id) diff --git a/neutron/tests/unit/services/loadbalancer/drivers/logging_noop/__init__.py b/neutron/tests/unit/services/loadbalancer/drivers/logging_noop/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutron/tests/unit/services/loadbalancer/drivers/logging_noop/test_logging_noop_driver.py b/neutron/tests/unit/services/loadbalancer/drivers/logging_noop/test_logging_noop_driver.py new file mode 100644 index 000000000..be695e846 --- /dev/null +++ b/neutron/tests/unit/services/loadbalancer/drivers/logging_noop/test_logging_noop_driver.py @@ -0,0 +1,148 @@ +# Copyright 2014, Doug Wiegley (dougwig), A10 Networks +# +# 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. + +import mock + +from neutron import context +from neutron.services.loadbalancer.drivers.logging_noop import driver +from neutron.tests.unit.db.loadbalancer import test_db_loadbalancer + +log_path = 'neutron.services.loadbalancer.drivers.logging_noop.driver.LOG' + + +class FakeModel(object): + def __init__(self, id): + self.id = id + + +def patch_manager(func): + @mock.patch(log_path) + def wrapper(*args): + log_mock = args[-1] + manager_test = args[0] + model = args[1] + parent = manager_test.parent + driver = parent.driver + driver.plugin.reset_mock() + + func(*args[:-1]) + + s = str(log_mock.mock_calls[0]) + parent.assertEqual(s[:11], "call.debug(") + parent.assertTrue(s.index(model.id) != -1, + msg="Model ID not found in log") + + return wrapper + + +class ManagerTest(object): + def __init__(self, parent, manager, model): + self.parent = parent + self.manager = manager + + self.create(model) + self.update(model, model) + self.delete(model) + + @patch_manager + def create(self, model): + self.manager.create(self.parent.context, model) + + @patch_manager + def update(self, old_model, model): + self.manager.update(self.parent.context, old_model, model) + + @patch_manager + def delete(self, model): + self.manager.delete(self.parent.context, model) + + +class ManagerTestWithUpdates(ManagerTest): + def __init__(self, parent, manager, model): + self.parent = parent + self.manager = manager + + self.create(model) + self.update(model, model) + self.delete(model) + + @patch_manager + def create(self, model): + self.manager.create(self.parent.context, model) + if self.manager.model_class is not None: + self.parent.assertEqual( + str(self.parent.driver.plugin.mock_calls[0])[:18], + "call.update_status") + + @patch_manager + def update(self, old_model, model): + self.manager.update(self.parent.context, old_model, model) + if self.manager.model_class is not None: + self.parent.assertEqual( + str(self.parent.driver.plugin.mock_calls[0])[:18], + "call.update_status") + + @patch_manager + def delete(self, model): + self.manager.delete(self.parent.context, model) + + +class LoadBalancerManagerTest(ManagerTestWithUpdates): + def __init__(self, parent, manager, model): + super(LoadBalancerManagerTest, self).__init__(parent, manager, model) + + self.refresh(model) + self.stats(model) + + @patch_manager + def refresh(self, model): + self.manager.refresh(self.parent.context, model) + + @patch_manager + def stats(self, model): + dummy_stats = { + "bytes_in": 0, + "bytes_out": 0, + "active_connections": 0, + "total_connections": 0 + } + h = self.manager.stats(self.parent.context, model) + self.parent.assertEqual(h, dummy_stats) + + +class TestLoggingNoopLoadBalancerDriver( + test_db_loadbalancer.LoadBalancerPluginDbTestCase): + + def setUp(self): + super(TestLoggingNoopLoadBalancerDriver, self).setUp() + self.context = context.get_admin_context() + self.plugin = mock.Mock() + self.driver = driver.LoggingNoopLoadBalancerDriver(self.plugin) + + def test_load_balancer_ops(self): + LoadBalancerManagerTest(self, self.driver.load_balancer, + FakeModel("loadbalancer-001")) + + def test_listener_ops(self): + ManagerTest(self, self.driver.listener, FakeModel("listener-001")) + + def test_pool_ops(self): + ManagerTestWithUpdates(self, self.driver.pool, FakeModel("pool-001")) + + def test_member_ops(self): + ManagerTestWithUpdates(self, self.driver.member, + FakeModel("member-001")) + + def test_health_monitor_ops(self): + ManagerTest(self, self.driver.health_monitor, FakeModel("hm-001"))