# 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
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):
--- /dev/null
+# 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
--- /dev/null
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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"))