Merge "Defining interface for amphora base driver"

This commit is contained in:
Jenkins 2014-11-19 00:16:24 +00:00 committed by Gerrit Code Review
commit 2a1e144911
6 changed files with 565 additions and 1 deletions

View File

@ -0,0 +1,119 @@
# Copyright 2011-2014 OpenStack Foundation,author: Min Wang,German Eichberger
#
# 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.openstack.common import excutils
class AmphoraDriverError(Exception):
message = _("A super class for all other exceptions and the catch.")
def __init__(self, **kwargs):
try:
super(AmphoraDriverError, self).__init__(self.message % kwargs)
self.msg = self.message % kwargs
except Exception:
with excutils.save_and_reraise_exception() as ctxt:
if not self.use_fatal_exceptions():
ctxt.reraise = False
# at least get the core message out if something happened
super(AmphoraDriverError, self).__init__(self.message)
def __unicode__(self):
return unicode(self.msg)
def use_fatal_exceptions(self):
"""Return True if use fatal exceptions by raising them."""
return False
class NotFoundError(AmphoraDriverError):
message = _('this amphora couldn\'t be found')
class InfoException(AmphoraDriverError):
message = _('gathering information about this amphora failed')
class MetricsException(AmphoraDriverError):
message = _('gathering metrics failed')
class UnauthorizedException(AmphoraDriverError):
message = _('the driver can\'t access the amphora')
class StatisticsException(AmphoraDriverError):
message = _('gathering statistics failed')
class TimeOutException(AmphoraDriverError):
message = _('contacting the amphora timed out')
class UnavailableException(AmphoraDriverError):
message = _('the amphora is temporary unavailable')
class DeleteFailed(AmphoraDriverError):
message = _('this load balancer couldn\'t be deleted')
class SuspendFailed(AmphoraDriverError):
message = _('this load balancer couldn\'t be suspended')
class EnableFailed(AmphoraDriverError):
message = _('this load balancer couldn\'t be enabled')
class ArchiveException(AmphoraDriverError):
message = _('couldn\'t archive the logs')
class ProvisioningErrors(AmphoraDriverError):
message = _('Super class for provisioning amphora errors')
class ListenerProvisioningError(ProvisioningErrors):
message = _('couldn\'t provision Listener')
class LoadBalancerProvisoningError(ProvisioningErrors):
message = _('couldn\'t provision LoadBalancer')
class HealthMonitorProvisioningError(ProvisioningErrors):
message = _('couldn\'t provision HealthMonitor')
class NodeProvisioningError(ProvisioningErrors):
message = _('couldn\'t provision Node')

View File

@ -0,0 +1,210 @@
# Copyright 2011-2014 OpenStack Foundation,author: Min Wang,German Eichberger
#
# 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
@six.add_metaclass(abc.ABCMeta)
class AmphoraLoadBalancerDriver(object):
@abc.abstractmethod
def get_logger(self):
"""Return the logger to use.
This is a way to inject a custom logger for testing,etc.
:returns: the logger
"""
pass
@abc.abstractmethod
def update(self, listener, vip):
"""Update the amphora with a new configuration
:param listener: listener object,
need to use its protocol_port property
:type listener: object
:param vip: vip object, need to use its ip_address property
:type vip: object
:returns: return a value list (listener, vip, status flag--update)
At this moment, we just build the basic structure for testing, will
add more function along with the development
"""
pass
@abc.abstractmethod
def disable(self, listener, vip):
"""Suspend the running amphora --optional
:param listener: listener object,
need to use its protocol_port property
:type listener: object
:param vip: vip object, need to use its ip_address property
:type vip: object
:returns: return a value list (listener, vip, status flag--suspend)
At this moment, we just build the basic structure for testing, will
add more function along with the development
"""
pass
@abc.abstractmethod
def enable(self, listener, vip):
"""Start/enable the listener
:param listener: listener object,
need to use its protocol_port property
:type listener: object
:param vip : vip object, need to use its ip_address property
:type vip: object
:returns: return a value list (listener, vip, status flag--enable)
At this moment, we just build the basic structure for testing, will
add more function along with the development
"""
pass
@abc.abstractmethod
def delete(self, listener, vip):
"""Delete the listener from the amphora
:param listener: listener object,
need to use its protocol_port property
:type listener: object
:param vip: vip object, need to use its ip_address property
:type vip: object
:returns: return a value list (listener, vip, status flag--delete)
At this moment, we just build the basic structure for testing, will
add more function along with the development
"""
pass
@abc.abstractmethod
def info(self, amphora):
"""Returns information about the amphora
:param amphora: amphora object, need to use its id property
:type amphora: object
:returns: return a value list (amphora.id, status flag--'info')
At this moment, we just build the basic structure for testing, will
add more function along with the development, eventually, we want it
to return information as:
{"Rest Interface": "1.0", "Amphorae": "1.0",
"packages":{"ha proxy":"1.5"}}
some information might come from querying the amphora
"""
pass
@abc.abstractmethod
def get_metrics(self, amphora):
"""Return ceilometer ready metrics
Some amphora might choose to send them straight to ceilometer others
might use the mixin support metrics to be compatible with Neutron LBaaS
:param amphora: amphora object, need to use its id property
:type amphora: object
:returns: return a value list (amphora.id, status flag--'get_metrics')
At this moment, we just build the basic structure for testing, will
add more function along with the development, eventually, we want it
to return information as:
{"Rest Interface": "1.0", "Amphorae": "1.0",
"packages":{"ha proxy":"1.5"}}
some information might come from querying the amphora
"""
pass
@abc.abstractmethod
def get_health(self, amphora):
"""Return ceilometer ready health
:param amphora: amphora object, need to use its id property
:type amphora: object
:returns: return a value list (amphora.id, status flag--'get_health')
At this moment, we just build the basic structure for testing, will
add more function along with the development, eventually, we want it
to return information as:
returns map: {"amphora-status":HEALTHY,
loadbalancers: {"loadbalancer-id": {"loadbalancer-status": HEALTHY,
"listeners":{"listener-id":{"listener-status":HEALTHY,
"nodes":{"node-id":HEALTHY, ...}}, ...}, ...}}
"""
pass
@abc.abstractmethod
def get_diagnostics(self, amphora):
"""Return ceilometer ready health
:param amphora: amphora object, need to use its id property
:type amphora: object
:returns: return a value list (amphora.id, status flag--'ge
t_diagnostics')
At this moment, we just build the basic structure for testing, will
add more function along with the development, eventually, we want it
run some expensive self tests to determine if the amphora and the lbs
are healthy the idea is that those tests are triggered more infrequent
than the health gathering
"""
pass
@six.add_metaclass(abc.ABCMeta)
class HealthMixin(object):
@abc.abstractmethod
def update_health(self, health):
"""Return ceilometer ready health
:param health: health information emitted from the amphora
:type health: bool
:returns: return health
At this moment, we just build the basic structure for testing, will
add more function along with the development, eventually, we want it
return:
map: {"amphora-status":HEALTHY, loadbalancers: {"loadbalancer-id":
{"loadbalancer-status": HEALTHY,
"listeners":{"listener-id":{"listener-status":HEALTHY,
"nodes":{"node-id":HEALTHY, ...}}, ...}, ...}}
only items whose health has changed need to be submitted
awesome update code
"""
pass
@six.add_metaclass(abc.ABCMeta)
class StatsMixin(object):
@abc.abstractmethod
def update_stats(self, stats):
"""Return ceilometer ready stats
:param stats: statistic information emitted from the amphora
:type stats: string
:returns: return stats
At this moment, we just build the basic structure for testing, will
add more function along with the development, eventually, we want it
return:
uses map {"loadbalancer-id":{"listener-id":
{"bytes-in": 123, "bytes_out":123, "active_connections":123,
"total_connections", 123}, ...}
elements are named to keep it extsnsible for future versions
awesome update code and code to send to ceilometer
"""
pass

View File

@ -0,0 +1,128 @@
# Copyright 2014, Author: Min Wang,German Eichberger
#
# 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.amphorae.drivers import driver_base as driver_base
from octavia.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class LoggingMixIn(driver_base.HealthMixin, driver_base.StatsMixin):
def update_stats(self, stats):
LOG.debug("Amphora %s no-op, update stats %s",
self.__class__.__name__, stats)
self.stats = stats
def update_health(self, health):
LOG.debug("Amphora %s no-op, update health %s",
self.__class__.__name__, health)
self.health = health
class NoopManager(object):
def __init__(self):
super(NoopManager, self).__init__()
self.amphoraconfig = {}
def update(self, listener, vip):
LOG.debug("Amphora %s no-op, update listener %s, vip %s",
self.__class__.__name__, listener.protocol_port,
vip.ip_address)
self.amphoraconfig[(listener.protocol_port,
vip.ip_address)] = (listener, vip, 'active')
def disable(self, listener, vip):
LOG.debug("Amphora %s no-op, suspend listener %s, vip %s",
self.__class__.__name__,
listener.protocol_port, vip.ip_address)
self.amphoraconfig[(listener.protocol_port,
vip.ip_address)] = (listener, vip, 'disable')
def delete(self, listener, vip):
LOG.debug("Amphora %s no-op, delete listener %s, vip %s",
self.__class__.__name__,
listener.protocol_port, vip.ip_address)
self.amphoraconfig[(listener.protocol_port,
vip.ip_address)] = (listener, vip, 'delete')
def enable(self, listener, vip):
LOG.debug("Amphora %s no-op, enable listener %s, vip %s",
self.__class__.__name__,
listener.protocol_port, vip.ip_address)
self.amphoraconfig[(listener.protocol_port,
vip.ip_address)] = (listener, vip, 'enable')
def info(self, amphora):
LOG.debug("Amphora %s no-op, info amphora %s",
self.__class__.__name__, amphora.id)
self.amphoraconfig[amphora.id] = (amphora.id, 'info')
def get_metrics(self, amphora):
LOG.debug("Amphora %s no-op, get metrics amphora %s",
self.__class__.__name__, amphora.id)
self.amphoraconfig[amphora.id] = (amphora.id, 'get_metrics')
def get_health(self, amphora):
LOG.debug("Amphora %s no-op, get health amphora %s",
self.__class__.__name__, amphora.id)
self.amphoraconfig[amphora.id] = (amphora.id, 'get_health')
def get_diagnostics(self, amphora):
LOG.debug("Amphora %s no-op, get diagnostics amphora %s",
self.__class__.__name__, amphora.id)
self.amphoraconfig[amphora.id] = (amphora.id, 'get_diagnostics')
class NoopAmphoraLoadBalancerDriver(driver_base.AmphoraLoadBalancerDriver):
def __init__(self, log):
super(NoopAmphoraLoadBalancerDriver, self).__init__()
self.log = log
self.driver = NoopManager()
def get_logger(self):
raise NotImplementedError
def update(self, listener, vip):
self.driver.update(listener, vip)
def disable(self, listener, vip):
self.driver.disable(listener, vip)
def enable(self, listener, vip):
self.driver.enable(listener, vip)
def delete(self, listener, vip):
self.driver.delete(listener, vip)
def info(self, amphora):
self.driver.info(amphora)
def get_metrics(self, amphora):
self.driver.get_metrics(amphora)
def get_health(self, amphora):
self.driver.get_health(amphora)
def get_diagnostics(self, amphora):
self.driver.get_diagnostics(amphora)

View File

@ -0,0 +1,105 @@
# Copyright 2014, Author: Min Wang,German Eichberger
#
# 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.amphorae.drivers.noop_driver import driver as driver
from octavia.db import models as models
from octavia.openstack.common import log as logging
from octavia.openstack.common import uuidutils
from octavia.tests.unit import base as base
LOG = logging.getLogger(__name__)
class LoggingMixIn(base.TestCase):
def setUp(self):
super(LoggingMixIn, self).setUp()
self.mixin = driver.LoggingMixIn()
def test_update_stats(self):
self.mixin.update_stats('test update stats')
self.assertEqual('test update stats', self.mixin.stats)
def test_update_health(self):
self.mixin.update_health('test update health')
self.assertEqual('test update health', self.mixin.health)
class NoopAmphoraLoadBalancerDriver(base.TestCase):
FAKE_UUID_1 = uuidutils.generate_uuid()
def setUp(self):
super(NoopAmphoraLoadBalancerDriver, self).setUp()
self.driver = driver.NoopAmphoraLoadBalancerDriver(LOG)
self.listener = models.Listener()
self.listener.protocol_port = 80
self.vip = models.Vip()
self.vip.ip_address = "10.0.0.1"
self.amphora = models.Amphora()
self.amphora.id = self.FAKE_UUID_1
def test_get_logger(self):
self.assertEqual(LOG, self.driver.log)
def test_update(self):
self.driver.update(self.listener, self.vip)
self.assertEqual((self.listener, self.vip, 'active'),
self.driver.driver.amphoraconfig[(
self.listener.protocol_port,
self.vip.ip_address)])
def test_disable(self):
self.driver.disable(self.listener, self.vip)
self.assertEqual((self.listener, self.vip, 'disable'),
self.driver.driver.amphoraconfig[(
self.listener.protocol_port,
self.vip.ip_address)])
def test_delete(self):
self.driver.delete(self.listener, self.vip)
self.assertEqual((self.listener, self.vip, 'delete'),
self.driver.driver.amphoraconfig[(
self.listener.protocol_port,
self.vip.ip_address)])
def test_enable(self):
self.driver.enable(self.listener, self.vip)
self.assertEqual((self.listener, self.vip, 'enable'),
self.driver.driver.amphoraconfig[(
self.listener.protocol_port,
self.vip.ip_address)])
def test_info(self):
self.driver.info(self.amphora)
self.assertEqual((self.amphora.id, 'info'),
self.driver.driver.amphoraconfig[
self.amphora.id])
def test_get_metrics(self):
self.driver.get_metrics(self.amphora)
self.assertEqual((self.amphora.id, 'get_metrics'),
self.driver.driver.amphoraconfig[
self.amphora.id])
def test_get_health(self):
self.driver.get_health(self.amphora)
self.assertEqual((self.amphora.id, 'get_health'),
self.driver.driver.amphoraconfig[
self.amphora.id])
def test_get_diagnostics(self):
self.driver.get_diagnostics(self.amphora)
self.assertEqual((self.amphora.id, 'get_diagnostics'),
self.driver.driver.amphoraconfig[
self.amphora.id])

View File

@ -16,4 +16,6 @@ import testtools
class TestCase(testtools.TestCase):
pass
def setUp(self):
super(TestCase, self).setUp()