LBaaS integration with service type framework
The patch makes the following changes: * adds new attribute of the pool: provider, which is provider name as it is written in configuration * adds support for multiple plugin drivers for loadbalancer * cleans up healthmonitor-related plugin driver API Drivers should work with healthmonitor associations only * adds ability to update provider attribute for the pool used to reassociate pools with new providers in case their providers were removed from configuration implements blueprint lbaas-integration-with-service-types DocImpact Change-Id: I4295c9bcceb38e60f813d5596af48bd8194c1c9b
This commit is contained in:
parent
df514c0f50
commit
aa2ccf3991
@ -312,12 +312,6 @@ admin_user = %SERVICE_USER%
|
|||||||
admin_password = %SERVICE_PASSWORD%
|
admin_password = %SERVICE_PASSWORD%
|
||||||
signing_dir = $state_path/keystone-signing
|
signing_dir = $state_path/keystone-signing
|
||||||
|
|
||||||
[lbaas]
|
|
||||||
# ==================================================================================================
|
|
||||||
# driver_fqn is the fully qualified name of the lbaas driver that will be loaded by the lbass plugin
|
|
||||||
# ==================================================================================================
|
|
||||||
# driver_fqn = neutron.services.loadbalancer.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver
|
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
# This line MUST be changed to actually run the plugin.
|
# This line MUST be changed to actually run the plugin.
|
||||||
# Example:
|
# Example:
|
||||||
@ -368,3 +362,5 @@ signing_dir = $state_path/keystone-signing
|
|||||||
# service_provider=LOADBALANCER:name:lbaas_plugin_driver_path:default
|
# service_provider=LOADBALANCER:name:lbaas_plugin_driver_path:default
|
||||||
# example of non-default provider:
|
# example of non-default provider:
|
||||||
# service_provider=FIREWALL:name2:firewall_driver_path
|
# service_provider=FIREWALL:name2:firewall_driver_path
|
||||||
|
# --- Reference implementations ---
|
||||||
|
service_provider=LOADBALANCER:Haproxy:neutron.services.loadbalancer.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver:default
|
||||||
|
@ -25,6 +25,7 @@ from neutron.common import exceptions as q_exc
|
|||||||
from neutron.db import db_base_plugin_v2 as base_db
|
from neutron.db import db_base_plugin_v2 as base_db
|
||||||
from neutron.db import model_base
|
from neutron.db import model_base
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
|
from neutron.db import servicetype_db as st_db
|
||||||
from neutron.extensions import loadbalancer
|
from neutron.extensions import loadbalancer
|
||||||
from neutron.extensions.loadbalancer import LoadBalancerPluginBase
|
from neutron.extensions.loadbalancer import LoadBalancerPluginBase
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
@ -130,6 +131,14 @@ class Pool(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant,
|
|||||||
cascade="all, delete-orphan")
|
cascade="all, delete-orphan")
|
||||||
vip = orm.relationship(Vip, backref='pool')
|
vip = orm.relationship(Vip, backref='pool')
|
||||||
|
|
||||||
|
provider = orm.relationship(
|
||||||
|
st_db.ProviderResourceAssociation,
|
||||||
|
uselist=False,
|
||||||
|
lazy="joined",
|
||||||
|
primaryjoin="Pool.id==ProviderResourceAssociation.resource_id",
|
||||||
|
foreign_keys=[st_db.ProviderResourceAssociation.resource_id]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HealthMonitor(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
class HealthMonitor(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||||
"""Represents a v2 neutron loadbalancer healthmonitor."""
|
"""Represents a v2 neutron loadbalancer healthmonitor."""
|
||||||
@ -457,7 +466,12 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase,
|
|||||||
'lb_method': pool['lb_method'],
|
'lb_method': pool['lb_method'],
|
||||||
'admin_state_up': pool['admin_state_up'],
|
'admin_state_up': pool['admin_state_up'],
|
||||||
'status': pool['status'],
|
'status': pool['status'],
|
||||||
'status_description': pool['status_description']}
|
'status_description': pool['status_description'],
|
||||||
|
'provider': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if pool.provider:
|
||||||
|
res['provider'] = pool.provider.provider_name
|
||||||
|
|
||||||
# Get the associated members
|
# Get the associated members
|
||||||
res['members'] = [member['id'] for member in pool['members']]
|
res['members'] = [member['id'] for member in pool['members']]
|
||||||
@ -465,7 +479,6 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase,
|
|||||||
# Get the associated health_monitors
|
# Get the associated health_monitors
|
||||||
res['health_monitors'] = [
|
res['health_monitors'] = [
|
||||||
monitor['monitor_id'] for monitor in pool['monitors']]
|
monitor['monitor_id'] for monitor in pool['monitors']]
|
||||||
|
|
||||||
return self._fields(res, fields)
|
return self._fields(res, fields)
|
||||||
|
|
||||||
def update_pool_stats(self, context, pool_id, data=None):
|
def update_pool_stats(self, context, pool_id, data=None):
|
||||||
@ -523,12 +536,10 @@ class LoadBalancerPluginDb(LoadBalancerPluginBase,
|
|||||||
pool_db.stats = self._create_pool_stats(context, pool_db['id'])
|
pool_db.stats = self._create_pool_stats(context, pool_db['id'])
|
||||||
context.session.add(pool_db)
|
context.session.add(pool_db)
|
||||||
|
|
||||||
pool_db = self._get_resource(context, Pool, pool_db['id'])
|
|
||||||
return self._make_pool_dict(pool_db)
|
return self._make_pool_dict(pool_db)
|
||||||
|
|
||||||
def update_pool(self, context, id, pool):
|
def update_pool(self, context, id, pool):
|
||||||
p = pool['pool']
|
p = pool['pool']
|
||||||
|
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
pool_db = self._get_resource(context, Pool, id)
|
pool_db = self._get_resource(context, Pool, id)
|
||||||
self.assert_modification_allowed(pool_db)
|
self.assert_modification_allowed(pool_db)
|
||||||
|
@ -77,9 +77,10 @@ class ServiceTypeManager(object):
|
|||||||
def add_resource_association(self, context, service_type, provider_name,
|
def add_resource_association(self, context, service_type, provider_name,
|
||||||
resource_id):
|
resource_id):
|
||||||
r = self.conf.get_service_providers(
|
r = self.conf.get_service_providers(
|
||||||
filters={'service_type': service_type, 'name': provider_name})
|
filters={'service_type': [service_type], 'name': [provider_name]})
|
||||||
if not r:
|
if not r:
|
||||||
raise pconf.ServiceProviderNotFound(service_type=service_type)
|
raise pconf.ServiceProviderNotFound(provider=provider_name,
|
||||||
|
service_type=service_type)
|
||||||
|
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
# we don't actually need service type for association.
|
# we don't actually need service type for association.
|
||||||
@ -88,3 +89,12 @@ class ServiceTypeManager(object):
|
|||||||
assoc = ProviderResourceAssociation(provider_name=provider_name,
|
assoc = ProviderResourceAssociation(provider_name=provider_name,
|
||||||
resource_id=resource_id)
|
resource_id=resource_id)
|
||||||
context.session.add(assoc)
|
context.session.add(assoc)
|
||||||
|
|
||||||
|
def del_resource_associations(self, context, resource_ids):
|
||||||
|
if not resource_ids:
|
||||||
|
return
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
(context.session.query(ProviderResourceAssociation).
|
||||||
|
filter(
|
||||||
|
ProviderResourceAssociation.resource_id.in_(resource_ids)).
|
||||||
|
delete(synchronize_session='fetch'))
|
||||||
|
@ -162,6 +162,9 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
'protocol': {'allow_post': True, 'allow_put': False,
|
'protocol': {'allow_post': True, 'allow_put': False,
|
||||||
'validate': {'type:values': ['TCP', 'HTTP', 'HTTPS']},
|
'validate': {'type:values': ['TCP', 'HTTP', 'HTTPS']},
|
||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
|
'provider': {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'is_visible': True, 'default': attr.ATTR_NOT_SPECIFIED},
|
||||||
'lb_method': {'allow_post': True, 'allow_put': True,
|
'lb_method': {'allow_post': True, 'allow_put': True,
|
||||||
'validate': {'type:string': None},
|
'validate': {'type:string': None},
|
||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
|
@ -104,10 +104,6 @@ class LoadBalancerAbstractDriver(object):
|
|||||||
def delete_member(self, context, member):
|
def delete_member(self, context, member):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def create_health_monitor(self, context, health_monitor):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def update_health_monitor(self, context,
|
def update_health_monitor(self, context,
|
||||||
old_health_monitor,
|
old_health_monitor,
|
||||||
@ -115,13 +111,6 @@ class LoadBalancerAbstractDriver(object):
|
|||||||
pool_id):
|
pool_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def delete_health_monitor(self, context, health_monitor):
|
|
||||||
"""Driver may call the code below in order to delete the monitor.
|
|
||||||
self.plugin._delete_db_health_monitor(context, health_monitor["id"])
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def create_pool_health_monitor(self, context,
|
def create_pool_health_monitor(self, context,
|
||||||
health_monitor,
|
health_monitor,
|
||||||
|
@ -354,11 +354,5 @@ class HaproxyOnHostPluginDriver(abstract_driver.LoadBalancerAbstractDriver):
|
|||||||
agent = self.get_pool_agent(context, pool_id)
|
agent = self.get_pool_agent(context, pool_id)
|
||||||
self.agent_rpc.modify_pool(context, pool_id, agent['host'])
|
self.agent_rpc.modify_pool(context, pool_id, agent['host'])
|
||||||
|
|
||||||
def create_health_monitor(self, context, health_monitor):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_health_monitor(self, context, health_monitor):
|
|
||||||
self.plugin._delete_db_health_monitor(context, health_monitor["id"])
|
|
||||||
|
|
||||||
def stats(self, context, pool_id):
|
def stats(self, context, pool_id):
|
||||||
pass
|
pass
|
||||||
|
@ -79,20 +79,12 @@ class NoopLbaaSDriver(abstract_driver.LoadBalancerAbstractDriver):
|
|||||||
def delete_member(self, context, member):
|
def delete_member(self, context, member):
|
||||||
self.plugin._delete_db_member(context, member["id"])
|
self.plugin._delete_db_member(context, member["id"])
|
||||||
|
|
||||||
@log.log
|
|
||||||
def create_health_monitor(self, context, health_monitor):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@log.log
|
@log.log
|
||||||
def update_health_monitor(self, context, old_health_monitor,
|
def update_health_monitor(self, context, old_health_monitor,
|
||||||
health_monitor,
|
health_monitor,
|
||||||
pool_association):
|
pool_association):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@log.log
|
|
||||||
def delete_health_monitor(self, context, health_monitor):
|
|
||||||
self.plugin._delete_db_health_monitor(context, health_monitor["id"])
|
|
||||||
|
|
||||||
@log.log
|
@log.log
|
||||||
def create_pool_health_monitor(self, context,
|
def create_pool_health_monitor(self, context,
|
||||||
health_monitor, pool_id):
|
health_monitor, pool_id):
|
||||||
|
@ -15,41 +15,32 @@
|
|||||||
#
|
#
|
||||||
# @author: Avishay Balderman, Radware
|
# @author: Avishay Balderman, Radware
|
||||||
|
|
||||||
from oslo.config import cfg
|
from neutron.api.v2 import attributes as attrs
|
||||||
|
from neutron.common import exceptions as n_exc
|
||||||
from neutron.common import legacy
|
from neutron import context
|
||||||
from neutron.db import api as qdbapi
|
from neutron.db import api as qdbapi
|
||||||
from neutron.db.loadbalancer import loadbalancer_db
|
from neutron.db.loadbalancer import loadbalancer_db as ldb
|
||||||
from neutron.openstack.common import importutils
|
from neutron.db import servicetype_db as st_db
|
||||||
from neutron.openstack.common import log as logging
|
from neutron.openstack.common import log as logging
|
||||||
from neutron.plugins.common import constants
|
from neutron.plugins.common import constants
|
||||||
from neutron.services.loadbalancer import agent_scheduler
|
from neutron.services.loadbalancer import agent_scheduler
|
||||||
|
from neutron.services import provider_configuration as pconf
|
||||||
|
from neutron.services import service_base
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_DRIVER = ("neutron.services.loadbalancer.drivers.haproxy"
|
|
||||||
".plugin_driver.HaproxyOnHostPluginDriver")
|
|
||||||
|
|
||||||
lbaas_plugin_opts = [
|
class LoadBalancerPlugin(ldb.LoadBalancerPluginDb,
|
||||||
cfg.StrOpt('driver_fqn',
|
|
||||||
default=DEFAULT_DRIVER,
|
|
||||||
help=_('LBaaS driver Fully Qualified Name'))
|
|
||||||
]
|
|
||||||
|
|
||||||
cfg.CONF.register_opts(lbaas_plugin_opts, "LBAAS")
|
|
||||||
legacy.override_config(cfg.CONF, [('LBAAS', 'driver_fqn')])
|
|
||||||
|
|
||||||
|
|
||||||
class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|
||||||
agent_scheduler.LbaasAgentSchedulerDbMixin):
|
agent_scheduler.LbaasAgentSchedulerDbMixin):
|
||||||
|
|
||||||
"""Implementation of the Neutron Loadbalancer Service Plugin.
|
"""Implementation of the Neutron Loadbalancer Service Plugin.
|
||||||
|
|
||||||
This class manages the workflow of LBaaS request/response.
|
This class manages the workflow of LBaaS request/response.
|
||||||
Most DB related works are implemented in class
|
Most DB related works are implemented in class
|
||||||
loadbalancer_db.LoadBalancerPluginDb.
|
loadbalancer_db.LoadBalancerPluginDb.
|
||||||
"""
|
"""
|
||||||
supported_extension_aliases = ["lbaas", "lbaas_agent_scheduler"]
|
supported_extension_aliases = ["lbaas",
|
||||||
|
"lbaas_agent_scheduler",
|
||||||
|
"service-type"]
|
||||||
|
|
||||||
# lbaas agent notifiers to handle agent update operations;
|
# lbaas agent notifiers to handle agent update operations;
|
||||||
# can be updated by plugin drivers while loading;
|
# can be updated by plugin drivers while loading;
|
||||||
@ -60,20 +51,51 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
"""Initialization for the loadbalancer service plugin."""
|
"""Initialization for the loadbalancer service plugin."""
|
||||||
|
|
||||||
qdbapi.register_models()
|
qdbapi.register_models()
|
||||||
|
self.service_type_manager = st_db.ServiceTypeManager.get_instance()
|
||||||
self._load_drivers()
|
self._load_drivers()
|
||||||
|
|
||||||
def _load_drivers(self):
|
def _load_drivers(self):
|
||||||
"""Loads plugin-driver from configuration.
|
"""Loads plugin-drivers specified in configuration."""
|
||||||
|
self.drivers, self.default_provider = service_base.load_drivers(
|
||||||
|
constants.LOADBALANCER, self)
|
||||||
|
|
||||||
That method will later leverage service type framework
|
# we're at the point when extensions are not loaded yet
|
||||||
|
# so prevent policy from being loaded
|
||||||
|
ctx = context.get_admin_context(load_admin_roles=False)
|
||||||
|
# stop service in case provider was removed, but resources were not
|
||||||
|
self._check_orphan_pool_associations(ctx, self.drivers.keys())
|
||||||
|
|
||||||
|
def _check_orphan_pool_associations(self, context, provider_names):
|
||||||
|
"""Checks remaining associations between pools and providers.
|
||||||
|
|
||||||
|
If admin has not undeployed resources with provider that was deleted
|
||||||
|
from configuration, neutron service is stopped. Admin must delete
|
||||||
|
resources prior to removing providers from configuration.
|
||||||
"""
|
"""
|
||||||
|
pools = self.get_pools(context)
|
||||||
|
lost_providers = set([pool['provider'] for pool in pools
|
||||||
|
if pool['provider'] not in provider_names])
|
||||||
|
# resources are left without provider - stop the service
|
||||||
|
if lost_providers:
|
||||||
|
msg = _("Delete associated loadbalancer pools before "
|
||||||
|
"removing providers %s") % list(lost_providers)
|
||||||
|
LOG.exception(msg)
|
||||||
|
raise SystemExit(msg)
|
||||||
|
|
||||||
|
def _get_driver_for_provider(self, provider):
|
||||||
|
if provider in self.drivers:
|
||||||
|
return self.drivers[provider]
|
||||||
|
# raise if not associated (should never be reached)
|
||||||
|
raise n_exc.Invalid(_("Error retrieving driver for provider %s") %
|
||||||
|
provider)
|
||||||
|
|
||||||
|
def _get_driver_for_pool(self, context, pool_id):
|
||||||
|
pool = self.get_pool(context, pool_id)
|
||||||
try:
|
try:
|
||||||
self.driver = importutils.import_object(
|
return self.drivers[pool['provider']]
|
||||||
cfg.CONF.LBAAS.driver_fqn, self
|
except KeyError:
|
||||||
)
|
raise n_exc.Invalid(_("Error retrieving provider for pool %s") %
|
||||||
except ImportError:
|
pool_id)
|
||||||
LOG.exception(_("Error loading LBaaS driver %s"),
|
|
||||||
cfg.CONF.LBAAS.driver_fqn)
|
|
||||||
|
|
||||||
def get_plugin_type(self):
|
def get_plugin_type(self):
|
||||||
return constants.LOADBALANCER
|
return constants.LOADBALANCER
|
||||||
@ -83,7 +105,8 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
|
|
||||||
def create_vip(self, context, vip):
|
def create_vip(self, context, vip):
|
||||||
v = super(LoadBalancerPlugin, self).create_vip(context, vip)
|
v = super(LoadBalancerPlugin, self).create_vip(context, vip)
|
||||||
self.driver.create_vip(context, v)
|
driver = self._get_driver_for_pool(context, v['pool_id'])
|
||||||
|
driver.create_vip(context, v)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
def update_vip(self, context, id, vip):
|
def update_vip(self, context, id, vip):
|
||||||
@ -91,7 +114,8 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
vip['vip']['status'] = constants.PENDING_UPDATE
|
vip['vip']['status'] = constants.PENDING_UPDATE
|
||||||
old_vip = self.get_vip(context, id)
|
old_vip = self.get_vip(context, id)
|
||||||
v = super(LoadBalancerPlugin, self).update_vip(context, id, vip)
|
v = super(LoadBalancerPlugin, self).update_vip(context, id, vip)
|
||||||
self.driver.update_vip(context, old_vip, v)
|
driver = self._get_driver_for_pool(context, v['pool_id'])
|
||||||
|
driver.update_vip(context, old_vip, v)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
def _delete_db_vip(self, context, id):
|
def _delete_db_vip(self, context, id):
|
||||||
@ -99,14 +123,37 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
super(LoadBalancerPlugin, self).delete_vip(context, id)
|
super(LoadBalancerPlugin, self).delete_vip(context, id)
|
||||||
|
|
||||||
def delete_vip(self, context, id):
|
def delete_vip(self, context, id):
|
||||||
self.update_status(context, loadbalancer_db.Vip,
|
self.update_status(context, ldb.Vip,
|
||||||
id, constants.PENDING_DELETE)
|
id, constants.PENDING_DELETE)
|
||||||
v = self.get_vip(context, id)
|
v = self.get_vip(context, id)
|
||||||
self.driver.delete_vip(context, v)
|
driver = self._get_driver_for_pool(context, v['pool_id'])
|
||||||
|
driver.delete_vip(context, v)
|
||||||
|
|
||||||
|
def _get_provider_name(self, context, pool):
|
||||||
|
if ('provider' in pool and
|
||||||
|
pool['provider'] != attrs.ATTR_NOT_SPECIFIED):
|
||||||
|
provider_name = pconf.normalize_provider_name(pool['provider'])
|
||||||
|
self.validate_provider(provider_name)
|
||||||
|
return provider_name
|
||||||
|
else:
|
||||||
|
if not self.default_provider:
|
||||||
|
raise pconf.DefaultServiceProviderNotFound(
|
||||||
|
service_type=constants.LOADBALANCER)
|
||||||
|
return self.default_provider
|
||||||
|
|
||||||
def create_pool(self, context, pool):
|
def create_pool(self, context, pool):
|
||||||
|
provider_name = self._get_provider_name(context, pool['pool'])
|
||||||
p = super(LoadBalancerPlugin, self).create_pool(context, pool)
|
p = super(LoadBalancerPlugin, self).create_pool(context, pool)
|
||||||
self.driver.create_pool(context, p)
|
|
||||||
|
self.service_type_manager.add_resource_association(
|
||||||
|
context,
|
||||||
|
constants.LOADBALANCER,
|
||||||
|
provider_name, p['id'])
|
||||||
|
#need to add provider name to pool dict,
|
||||||
|
#because provider was not known to db plugin at pool creation
|
||||||
|
p['provider'] = provider_name
|
||||||
|
driver = self.drivers[provider_name]
|
||||||
|
driver.create_pool(context, p)
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def update_pool(self, context, id, pool):
|
def update_pool(self, context, id, pool):
|
||||||
@ -114,22 +161,28 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
pool['pool']['status'] = constants.PENDING_UPDATE
|
pool['pool']['status'] = constants.PENDING_UPDATE
|
||||||
old_pool = self.get_pool(context, id)
|
old_pool = self.get_pool(context, id)
|
||||||
p = super(LoadBalancerPlugin, self).update_pool(context, id, pool)
|
p = super(LoadBalancerPlugin, self).update_pool(context, id, pool)
|
||||||
self.driver.update_pool(context, old_pool, p)
|
driver = self._get_driver_for_provider(p['provider'])
|
||||||
|
driver.update_pool(context, old_pool, p)
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def _delete_db_pool(self, context, id):
|
def _delete_db_pool(self, context, id):
|
||||||
# proxy the call until plugin inherits from DBPlugin
|
# proxy the call until plugin inherits from DBPlugin
|
||||||
super(LoadBalancerPlugin, self).delete_pool(context, id)
|
# rely on uuid uniqueness:
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
self.service_type_manager.del_resource_associations(context, [id])
|
||||||
|
super(LoadBalancerPlugin, self).delete_pool(context, id)
|
||||||
|
|
||||||
def delete_pool(self, context, id):
|
def delete_pool(self, context, id):
|
||||||
self.update_status(context, loadbalancer_db.Pool,
|
self.update_status(context, ldb.Pool,
|
||||||
id, constants.PENDING_DELETE)
|
id, constants.PENDING_DELETE)
|
||||||
p = self.get_pool(context, id)
|
p = self.get_pool(context, id)
|
||||||
self.driver.delete_pool(context, p)
|
driver = self._get_driver_for_provider(p['provider'])
|
||||||
|
driver.delete_pool(context, p)
|
||||||
|
|
||||||
def create_member(self, context, member):
|
def create_member(self, context, member):
|
||||||
m = super(LoadBalancerPlugin, self).create_member(context, member)
|
m = super(LoadBalancerPlugin, self).create_member(context, member)
|
||||||
self.driver.create_member(context, m)
|
driver = self._get_driver_for_pool(context, m['pool_id'])
|
||||||
|
driver.create_member(context, m)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def update_member(self, context, id, member):
|
def update_member(self, context, id, member):
|
||||||
@ -137,7 +190,8 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
member['member']['status'] = constants.PENDING_UPDATE
|
member['member']['status'] = constants.PENDING_UPDATE
|
||||||
old_member = self.get_member(context, id)
|
old_member = self.get_member(context, id)
|
||||||
m = super(LoadBalancerPlugin, self).update_member(context, id, member)
|
m = super(LoadBalancerPlugin, self).update_member(context, id, member)
|
||||||
self.driver.update_member(context, old_member, m)
|
driver = self._get_driver_for_pool(context, m['pool_id'])
|
||||||
|
driver.update_member(context, old_member, m)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def _delete_db_member(self, context, id):
|
def _delete_db_member(self, context, id):
|
||||||
@ -145,17 +199,17 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
super(LoadBalancerPlugin, self).delete_member(context, id)
|
super(LoadBalancerPlugin, self).delete_member(context, id)
|
||||||
|
|
||||||
def delete_member(self, context, id):
|
def delete_member(self, context, id):
|
||||||
self.update_status(context, loadbalancer_db.Member,
|
self.update_status(context, ldb.Member,
|
||||||
id, constants.PENDING_DELETE)
|
id, constants.PENDING_DELETE)
|
||||||
m = self.get_member(context, id)
|
m = self.get_member(context, id)
|
||||||
self.driver.delete_member(context, m)
|
driver = self._get_driver_for_pool(context, m['pool_id'])
|
||||||
|
driver.delete_member(context, m)
|
||||||
|
|
||||||
def create_health_monitor(self, context, health_monitor):
|
def create_health_monitor(self, context, health_monitor):
|
||||||
hm = super(LoadBalancerPlugin, self).create_health_monitor(
|
hm = super(LoadBalancerPlugin, self).create_health_monitor(
|
||||||
context,
|
context,
|
||||||
health_monitor
|
health_monitor
|
||||||
)
|
)
|
||||||
self.driver.create_health_monitor(context, hm)
|
|
||||||
return hm
|
return hm
|
||||||
|
|
||||||
def update_health_monitor(self, context, id, health_monitor):
|
def update_health_monitor(self, context, id, health_monitor):
|
||||||
@ -168,11 +222,12 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
|
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
qry = context.session.query(
|
qry = context.session.query(
|
||||||
loadbalancer_db.PoolMonitorAssociation
|
ldb.PoolMonitorAssociation
|
||||||
).filter_by(monitor_id=hm['id'])
|
).filter_by(monitor_id=hm['id']).join(ldb.Pool)
|
||||||
for assoc in qry:
|
for assoc in qry:
|
||||||
self.driver.update_health_monitor(context, old_hm,
|
driver = self._get_driver_for_pool(context, assoc['pool_id'])
|
||||||
hm, assoc['pool_id'])
|
driver.update_health_monitor(context, old_hm,
|
||||||
|
hm, assoc['pool_id'])
|
||||||
return hm
|
return hm
|
||||||
|
|
||||||
def _delete_db_pool_health_monitor(self, context, hm_id, pool_id):
|
def _delete_db_pool_health_monitor(self, context, hm_id, pool_id):
|
||||||
@ -187,13 +242,14 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
hm = self.get_health_monitor(context, id)
|
hm = self.get_health_monitor(context, id)
|
||||||
qry = context.session.query(
|
qry = context.session.query(
|
||||||
loadbalancer_db.PoolMonitorAssociation
|
ldb.PoolMonitorAssociation
|
||||||
).filter_by(monitor_id=id)
|
).filter_by(monitor_id=id).join(ldb.Pool)
|
||||||
for assoc in qry:
|
for assoc in qry:
|
||||||
self.driver.delete_pool_health_monitor(context,
|
driver = self._get_driver_for_pool(context, assoc['pool_id'])
|
||||||
hm,
|
driver.delete_pool_health_monitor(context,
|
||||||
assoc['pool_id'])
|
hm,
|
||||||
self.driver.delete_health_monitor(context, hm)
|
assoc['pool_id'])
|
||||||
|
super(LoadBalancerPlugin, self).delete_health_monitor(context, id)
|
||||||
|
|
||||||
def create_pool_health_monitor(self, context, health_monitor, pool_id):
|
def create_pool_health_monitor(self, context, health_monitor, pool_id):
|
||||||
retval = super(LoadBalancerPlugin, self).create_pool_health_monitor(
|
retval = super(LoadBalancerPlugin, self).create_pool_health_monitor(
|
||||||
@ -203,19 +259,20 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
)
|
)
|
||||||
monitor_id = health_monitor['health_monitor']['id']
|
monitor_id = health_monitor['health_monitor']['id']
|
||||||
hm = self.get_health_monitor(context, monitor_id)
|
hm = self.get_health_monitor(context, monitor_id)
|
||||||
self.driver.create_pool_health_monitor(
|
driver = self._get_driver_for_pool(context, pool_id)
|
||||||
context, hm, pool_id)
|
driver.create_pool_health_monitor(context, hm, pool_id)
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
def delete_pool_health_monitor(self, context, id, pool_id):
|
def delete_pool_health_monitor(self, context, id, pool_id):
|
||||||
self.update_pool_health_monitor(context, id, pool_id,
|
self.update_pool_health_monitor(context, id, pool_id,
|
||||||
constants.PENDING_DELETE)
|
constants.PENDING_DELETE)
|
||||||
hm = self.get_health_monitor(context, id)
|
hm = self.get_health_monitor(context, id)
|
||||||
self.driver.delete_pool_health_monitor(
|
driver = self._get_driver_for_pool(context, pool_id)
|
||||||
context, hm, pool_id)
|
driver.delete_pool_health_monitor(context, hm, pool_id)
|
||||||
|
|
||||||
def stats(self, context, pool_id):
|
def stats(self, context, pool_id):
|
||||||
stats_data = self.driver.stats(context, pool_id)
|
driver = self._get_driver_for_pool(context, pool_id)
|
||||||
|
stats_data = driver.stats(context, pool_id)
|
||||||
# if we get something from the driver -
|
# if we get something from the driver -
|
||||||
# update the db and return the value from db
|
# update the db and return the value from db
|
||||||
# else - return what we have in db
|
# else - return what we have in db
|
||||||
@ -233,10 +290,13 @@ class LoadBalancerPlugin(loadbalancer_db.LoadBalancerPluginDb,
|
|||||||
|
|
||||||
pool = self.get_pool(context, vip['pool_id'])
|
pool = self.get_pool(context, vip['pool_id'])
|
||||||
vip['pool'] = pool
|
vip['pool'] = pool
|
||||||
vip['members'] = [
|
vip['members'] = [self.get_member(context, member_id)
|
||||||
self.get_member(context, member_id)
|
for member_id in pool['members']]
|
||||||
for member_id in pool['members']]
|
vip['health_monitors'] = [self.get_health_monitor(context, hm_id)
|
||||||
vip['health_monitors'] = [
|
for hm_id in pool['health_monitors']]
|
||||||
self.get_health_monitor(context, hm_id)
|
|
||||||
for hm_id in pool['health_monitors']]
|
|
||||||
return vip
|
return vip
|
||||||
|
|
||||||
|
def validate_provider(self, provider):
|
||||||
|
if provider not in self.drivers:
|
||||||
|
raise pconf.ServiceProviderNotFound(
|
||||||
|
provider=provider, service_type=constants.LOADBALANCER)
|
||||||
|
@ -42,8 +42,8 @@ def parse_service_provider_opt():
|
|||||||
"""Parse service definition opts and returns result."""
|
"""Parse service definition opts and returns result."""
|
||||||
def validate_name(name):
|
def validate_name(name):
|
||||||
if len(name) > 255:
|
if len(name) > 255:
|
||||||
raise n_exc.Invalid("Provider name is limited by 255 characters:"
|
raise n_exc.Invalid(
|
||||||
" %s" % name)
|
_("Provider name is limited by 255 characters: %s") % name)
|
||||||
|
|
||||||
svc_providers_opt = cfg.CONF.service_providers.service_provider
|
svc_providers_opt = cfg.CONF.service_providers.service_provider
|
||||||
res = []
|
res = []
|
||||||
@ -79,16 +79,21 @@ def parse_service_provider_opt():
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class ServiceProviderNotFound(n_exc.NotFound):
|
class ServiceProviderNotFound(n_exc.InvalidInput):
|
||||||
message = _("Service provider could not be found "
|
message = _("Service provider '%(provider)s' could not be found "
|
||||||
"for service type %(service_type)s")
|
"for service type %(service_type)s")
|
||||||
|
|
||||||
|
|
||||||
class DefaultServiceProviderNotFound(ServiceProviderNotFound):
|
class DefaultServiceProviderNotFound(n_exc.InvalidInput):
|
||||||
message = _("Service type %(service_type)s does not have a default "
|
message = _("Service type %(service_type)s does not have a default "
|
||||||
"service provider")
|
"service provider")
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceProviderAlreadyAssociated(n_exc.Conflict):
|
||||||
|
message = _("Resource '%(resource_id)s' is already associated with "
|
||||||
|
"provider '%(provider)s' for service type '%(service_type)s'")
|
||||||
|
|
||||||
|
|
||||||
class ProviderConfiguration(object):
|
class ProviderConfiguration(object):
|
||||||
def __init__(self, prov_data):
|
def __init__(self, prov_data):
|
||||||
self.providers = {}
|
self.providers = {}
|
||||||
|
@ -18,6 +18,12 @@
|
|||||||
import abc
|
import abc
|
||||||
|
|
||||||
from neutron.api import extensions
|
from neutron.api import extensions
|
||||||
|
from neutron.db import servicetype_db as sdb
|
||||||
|
from neutron.openstack.common import importutils
|
||||||
|
from neutron.openstack.common import log as logging
|
||||||
|
from neutron.services import provider_configuration as pconf
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ServicePluginBase(extensions.PluginInterface):
|
class ServicePluginBase(extensions.PluginInterface):
|
||||||
@ -46,3 +52,49 @@ class ServicePluginBase(extensions.PluginInterface):
|
|||||||
def get_plugin_description(self):
|
def get_plugin_description(self):
|
||||||
"""Return string description of the plugin."""
|
"""Return string description of the plugin."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def load_drivers(service_type, plugin):
|
||||||
|
"""Loads drivers for specific service.
|
||||||
|
|
||||||
|
Passes plugin instance to driver's constructor
|
||||||
|
"""
|
||||||
|
service_type_manager = sdb.ServiceTypeManager.get_instance()
|
||||||
|
providers = (service_type_manager.
|
||||||
|
get_service_providers(
|
||||||
|
None,
|
||||||
|
filters={'service_type': [service_type]})
|
||||||
|
)
|
||||||
|
if not providers:
|
||||||
|
msg = (_("No providers specified for '%s' service, exiting") %
|
||||||
|
service_type)
|
||||||
|
LOG.error(msg)
|
||||||
|
raise SystemExit(msg)
|
||||||
|
|
||||||
|
drivers = {}
|
||||||
|
for provider in providers:
|
||||||
|
try:
|
||||||
|
drivers[provider['name']] = importutils.import_object(
|
||||||
|
provider['driver'], plugin
|
||||||
|
)
|
||||||
|
LOG.debug(_("Loaded '%(provider)s' provider for service "
|
||||||
|
"%(service_type)s"),
|
||||||
|
{'provider': provider['driver'],
|
||||||
|
'service_type': service_type})
|
||||||
|
except ImportError:
|
||||||
|
LOG.exception(_("Error loading provider '%(provider)s' for "
|
||||||
|
"service %(service_type)s"),
|
||||||
|
{'provider': provider['driver'],
|
||||||
|
'service_type': service_type})
|
||||||
|
raise
|
||||||
|
|
||||||
|
default_provider = None
|
||||||
|
try:
|
||||||
|
provider = service_type_manager.get_default_service_provider(
|
||||||
|
None, service_type)
|
||||||
|
default_provider = provider['name']
|
||||||
|
except pconf.DefaultServiceProviderNotFound:
|
||||||
|
LOG.info(_("Default provider is not specified for service type %s"),
|
||||||
|
service_type)
|
||||||
|
|
||||||
|
return drivers, default_provider
|
||||||
|
@ -28,12 +28,14 @@ from neutron.common import config
|
|||||||
from neutron import context
|
from neutron import context
|
||||||
import neutron.db.l3_db # noqa
|
import neutron.db.l3_db # noqa
|
||||||
from neutron.db.loadbalancer import loadbalancer_db as ldb
|
from neutron.db.loadbalancer import loadbalancer_db as ldb
|
||||||
|
from neutron.db import servicetype_db as sdb
|
||||||
import neutron.extensions
|
import neutron.extensions
|
||||||
from neutron.extensions import loadbalancer
|
from neutron.extensions import loadbalancer
|
||||||
from neutron.plugins.common import constants
|
from neutron.plugins.common import constants
|
||||||
from neutron.services.loadbalancer import (
|
from neutron.services.loadbalancer import (
|
||||||
plugin as loadbalancer_plugin
|
plugin as loadbalancer_plugin
|
||||||
)
|
)
|
||||||
|
from neutron.services import provider_configuration as pconf
|
||||||
from neutron.tests.unit import test_db_plugin
|
from neutron.tests.unit import test_db_plugin
|
||||||
|
|
||||||
|
|
||||||
@ -90,10 +92,9 @@ class LoadBalancerTestMixin(object):
|
|||||||
'protocol': protocol,
|
'protocol': protocol,
|
||||||
'admin_state_up': admin_state_up,
|
'admin_state_up': admin_state_up,
|
||||||
'tenant_id': self._tenant_id}}
|
'tenant_id': self._tenant_id}}
|
||||||
for arg in ('description'):
|
for arg in ('description', 'provider'):
|
||||||
if arg in kwargs and kwargs[arg] is not None:
|
if arg in kwargs and kwargs[arg] is not None:
|
||||||
data['pool'][arg] = kwargs[arg]
|
data['pool'][arg] = kwargs[arg]
|
||||||
|
|
||||||
pool_req = self.new_create_request('pools', data, fmt)
|
pool_req = self.new_create_request('pools', data, fmt)
|
||||||
pool_res = pool_req.get_response(self.ext_api)
|
pool_res = pool_req.get_response(self.ext_api)
|
||||||
if expected_res_status:
|
if expected_res_status:
|
||||||
@ -254,8 +255,19 @@ class LoadBalancerTestMixin(object):
|
|||||||
|
|
||||||
class LoadBalancerPluginDbTestCase(LoadBalancerTestMixin,
|
class LoadBalancerPluginDbTestCase(LoadBalancerTestMixin,
|
||||||
test_db_plugin.NeutronDbPluginV2TestCase):
|
test_db_plugin.NeutronDbPluginV2TestCase):
|
||||||
def setUp(self, core_plugin=None, lb_plugin=None):
|
def setUp(self, core_plugin=None, lb_plugin=None, lbaas_provider=None):
|
||||||
service_plugins = {'lb_plugin_name': DB_LB_PLUGIN_KLASS}
|
service_plugins = {'lb_plugin_name': DB_LB_PLUGIN_KLASS}
|
||||||
|
if not lbaas_provider:
|
||||||
|
lbaas_provider = (
|
||||||
|
constants.LOADBALANCER +
|
||||||
|
':lbaas:neutron.services.loadbalancer.'
|
||||||
|
'drivers.noop.noop_driver.NoopLbaaSDriver:default')
|
||||||
|
cfg.CONF.set_override('service_provider',
|
||||||
|
[lbaas_provider],
|
||||||
|
'service_providers')
|
||||||
|
#force service type manager to reload configuration:
|
||||||
|
sdb.ServiceTypeManager._instance = None
|
||||||
|
|
||||||
super(LoadBalancerPluginDbTestCase, self).setUp(
|
super(LoadBalancerPluginDbTestCase, self).setUp(
|
||||||
service_plugins=service_plugins
|
service_plugins=service_plugins
|
||||||
)
|
)
|
||||||
@ -271,6 +283,7 @@ class LoadBalancerPluginDbTestCase(LoadBalancerTestMixin,
|
|||||||
get_lbaas_agent_patcher.start().return_value = mock_lbaas_agent
|
get_lbaas_agent_patcher.start().return_value = mock_lbaas_agent
|
||||||
mock_lbaas_agent.__getitem__.return_value = {'host': 'host'}
|
mock_lbaas_agent.__getitem__.return_value = {'host': 'host'}
|
||||||
self.addCleanup(mock.patch.stopall)
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
self.addCleanup(cfg.CONF.reset)
|
||||||
|
|
||||||
ext_mgr = PluginAwareExtensionManager(
|
ext_mgr = PluginAwareExtensionManager(
|
||||||
extensions_path,
|
extensions_path,
|
||||||
@ -282,10 +295,6 @@ class LoadBalancerPluginDbTestCase(LoadBalancerTestMixin,
|
|||||||
|
|
||||||
class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
cfg.CONF.set_override('driver_fqn',
|
|
||||||
'neutron.services.loadbalancer.drivers.noop'
|
|
||||||
'.noop_driver.NoopLbaaSDriver',
|
|
||||||
group='LBAAS')
|
|
||||||
self.addCleanup(cfg.CONF.reset)
|
self.addCleanup(cfg.CONF.reset)
|
||||||
super(TestLoadBalancer, self).setUp()
|
super(TestLoadBalancer, self).setUp()
|
||||||
|
|
||||||
@ -566,6 +575,48 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
|||||||
pool = self.pool(name=name, lb_method='UNSUPPORTED')
|
pool = self.pool(name=name, lb_method='UNSUPPORTED')
|
||||||
self.assertRaises(webob.exc.HTTPClientError, pool.__enter__)
|
self.assertRaises(webob.exc.HTTPClientError, pool.__enter__)
|
||||||
|
|
||||||
|
def _create_pool_directly_via_plugin(self, provider_name):
|
||||||
|
#default provider will be haproxy
|
||||||
|
prov1 = (constants.LOADBALANCER +
|
||||||
|
':lbaas:neutron.services.loadbalancer.'
|
||||||
|
'drivers.noop.noop_driver.NoopLbaaSDriver')
|
||||||
|
prov2 = (constants.LOADBALANCER +
|
||||||
|
':haproxy:neutron.services.loadbalancer.'
|
||||||
|
'drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver'
|
||||||
|
':default')
|
||||||
|
cfg.CONF.set_override('service_provider',
|
||||||
|
[prov1, prov2],
|
||||||
|
'service_providers')
|
||||||
|
sdb.ServiceTypeManager._instance = None
|
||||||
|
self.plugin = loadbalancer_plugin.LoadBalancerPlugin()
|
||||||
|
with self.subnet() as subnet:
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
#create pool with another provider - lbaas
|
||||||
|
#which is noop driver
|
||||||
|
pool = {'name': 'pool1',
|
||||||
|
'subnet_id': subnet['subnet']['id'],
|
||||||
|
'lb_method': 'ROUND_ROBIN',
|
||||||
|
'protocol': 'HTTP',
|
||||||
|
'admin_state_up': True,
|
||||||
|
'tenant_id': self._tenant_id,
|
||||||
|
'provider': provider_name,
|
||||||
|
'description': ''}
|
||||||
|
self.plugin.create_pool(ctx, {'pool': pool})
|
||||||
|
assoc = ctx.session.query(sdb.ProviderResourceAssociation).one()
|
||||||
|
self.assertEqual(assoc.provider_name,
|
||||||
|
pconf.normalize_provider_name(provider_name))
|
||||||
|
|
||||||
|
def test_create_pool_another_provider(self):
|
||||||
|
self._create_pool_directly_via_plugin('lbaas')
|
||||||
|
|
||||||
|
def test_create_pool_unnormalized_provider_name(self):
|
||||||
|
self._create_pool_directly_via_plugin('LBAAS')
|
||||||
|
|
||||||
|
def test_create_pool_unexisting_provider(self):
|
||||||
|
self.assertRaises(
|
||||||
|
pconf.ServiceProviderNotFound,
|
||||||
|
self._create_pool_directly_via_plugin, 'unexisting')
|
||||||
|
|
||||||
def test_create_pool(self):
|
def test_create_pool(self):
|
||||||
name = "pool1"
|
name = "pool1"
|
||||||
keys = [('name', name),
|
keys = [('name', name),
|
||||||
@ -1211,7 +1262,7 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
|||||||
res)
|
res)
|
||||||
|
|
||||||
def test_driver_call_create_pool_health_monitor(self):
|
def test_driver_call_create_pool_health_monitor(self):
|
||||||
with mock.patch.object(self.plugin.driver,
|
with mock.patch.object(self.plugin.drivers['lbaas'],
|
||||||
'create_pool_health_monitor') as driver_call:
|
'create_pool_health_monitor') as driver_call:
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
self.pool(),
|
self.pool(),
|
||||||
@ -1342,6 +1393,30 @@ class TestLoadBalancer(LoadBalancerPluginDbTestCase):
|
|||||||
self.assertEqual(assoc['status'], 'ACTIVE')
|
self.assertEqual(assoc['status'], 'ACTIVE')
|
||||||
self.assertEqual(assoc['status_description'], 'ok')
|
self.assertEqual(assoc['status_description'], 'ok')
|
||||||
|
|
||||||
|
def test_check_orphan_pool_associations(self):
|
||||||
|
with contextlib.nested(
|
||||||
|
#creating pools with default noop driver
|
||||||
|
self.pool(),
|
||||||
|
self.pool()
|
||||||
|
) as (p1, p2):
|
||||||
|
#checking that 3 associations exist
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
qry = ctx.session.query(sdb.ProviderResourceAssociation)
|
||||||
|
self.assertEqual(qry.count(), 2)
|
||||||
|
#removing driver
|
||||||
|
cfg.CONF.set_override('service_provider',
|
||||||
|
[constants.LOADBALANCER +
|
||||||
|
':lbaas1:neutron.services.loadbalancer.'
|
||||||
|
'drivers.noop.noop_driver.'
|
||||||
|
'NoopLbaaSDriver:default'],
|
||||||
|
'service_providers')
|
||||||
|
sdb.ServiceTypeManager._instance = None
|
||||||
|
# calling _remove_orphan... in constructor
|
||||||
|
self.assertRaises(
|
||||||
|
SystemExit,
|
||||||
|
loadbalancer_plugin.LoadBalancerPlugin
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestLoadBalancerXML(TestLoadBalancer):
|
class TestLoadBalancerXML(TestLoadBalancer):
|
||||||
fmt = 'xml'
|
fmt = 'xml'
|
||||||
|
@ -21,6 +21,7 @@ import mock
|
|||||||
from neutron.common import exceptions
|
from neutron.common import exceptions
|
||||||
from neutron import context
|
from neutron import context
|
||||||
from neutron.db.loadbalancer import loadbalancer_db as ldb
|
from neutron.db.loadbalancer import loadbalancer_db as ldb
|
||||||
|
from neutron.db import servicetype_db as st_db
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.openstack.common import uuidutils
|
from neutron.openstack.common import uuidutils
|
||||||
from neutron.plugins.common import constants
|
from neutron.plugins.common import constants
|
||||||
@ -35,8 +36,12 @@ class TestLoadBalancerPluginBase(
|
|||||||
test_db_loadbalancer.LoadBalancerPluginDbTestCase):
|
test_db_loadbalancer.LoadBalancerPluginDbTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestLoadBalancerPluginBase, self).setUp()
|
# needed to reload provider configuration
|
||||||
|
st_db.ServiceTypeManager._instance = None
|
||||||
|
super(TestLoadBalancerPluginBase, self).setUp(
|
||||||
|
lbaas_provider=('LOADBALANCER:lbaas:neutron.services.'
|
||||||
|
'loadbalancer.drivers.haproxy.plugin_driver.'
|
||||||
|
'HaproxyOnHostPluginDriver:default'))
|
||||||
# create another API instance to make testing easier
|
# create another API instance to make testing easier
|
||||||
# pass a mock to our API instance
|
# pass a mock to our API instance
|
||||||
|
|
||||||
@ -328,6 +333,13 @@ class TestLoadBalancerPluginNotificationWrapper(TestLoadBalancerPluginBase):
|
|||||||
'.plugin_driver.HaproxyOnHostPluginDriver'
|
'.plugin_driver.HaproxyOnHostPluginDriver'
|
||||||
'.create_pool').start()
|
'.create_pool').start()
|
||||||
|
|
||||||
|
self.mock_get_driver = mock.patch.object(self.plugin_instance,
|
||||||
|
'_get_driver')
|
||||||
|
self.mock_get_driver.return_value = (plugin_driver.
|
||||||
|
HaproxyOnHostPluginDriver(
|
||||||
|
self.plugin_instance
|
||||||
|
))
|
||||||
|
|
||||||
self.addCleanup(mock.patch.stopall)
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
|
||||||
def test_create_vip(self):
|
def test_create_vip(self):
|
||||||
@ -387,6 +399,7 @@ class TestLoadBalancerPluginNotificationWrapper(TestLoadBalancerPluginBase):
|
|||||||
with self.pool() as pool:
|
with self.pool() as pool:
|
||||||
pool['pool']['status'] = 'INACTIVE'
|
pool['pool']['status'] = 'INACTIVE'
|
||||||
ctx = context.get_admin_context()
|
ctx = context.get_admin_context()
|
||||||
|
del pool['pool']['provider']
|
||||||
self.plugin_instance.update_pool(ctx, pool['pool']['id'], pool)
|
self.plugin_instance.update_pool(ctx, pool['pool']['id'], pool)
|
||||||
self.mock_api.destroy_pool.assert_called_once_with(
|
self.mock_api.destroy_pool.assert_called_once_with(
|
||||||
mock.ANY, pool['pool']['id'], 'host')
|
mock.ANY, pool['pool']['id'], 'host')
|
||||||
@ -396,6 +409,7 @@ class TestLoadBalancerPluginNotificationWrapper(TestLoadBalancerPluginBase):
|
|||||||
def test_update_pool_no_vip_id(self):
|
def test_update_pool_no_vip_id(self):
|
||||||
with self.pool() as pool:
|
with self.pool() as pool:
|
||||||
ctx = context.get_admin_context()
|
ctx = context.get_admin_context()
|
||||||
|
del pool['pool']['provider']
|
||||||
self.plugin_instance.update_pool(ctx, pool['pool']['id'], pool)
|
self.plugin_instance.update_pool(ctx, pool['pool']['id'], pool)
|
||||||
self.assertFalse(self.mock_api.destroy_pool.called)
|
self.assertFalse(self.mock_api.destroy_pool.called)
|
||||||
self.assertFalse(self.mock_api.reload_pool.called)
|
self.assertFalse(self.mock_api.reload_pool.called)
|
||||||
@ -405,6 +419,7 @@ class TestLoadBalancerPluginNotificationWrapper(TestLoadBalancerPluginBase):
|
|||||||
with self.pool() as pool:
|
with self.pool() as pool:
|
||||||
with self.vip(pool=pool):
|
with self.vip(pool=pool):
|
||||||
ctx = context.get_admin_context()
|
ctx = context.get_admin_context()
|
||||||
|
del pool['pool']['provider']
|
||||||
self.plugin_instance.update_pool(ctx, pool['pool']['id'], pool)
|
self.plugin_instance.update_pool(ctx, pool['pool']['id'], pool)
|
||||||
self.mock_api.reload_pool.assert_called_once_with(
|
self.mock_api.reload_pool.assert_called_once_with(
|
||||||
mock.ANY, pool['pool']['id'], 'host')
|
mock.ANY, pool['pool']['id'], 'host')
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
from oslo.config import cfg
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from neutron.api import extensions
|
from neutron.api import extensions
|
||||||
@ -68,6 +69,15 @@ class LBaaSAgentSchedulerTestCase(test_agent_ext_plugin.AgentDBTestMixIn,
|
|||||||
self.saved_attr_map[resource] = attrs.copy()
|
self.saved_attr_map[resource] = attrs.copy()
|
||||||
service_plugins = {
|
service_plugins = {
|
||||||
'lb_plugin_name': test_db_loadbalancer.DB_LB_PLUGIN_KLASS}
|
'lb_plugin_name': test_db_loadbalancer.DB_LB_PLUGIN_KLASS}
|
||||||
|
|
||||||
|
#default provider should support agent scheduling
|
||||||
|
cfg.CONF.set_override(
|
||||||
|
'service_provider',
|
||||||
|
[('LOADBALANCER:lbaas:neutron.services.'
|
||||||
|
'loadbalancer.drivers.haproxy.plugin_driver.'
|
||||||
|
'HaproxyOnHostPluginDriver:default')],
|
||||||
|
'service_providers')
|
||||||
|
|
||||||
super(LBaaSAgentSchedulerTestCase, self).setUp(
|
super(LBaaSAgentSchedulerTestCase, self).setUp(
|
||||||
self.plugin_str, service_plugins=service_plugins)
|
self.plugin_str, service_plugins=service_plugins)
|
||||||
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
|
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
|
||||||
@ -131,7 +141,7 @@ class LBaaSAgentSchedulerTestCase(test_agent_ext_plugin.AgentDBTestMixIn,
|
|||||||
self.assertRaises(lbaas_agentscheduler.NoEligibleLbaasAgent,
|
self.assertRaises(lbaas_agentscheduler.NoEligibleLbaasAgent,
|
||||||
lbaas_plugin.create_pool, self.adminContext, pool)
|
lbaas_plugin.create_pool, self.adminContext, pool)
|
||||||
|
|
||||||
def test_schedule_poll_with_down_agent(self):
|
def test_schedule_pool_with_down_agent(self):
|
||||||
lbaas_hosta = {
|
lbaas_hosta = {
|
||||||
'binary': 'neutron-loadbalancer-agent',
|
'binary': 'neutron-loadbalancer-agent',
|
||||||
'host': LBAAS_HOSTA,
|
'host': LBAAS_HOSTA,
|
||||||
@ -153,6 +163,7 @@ class LBaaSAgentSchedulerTestCase(test_agent_ext_plugin.AgentDBTestMixIn,
|
|||||||
'subnet_id': 'test',
|
'subnet_id': 'test',
|
||||||
'lb_method': 'ROUND_ROBIN',
|
'lb_method': 'ROUND_ROBIN',
|
||||||
'protocol': 'HTTP',
|
'protocol': 'HTTP',
|
||||||
|
'provider': 'lbaas',
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': 'test',
|
'tenant_id': 'test',
|
||||||
'description': 'test'}}
|
'description': 'test'}}
|
||||||
|
@ -23,7 +23,7 @@ from webob import exc
|
|||||||
import webtest
|
import webtest
|
||||||
|
|
||||||
from neutron.api import extensions
|
from neutron.api import extensions
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes as attr
|
||||||
from neutron.common import config
|
from neutron.common import config
|
||||||
from neutron.extensions import loadbalancer
|
from neutron.extensions import loadbalancer
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
@ -45,7 +45,7 @@ class LoadBalancerTestExtensionManager(object):
|
|||||||
# This is done here as the setup process won't
|
# This is done here as the setup process won't
|
||||||
# initialize the main API router which extends
|
# initialize the main API router which extends
|
||||||
# the global attribute map
|
# the global attribute map
|
||||||
attributes.RESOURCE_ATTRIBUTE_MAP.update(
|
attr.RESOURCE_ATTRIBUTE_MAP.update(
|
||||||
loadbalancer.RESOURCE_ATTRIBUTE_MAP)
|
loadbalancer.RESOURCE_ATTRIBUTE_MAP)
|
||||||
return loadbalancer.Loadbalancer.get_resources()
|
return loadbalancer.Loadbalancer.get_resources()
|
||||||
|
|
||||||
@ -203,6 +203,7 @@ class LoadBalancerExtensionTestCase(testlib_api.WebTestCase):
|
|||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'tenant_id': _uuid()}}
|
'tenant_id': _uuid()}}
|
||||||
return_value = copy.copy(data['pool'])
|
return_value = copy.copy(data['pool'])
|
||||||
|
return_value['provider'] = 'lbaas'
|
||||||
return_value.update({'status': "ACTIVE", 'id': pool_id})
|
return_value.update({'status': "ACTIVE", 'id': pool_id})
|
||||||
|
|
||||||
instance = self.plugin.return_value
|
instance = self.plugin.return_value
|
||||||
@ -210,6 +211,7 @@ class LoadBalancerExtensionTestCase(testlib_api.WebTestCase):
|
|||||||
res = self.api.post(_get_path('lb/pools', fmt=self.fmt),
|
res = self.api.post(_get_path('lb/pools', fmt=self.fmt),
|
||||||
self.serialize(data),
|
self.serialize(data),
|
||||||
content_type='application/%s' % self.fmt)
|
content_type='application/%s' % self.fmt)
|
||||||
|
data['pool']['provider'] = attr.ATTR_NOT_SPECIFIED
|
||||||
instance.create_pool.assert_called_with(mock.ANY,
|
instance.create_pool.assert_called_with(mock.ANY,
|
||||||
pool=data)
|
pool=data)
|
||||||
self.assertEqual(res.status_int, exc.HTTPCreated.code)
|
self.assertEqual(res.status_int, exc.HTTPCreated.code)
|
||||||
|
Loading…
Reference in New Issue
Block a user