Get Me A LB
Allows a request to be passed to the lbaas API that contains the full graph structure of a load balancer. This means that an entire load balancer graph can be created in one POST to the API, or a partial graph can be. This creates a new driver method to the driver interface that when implemented will be called when a full or partial graph is attempted to be created. If the driver does not implement this method, then the request is not allowed. Co-Author: Trevor Vardeman <trevor.vardeman@rackspace.com> Closes-Bug: #1463202 Depends-On: Id3a5ddb8efded8c6ad72a7118424ec01c777318d Change-Id: Ic16ca4cb3cb407d9e91eea74315a39bf86c654f3
This commit is contained in:
parent
b0b6a0aa85
commit
eafbfd23b2
|
@ -21,12 +21,13 @@ from neutron.callbacks import resources
|
|||
from neutron.db import common_db_mixin as base_db
|
||||
from neutron import manager
|
||||
from neutron.plugins.common import constants
|
||||
from neutron_lib import constants as n_constants
|
||||
from neutron_lib import constants as n_const
|
||||
from neutron_lib import exceptions as n_exc
|
||||
from oslo_db import exception
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import uuidutils
|
||||
from sqlalchemy import exc as sqlalchemy_exc
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
|
@ -92,17 +93,17 @@ class LoadBalancerPluginDbv2(base_db.CommonDbMixin,
|
|||
# resolve subnet and create port
|
||||
subnet = self._core_plugin.get_subnet(context, lb_db.vip_subnet_id)
|
||||
fixed_ip = {'subnet_id': subnet['id']}
|
||||
if ip_address and ip_address != n_constants.ATTR_NOT_SPECIFIED:
|
||||
if ip_address and ip_address != n_const.ATTR_NOT_SPECIFIED:
|
||||
fixed_ip['ip_address'] = ip_address
|
||||
|
||||
port_data = {
|
||||
'tenant_id': lb_db.tenant_id,
|
||||
'name': 'loadbalancer-' + lb_db.id,
|
||||
'network_id': subnet['network_id'],
|
||||
'mac_address': n_constants.ATTR_NOT_SPECIFIED,
|
||||
'mac_address': n_const.ATTR_NOT_SPECIFIED,
|
||||
'admin_state_up': False,
|
||||
'device_id': lb_db.id,
|
||||
'device_owner': n_constants.DEVICE_OWNER_LOADBALANCERV2,
|
||||
'device_owner': n_const.DEVICE_OWNER_LOADBALANCERV2,
|
||||
'fixed_ips': [fixed_ip]
|
||||
}
|
||||
|
||||
|
@ -203,6 +204,66 @@ class LoadBalancerPluginDbv2(base_db.CommonDbMixin,
|
|||
model_db.operating_status != operating_status):
|
||||
model_db.operating_status = operating_status
|
||||
|
||||
def create_loadbalancer_graph(self, context, loadbalancer,
|
||||
allocate_vip=True):
|
||||
l7policies_ids = []
|
||||
with context.session.begin(subtransactions=True):
|
||||
listeners = loadbalancer.pop('listeners', [])
|
||||
lb_db = self.create_loadbalancer(context, loadbalancer,
|
||||
allocate_vip=allocate_vip)
|
||||
for listener in listeners:
|
||||
listener['loadbalancer_id'] = lb_db.id
|
||||
default_pool = listener.pop('default_pool', None)
|
||||
if (default_pool and
|
||||
default_pool != n_const.ATTR_NOT_SPECIFIED):
|
||||
default_pool['loadbalancer_id'] = lb_db.id
|
||||
hm = default_pool.pop('healthmonitor', None)
|
||||
if hm and hm != n_const.ATTR_NOT_SPECIFIED:
|
||||
hm_db = self.create_healthmonitor(context, hm)
|
||||
default_pool['healthmonitor_id'] = hm_db.id
|
||||
members = default_pool.pop('members', [])
|
||||
pool_db = self.create_pool(context, default_pool)
|
||||
listener['default_pool_id'] = pool_db.id
|
||||
for member in members:
|
||||
member['pool_id'] = pool_db.id
|
||||
self.create_pool_member(context, member, pool_db.id)
|
||||
l7policies = listener.pop('l7policies', None)
|
||||
listener_db = self.create_listener(context, listener)
|
||||
if (l7policies and l7policies !=
|
||||
n_const.ATTR_NOT_SPECIFIED):
|
||||
for l7policy in l7policies:
|
||||
l7policy['listener_id'] = listener_db.id
|
||||
redirect_pool = l7policy.pop('redirect_pool', None)
|
||||
l7rules = l7policy.pop('rules', [])
|
||||
if (redirect_pool and redirect_pool !=
|
||||
n_const.ATTR_NOT_SPECIFIED):
|
||||
redirect_pool['loadbalancer_id'] = lb_db.id
|
||||
rhm = redirect_pool.pop('healthmonitor', None)
|
||||
rmembers = redirect_pool.pop('members', [])
|
||||
if rhm and rhm != n_const.ATTR_NOT_SPECIFIED:
|
||||
rhm_db = self.create_healthmonitor(context,
|
||||
rhm)
|
||||
redirect_pool['healthmonitor_id'] = rhm_db.id
|
||||
rpool_db = self.create_pool(context, redirect_pool)
|
||||
l7policy['redirect_pool_id'] = rpool_db.id
|
||||
for rmember in rmembers:
|
||||
rmember['pool_id'] = rpool_db.id
|
||||
self.create_pool_member(context, rmember,
|
||||
rpool_db.id)
|
||||
l7policy_db = self.create_l7policy(context, l7policy)
|
||||
l7policies_ids.append(l7policy_db.id)
|
||||
if (l7rules and l7rules !=
|
||||
n_const.ATTR_NOT_SPECIFIED):
|
||||
for l7rule in l7rules:
|
||||
self.create_l7policy_rule(
|
||||
context, l7rule, l7policy_db.id)
|
||||
# SQL Alchemy cache issue where l7rules won't show up as intended.
|
||||
for l7policy_id in l7policies_ids:
|
||||
l7policy_db = self._get_resource(context, models.L7Policy,
|
||||
l7policy_id)
|
||||
context.session.expire(l7policy_db)
|
||||
return self.get_loadbalancer(context, lb_db.id)
|
||||
|
||||
def create_loadbalancer(self, context, loadbalancer, allocate_vip=True):
|
||||
with context.session.begin(subtransactions=True):
|
||||
self._load_id(context, loadbalancer)
|
||||
|
@ -215,6 +276,7 @@ class LoadBalancerPluginDbv2(base_db.CommonDbMixin,
|
|||
lb_db.stats = self._create_loadbalancer_stats(
|
||||
context, lb_db.id)
|
||||
context.session.add(lb_db)
|
||||
context.session.flush()
|
||||
|
||||
# create port outside of lb create transaction since it can sometimes
|
||||
# cause lock wait timeouts
|
||||
|
@ -225,7 +287,11 @@ class LoadBalancerPluginDbv2(base_db.CommonDbMixin,
|
|||
vip_address)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
try:
|
||||
context.session.delete(lb_db)
|
||||
except sqlalchemy_exc.InvalidRequestError:
|
||||
# Revert already completed.
|
||||
pass
|
||||
context.session.flush()
|
||||
return data_models.LoadBalancer.from_sqlalchemy_model(lb_db)
|
||||
|
||||
|
@ -247,7 +313,7 @@ class LoadBalancerPluginDbv2(base_db.CommonDbMixin,
|
|||
port_db = self._core_plugin._get_port(context, port_id)
|
||||
except n_exc.PortNotFound:
|
||||
return
|
||||
if port_db['device_owner'] == n_constants.DEVICE_OWNER_LOADBALANCERV2:
|
||||
if port_db['device_owner'] == n_const.DEVICE_OWNER_LOADBALANCERV2:
|
||||
filters = {'vip_port_id': [port_id]}
|
||||
if len(self.get_loadbalancers(context, filters=filters)) > 0:
|
||||
reason = _('has device owner %s') % port_db['device_owner']
|
||||
|
@ -384,14 +450,17 @@ class LoadBalancerPluginDbv2(base_db.CommonDbMixin,
|
|||
# Check for unspecified loadbalancer_id and listener_id and
|
||||
# set to None
|
||||
for id in ['loadbalancer_id', 'default_pool_id']:
|
||||
if listener.get(id) == n_constants.ATTR_NOT_SPECIFIED:
|
||||
if listener.get(id) == n_const.ATTR_NOT_SPECIFIED:
|
||||
listener[id] = None
|
||||
|
||||
self._validate_listener_data(context, listener)
|
||||
sni_container_ids = []
|
||||
if 'sni_container_ids' in listener:
|
||||
sni_container_ids = listener.pop('sni_container_ids')
|
||||
try:
|
||||
listener_db_entry = models.Listener(**listener)
|
||||
except Exception as exc:
|
||||
raise exc
|
||||
for container_id in sni_container_ids:
|
||||
sni = models.SNI(listener_id=listener_db_entry.id,
|
||||
tls_container_id=container_id)
|
||||
|
@ -485,7 +554,7 @@ class LoadBalancerPluginDbv2(base_db.CommonDbMixin,
|
|||
pool['provisioning_status'] = constants.PENDING_CREATE
|
||||
pool['operating_status'] = lb_const.OFFLINE
|
||||
|
||||
session_info = pool.pop('session_persistence')
|
||||
session_info = pool.pop('session_persistence', None)
|
||||
pool_db = models.PoolV2(**pool)
|
||||
|
||||
if session_info:
|
||||
|
@ -667,8 +736,11 @@ class LoadBalancerPluginDbv2(base_db.CommonDbMixin,
|
|||
loadbalancer.stats)
|
||||
|
||||
def create_l7policy(self, context, l7policy):
|
||||
if l7policy['redirect_pool_id'] == n_constants.ATTR_NOT_SPECIFIED:
|
||||
if (l7policy.get('redirect_pool_id') and
|
||||
l7policy['redirect_pool_id'] == n_const.ATTR_NOT_SPECIFIED):
|
||||
l7policy['redirect_pool_id'] = None
|
||||
if not l7policy.get('position'):
|
||||
l7policy['position'] = 2147483647
|
||||
self._validate_l7policy_data(context, l7policy)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
|
|
|
@ -87,6 +87,18 @@ class BaseLoadBalancerManager(driver_mixins.BaseRefreshMixin,
|
|||
driver_mixins.BaseManagerMixin):
|
||||
model_class = models.LoadBalancer
|
||||
|
||||
@property
|
||||
def allows_create_graph(self):
|
||||
"""
|
||||
Can this driver create a load balancer graph in one call.
|
||||
|
||||
Return True if this driver has the capability to create a load balancer
|
||||
and any of its children in one driver call. If this returns True and
|
||||
the user requests the creation of a load balancer graph, then the
|
||||
create_graph method will be called to create the load balancer.
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def allocates_vip(self):
|
||||
"""Does this driver need to allocate its own virtual IPs"""
|
||||
|
@ -163,10 +175,12 @@ def driver_op(func):
|
|||
@wraps(func)
|
||||
def func_wrapper(*args, **kwargs):
|
||||
d = (func.__name__ == 'delete')
|
||||
lb_create = ((func.__name__ == 'create') and
|
||||
isinstance(args[0], BaseLoadBalancerManager))
|
||||
try:
|
||||
r = func(*args, **kwargs)
|
||||
args[0].successful_completion(
|
||||
args[1], args[2], delete=d)
|
||||
args[1], args[2], delete=d, lb_create=lb_create)
|
||||
return r
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
|
|
|
@ -47,6 +47,46 @@ class BaseManagerMixin(object):
|
|||
def delete(self, context, obj):
|
||||
pass
|
||||
|
||||
def _successful_completion_lb_graph(self, context, obj):
|
||||
listeners = obj.listeners
|
||||
obj.listeners = []
|
||||
for listener in listeners:
|
||||
# need to maintain the link from the child to the load balancer
|
||||
listener.loadbalancer = obj
|
||||
pool = listener.default_pool
|
||||
l7_policies = listener.l7_policies
|
||||
if pool:
|
||||
pool.listener = listener
|
||||
hm = pool.healthmonitor
|
||||
if hm:
|
||||
hm.pool = pool
|
||||
self.successful_completion(context, hm)
|
||||
for member in pool.members:
|
||||
member.pool = pool
|
||||
self.successful_completion(context, member)
|
||||
self.successful_completion(context, pool)
|
||||
if l7_policies:
|
||||
for l7policy in l7_policies:
|
||||
l7policy.listener = listener
|
||||
l7rules = l7policy.rules
|
||||
for l7rule in l7rules:
|
||||
l7rule.l7policy = l7policy
|
||||
self.successful_completion(context, l7rule)
|
||||
redirect_pool = l7policy.redirect_pool
|
||||
if redirect_pool:
|
||||
redirect_pool.listener = listener
|
||||
rhm = redirect_pool.healthmonitor
|
||||
if rhm:
|
||||
rhm.pool = redirect_pool
|
||||
self.successful_completion(context, rhm)
|
||||
for rmember in redirect_pool.members:
|
||||
rmember.pool = redirect_pool
|
||||
self.successful_completion(context, rmember)
|
||||
self.successful_completion(context, redirect_pool)
|
||||
self.successful_completion(context, l7policy)
|
||||
self.successful_completion(context, listener)
|
||||
self.successful_completion(context, obj)
|
||||
|
||||
def successful_completion(self, context, obj, delete=False,
|
||||
lb_create=False):
|
||||
"""
|
||||
|
@ -64,6 +104,9 @@ class BaseManagerMixin(object):
|
|||
"""
|
||||
LOG.debug("Starting successful_completion method after a successful "
|
||||
"driver action.")
|
||||
if lb_create and obj.listeners:
|
||||
self._successful_completion_lb_graph(context, obj)
|
||||
return
|
||||
obj_sa_cls = data_models.DATA_MODEL_TO_SA_MODEL_MAP[obj.__class__]
|
||||
if delete:
|
||||
# Check if driver is responsible for vip allocation. If the driver
|
||||
|
|
|
@ -59,6 +59,10 @@ class LoggingNoopCommonManager(object):
|
|||
class LoggingNoopLoadBalancerManager(LoggingNoopCommonManager,
|
||||
driver_base.BaseLoadBalancerManager):
|
||||
|
||||
@property
|
||||
def allows_create_graph(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def allocates_vip(self):
|
||||
LOG.debug('allocates_vip queried')
|
||||
|
|
|
@ -192,6 +192,10 @@ class LoadBalancerManager(driver_base.BaseLoadBalancerManager):
|
|||
s += '/%s' % id
|
||||
return s
|
||||
|
||||
@property
|
||||
def allows_create_graph(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def allocates_vip(self):
|
||||
return cfg.CONF.octavia.allocates_vip
|
||||
|
@ -200,32 +204,42 @@ class LoadBalancerManager(driver_base.BaseLoadBalancerManager):
|
|||
def deletes_cascade(self):
|
||||
return True
|
||||
|
||||
def _construct_args(self, db_lb, create=True, graph=False):
|
||||
args = {'name': db_lb.name,
|
||||
'description': db_lb.description,
|
||||
'enabled': db_lb.admin_state_up}
|
||||
if not create:
|
||||
return args
|
||||
|
||||
create_args = {'project_id': db_lb.tenant_id, 'id': db_lb.id,
|
||||
'vip': {'subnet_id': db_lb.vip_subnet_id,
|
||||
'ip_address': db_lb.vip_address,
|
||||
'port_id': db_lb.vip_port_id}}
|
||||
args.update(create_args)
|
||||
|
||||
if not graph:
|
||||
return args
|
||||
|
||||
if db_lb.listeners:
|
||||
args['listeners'] = []
|
||||
for db_listener in db_lb.listeners:
|
||||
listener_args = self.driver.listener._construct_args(db_listener,
|
||||
graph=True)
|
||||
args['listeners'].append(listener_args)
|
||||
return args
|
||||
|
||||
def create_and_allocate_vip(self, context, lb):
|
||||
self.create(context, lb)
|
||||
|
||||
@async_op
|
||||
def create(self, context, lb):
|
||||
args = {
|
||||
'id': lb.id,
|
||||
'name': lb.name,
|
||||
'description': lb.description,
|
||||
'enabled': lb.admin_state_up,
|
||||
'project_id': lb.tenant_id,
|
||||
'vip': {
|
||||
'subnet_id': lb.vip_subnet_id,
|
||||
'ip_address': lb.vip_address,
|
||||
'port_id': lb.vip_port_id,
|
||||
}
|
||||
}
|
||||
graph = (lb.listeners and len(lb.listeners) > 0)
|
||||
args = self._construct_args(lb, graph=graph)
|
||||
self.driver.req.post(self._url(lb), args)
|
||||
|
||||
@async_op
|
||||
def update(self, context, old_lb, lb):
|
||||
args = {
|
||||
'name': lb.name,
|
||||
'description': lb.description,
|
||||
'enabled': lb.admin_state_up,
|
||||
}
|
||||
args = self._construct_args(lb, create=False)
|
||||
self.driver.req.put(self._url(lb, lb.id), args)
|
||||
|
||||
@async_op
|
||||
|
@ -256,8 +270,7 @@ class ListenerManager(driver_base.BaseListenerManager):
|
|||
s += '/%s' % id
|
||||
return s
|
||||
|
||||
@classmethod
|
||||
def _write(cls, write_func, url, listener, create=True):
|
||||
def _construct_args(self, listener, create=True, graph=False):
|
||||
sni_container_ids = [sni.tls_container_id
|
||||
for sni in listener.sni_containers]
|
||||
args = {
|
||||
|
@ -271,19 +284,40 @@ class ListenerManager(driver_base.BaseListenerManager):
|
|||
'default_pool_id': listener.default_pool_id,
|
||||
'sni_containers': sni_container_ids
|
||||
}
|
||||
if create:
|
||||
|
||||
if not create:
|
||||
return args
|
||||
|
||||
args['project_id'] = listener.tenant_id
|
||||
args['id'] = listener.id
|
||||
write_func(url, args)
|
||||
|
||||
if not graph:
|
||||
return args
|
||||
|
||||
del args['default_pool_id']
|
||||
|
||||
if listener.default_pool:
|
||||
pool = listener.default_pool
|
||||
args['default_pool'] = self.driver.pool._construct_args(pool,
|
||||
graph=True)
|
||||
if listener.l7_policies:
|
||||
args['l7policies'] = []
|
||||
l7_policies = listener.l7_policies
|
||||
for l7_policy in l7_policies:
|
||||
l7_policy_args = self.driver.l7policy._construct_args(
|
||||
l7_policy, graph=True)
|
||||
args['l7policies'].append(l7_policy_args)
|
||||
return args
|
||||
|
||||
@async_op
|
||||
def create(self, context, listener):
|
||||
self._write(self.driver.req.post, self._url(listener), listener)
|
||||
args = self._construct_args(listener)
|
||||
self.driver.req.post(self._url(listener), args)
|
||||
|
||||
@async_op
|
||||
def update(self, context, old_listener, listener):
|
||||
self._write(self.driver.req.put, self._url(listener, id=listener.id),
|
||||
listener, create=False)
|
||||
args = self._construct_args(listener, create=False)
|
||||
self.driver.req.put(self._url(listener, id=listener.id), args)
|
||||
|
||||
@async_op
|
||||
def delete(self, context, listener):
|
||||
|
@ -300,8 +334,7 @@ class PoolManager(driver_base.BasePoolManager):
|
|||
s += '/%s' % id
|
||||
return s
|
||||
|
||||
@classmethod
|
||||
def _write(cls, write_func, url, pool, create=True):
|
||||
def _construct_args(self, pool, create=True, graph=False):
|
||||
args = {
|
||||
'name': pool.name,
|
||||
'description': pool.description,
|
||||
|
@ -316,21 +349,38 @@ class PoolManager(driver_base.BasePoolManager):
|
|||
}
|
||||
else:
|
||||
args['session_persistence'] = None
|
||||
if create:
|
||||
|
||||
if not create:
|
||||
return args
|
||||
|
||||
args['project_id'] = pool.tenant_id
|
||||
args['id'] = pool.id
|
||||
if pool.listeners:
|
||||
args['listener_id'] = pool.listeners[0].id
|
||||
write_func(url, args)
|
||||
|
||||
if not graph:
|
||||
return args
|
||||
|
||||
if pool.members:
|
||||
args['members'] = []
|
||||
for member in pool.members:
|
||||
member_args = self.driver.member._construct_args(member)
|
||||
args['members'].append(member_args)
|
||||
if pool.healthmonitor:
|
||||
hm_args = self.driver.health_monitor._construct_args(
|
||||
pool.healthmonitor)
|
||||
args['health_monitor'] = hm_args
|
||||
return args
|
||||
|
||||
@async_op
|
||||
def create(self, context, pool):
|
||||
self._write(self.driver.req.post, self._url(pool), pool)
|
||||
args = self._construct_args(pool)
|
||||
self.driver.req.post(self._url(pool), args)
|
||||
|
||||
@async_op
|
||||
def update(self, context, old_pool, pool):
|
||||
self._write(self.driver.req.put, self._url(pool, id=pool.id), pool,
|
||||
create=False)
|
||||
args = self._construct_args(pool, create=False)
|
||||
self.driver.req.put(self._url(pool, id=pool.id), args)
|
||||
|
||||
@async_op
|
||||
def delete(self, context, pool):
|
||||
|
@ -348,26 +398,33 @@ class MemberManager(driver_base.BaseMemberManager):
|
|||
s += '/%s' % id
|
||||
return s
|
||||
|
||||
@async_op
|
||||
def create(self, context, member):
|
||||
def _construct_args(self, member, create=True):
|
||||
args = {
|
||||
'id': member.id,
|
||||
'enabled': member.admin_state_up,
|
||||
'ip_address': member.address,
|
||||
'protocol_port': member.protocol_port,
|
||||
'weight': member.weight,
|
||||
'weight': member.weight
|
||||
}
|
||||
if not create:
|
||||
return args
|
||||
|
||||
create_args = {
|
||||
'id': member.id,
|
||||
'ip_address': member.address,
|
||||
'subnet_id': member.subnet_id,
|
||||
'project_id': member.tenant_id
|
||||
}
|
||||
args.update(create_args)
|
||||
|
||||
return args
|
||||
|
||||
@async_op
|
||||
def create(self, context, member):
|
||||
args = self._construct_args(member)
|
||||
self.driver.req.post(self._url(member), args)
|
||||
|
||||
@async_op
|
||||
def update(self, context, old_member, member):
|
||||
args = {
|
||||
'enabled': member.admin_state_up,
|
||||
'protocol_port': member.protocol_port,
|
||||
'weight': member.weight,
|
||||
}
|
||||
args = self._construct_args(member, create=False)
|
||||
self.driver.req.put(self._url(member, member.id), args)
|
||||
|
||||
@async_op
|
||||
|
@ -384,8 +441,7 @@ class HealthMonitorManager(driver_base.BaseHealthMonitorManager):
|
|||
hm.pool.id)
|
||||
return s
|
||||
|
||||
@classmethod
|
||||
def _write(cls, write_func, url, hm, create=True):
|
||||
def _construct_args(self, hm, create=True):
|
||||
args = {
|
||||
'type': hm.type,
|
||||
'delay': hm.delay,
|
||||
|
@ -399,15 +455,17 @@ class HealthMonitorManager(driver_base.BaseHealthMonitorManager):
|
|||
}
|
||||
if create:
|
||||
args['project_id'] = hm.tenant_id
|
||||
write_func(url, args)
|
||||
return args
|
||||
|
||||
@async_op
|
||||
def create(self, context, hm):
|
||||
self._write(self.driver.req.post, self._url(hm), hm)
|
||||
args = self._construct_args(hm)
|
||||
self.driver.req.post(self._url(hm), args)
|
||||
|
||||
@async_op
|
||||
def update(self, context, old_hm, hm):
|
||||
self._write(self.driver.req.put, self._url(hm), hm, create=False)
|
||||
args = self._construct_args(hm, create=False)
|
||||
self.driver.req.put(self._url(hm), args)
|
||||
|
||||
@async_op
|
||||
def delete(self, context, hm):
|
||||
|
@ -425,36 +483,55 @@ class L7PolicyManager(driver_base.BaseL7PolicyManager):
|
|||
s += '/%s' % id
|
||||
return s
|
||||
|
||||
@classmethod
|
||||
def _write(cls, write_func, url, l7p, create=True):
|
||||
def _construct_args(self, l7p, create=True, graph=False):
|
||||
args = {
|
||||
'name': l7p.name,
|
||||
'description': l7p.description,
|
||||
'action': l7p.action,
|
||||
'redirect_pool_id': l7p.redirect_pool_id,
|
||||
'redirect_url': l7p.redirect_url,
|
||||
'position': l7p.position,
|
||||
'enabled': l7p.admin_state_up
|
||||
}
|
||||
if args['action'] == constants.L7_POLICY_ACTION_REJECT:
|
||||
del args['redirect_url']
|
||||
del args['redirect_pool_id']
|
||||
elif args['action'] == constants.L7_POLICY_ACTION_REDIRECT_TO_POOL:
|
||||
args['redirect_pool_id'] = l7p.redirect_pool_id
|
||||
del args['redirect_url']
|
||||
elif args['action'] == constants.L7_POLICY_ACTION_REDIRECT_TO_URL:
|
||||
if args.get('redirect_pool_id'):
|
||||
del args['redirect_pool_id']
|
||||
if create:
|
||||
if not create:
|
||||
return args
|
||||
|
||||
args['id'] = l7p.id
|
||||
write_func(url, args)
|
||||
|
||||
if not graph:
|
||||
if l7p.listener_id:
|
||||
args['listener_id'] = l7p.listener_id
|
||||
return args
|
||||
|
||||
if (l7p.redirect_pool and l7p.action ==
|
||||
constants.L7_POLICY_ACTION_REDIRECT_TO_POOL):
|
||||
del args['redirect_pool_id']
|
||||
pool_args = self.driver.pool._construct_args(l7p.redirect_pool,
|
||||
graph=True)
|
||||
args['redirect_pool'] = pool_args
|
||||
if l7p.rules:
|
||||
args['l7rules'] = []
|
||||
for rule in l7p.rules:
|
||||
rule_args = self.driver.l7rule._construct_args(rule)
|
||||
args['l7rules'].append(rule_args)
|
||||
return args
|
||||
|
||||
@async_op
|
||||
def create(self, context, l7p):
|
||||
self._write(self.driver.req.post, self._url(l7p), l7p)
|
||||
args = self._construct_args(l7p)
|
||||
self.driver.req.post(self._url(l7p), args)
|
||||
|
||||
@async_op
|
||||
def update(self, context, old_l7p, l7p):
|
||||
self._write(self.driver.req.put, self._url(l7p, id=l7p.id),
|
||||
l7p, create=False)
|
||||
args = self._construct_args(l7p, create=False)
|
||||
self.driver.req.put(self._url(l7p, id=l7p.id), args)
|
||||
|
||||
@async_op
|
||||
def delete(self, context, l7p):
|
||||
|
@ -474,7 +551,7 @@ class L7RuleManager(driver_base.BaseL7RuleManager):
|
|||
return s
|
||||
|
||||
@classmethod
|
||||
def _write(cls, write_func, url, l7r, create=True):
|
||||
def _construct_args(cls, l7r, create=True):
|
||||
args = {
|
||||
'type': l7r.type,
|
||||
'compare_type': l7r.compare_type,
|
||||
|
@ -484,16 +561,17 @@ class L7RuleManager(driver_base.BaseL7RuleManager):
|
|||
}
|
||||
if create:
|
||||
args['id'] = l7r.id
|
||||
write_func(url, args)
|
||||
return args
|
||||
|
||||
@async_op
|
||||
def create(self, context, l7r):
|
||||
self._write(self.driver.req.post, self._url(l7r), l7r)
|
||||
args = self._construct_args(l7r)
|
||||
self.driver.req.post(self._url(l7r), args)
|
||||
|
||||
@async_op
|
||||
def update(self, context, old_l7r, l7r):
|
||||
self._write(self.driver.req.put, self._url(l7r, id=l7r.id),
|
||||
l7r, create=False)
|
||||
args = self._construct_args(l7r, create=False)
|
||||
self.driver.req.put(self._url(l7r, id=l7r.id), args)
|
||||
|
||||
@async_op
|
||||
def delete(self, context, l7r):
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
# Copyright 2014 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 oslo_log import log as logging
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.api.v2 import resource_helper
|
||||
from neutron.common import exceptions as nexception
|
||||
from neutron.plugins.common import constants
|
||||
|
||||
from neutron_lbaas._i18n import _
|
||||
from neutron_lbaas.extensions import loadbalancerv2
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProviderCannotCreateLoadBalancerGraph(nexception.BadRequest):
|
||||
message = _("The provider does not have the ability to create a load "
|
||||
"balancer graph.")
|
||||
|
||||
# NOTE(blogan): this dictionary is to be used only for importing from the
|
||||
# plugin to validate against. It is only put here for consistency with
|
||||
# all other extensions and an easy place to look what changes this extension
|
||||
# allows.
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
'graphs': {
|
||||
'loadbalancer': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True}
|
||||
}
|
||||
}
|
||||
|
||||
EXISTING_ATTR_GRAPH_ATTR_MAP = {
|
||||
'loadbalancers': {
|
||||
'listeners': {
|
||||
'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True, 'default': []
|
||||
}
|
||||
},
|
||||
'listeners': {
|
||||
'default_pool': {
|
||||
'allow_post': True, 'allow_put': False, 'is_visible': True,
|
||||
'default': attr.ATTR_NOT_SPECIFIED
|
||||
},
|
||||
'l7policies': {
|
||||
'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True, 'default': []
|
||||
}
|
||||
},
|
||||
'pools': {
|
||||
'healthmonitor': {
|
||||
'allow_post': True, 'allow_put': False, 'is_visible': True,
|
||||
'default': attr.ATTR_NOT_SPECIFIED
|
||||
},
|
||||
'members': {
|
||||
'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True, 'default': []
|
||||
}
|
||||
},
|
||||
'l7policies': {
|
||||
'rules': {
|
||||
'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True, 'default': []
|
||||
},
|
||||
'redirect_pool': {
|
||||
'allow_post': True, 'allow_put': False, 'is_visible': True,
|
||||
'default': attr.ATTR_NOT_SPECIFIED
|
||||
},
|
||||
'listener_id': {
|
||||
'allow_post': False, 'allow_put': False, 'is_visible': True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Lb_graph(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Load Balancer Graph"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "lb-graph"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Extension for allowing the creation of load balancers with a" \
|
||||
" full graph in one API request."
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return "http://wiki.openstack.org/neutron/LBaaS/API_2.0"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2016-02-09T10:00:00-00:00"
|
||||
|
||||
def get_required_extensions(self):
|
||||
return ["lbaasv2"]
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
plural_mappings = resource_helper.build_plural_mappings(
|
||||
{}, RESOURCE_ATTRIBUTE_MAP)
|
||||
resources = resource_helper.build_resource_info(
|
||||
plural_mappings,
|
||||
RESOURCE_ATTRIBUTE_MAP,
|
||||
constants.LOADBALANCERV2,
|
||||
register_quota=True)
|
||||
return resources
|
||||
|
||||
@classmethod
|
||||
def get_plugin_interface(cls):
|
||||
return loadbalancerv2.LoadBalancerPluginBaseV2
|
|
@ -669,3 +669,7 @@ class LoadBalancerPluginBaseV2(service_base.ServicePluginBase):
|
|||
@abc.abstractmethod
|
||||
def delete_l7policy_rule(self, context, id, l7policy_id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_graph(self, context, graph):
|
||||
pass
|
||||
|
|
|
@ -606,6 +606,8 @@ class L7Policy(BaseDataModel):
|
|||
if self.listener:
|
||||
ret_dict['listeners'].append({'id': self.listener.id})
|
||||
ret_dict['rules'] = [{'id': rule.id} for rule in self.rules]
|
||||
if ret_dict.get('action') == l_const.L7_POLICY_ACTION_REDIRECT_TO_POOL:
|
||||
del ret_dict['redirect_url']
|
||||
return ret_dict
|
||||
|
||||
@classmethod
|
||||
|
@ -662,7 +664,7 @@ class Listener(BaseDataModel):
|
|||
ret_dict = super(Listener, self).to_dict(
|
||||
loadbalancer=False, loadbalancer_id=False, default_pool=False,
|
||||
operating_status=False, provisioning_status=False,
|
||||
sni_containers=False)
|
||||
sni_containers=False, default_tls_container=False)
|
||||
# NOTE(blogan): Returning a list to future proof for M:N objects
|
||||
# that are not yet implemented.
|
||||
ret_dict['loadbalancers'] = []
|
||||
|
@ -671,7 +673,8 @@ class Listener(BaseDataModel):
|
|||
ret_dict['sni_container_refs'] = [container.tls_container_id
|
||||
for container in self.sni_containers]
|
||||
ret_dict['default_tls_container_ref'] = self.default_tls_container_id
|
||||
ret_dict['l7_policies'] = [{'id': l7_policy.id}
|
||||
del ret_dict['l7_policies']
|
||||
ret_dict['l7policies'] = [{'id': l7_policy.id}
|
||||
for l7_policy in self.l7_policies]
|
||||
return ret_dict
|
||||
|
||||
|
@ -724,12 +727,80 @@ class LoadBalancer(BaseDataModel):
|
|||
def attached_to_loadbalancer(self):
|
||||
return True
|
||||
|
||||
def to_api_dict(self):
|
||||
def _construct_full_graph_api_dict(self):
|
||||
api_listeners = []
|
||||
for listener in self.listeners:
|
||||
api_listener = listener.to_api_dict()
|
||||
del api_listener['loadbalancers']
|
||||
del api_listener['default_pool_id']
|
||||
if listener.default_pool:
|
||||
api_pool = listener.default_pool.to_api_dict()
|
||||
del api_pool['listeners']
|
||||
del api_pool['listener']
|
||||
del api_pool['listener_id']
|
||||
del api_pool['healthmonitor_id']
|
||||
del api_pool['loadbalancers']
|
||||
del api_pool['l7_policies']
|
||||
del api_pool['sessionpersistence']
|
||||
if listener.default_pool.healthmonitor:
|
||||
api_hm = listener.default_pool.healthmonitor.to_api_dict()
|
||||
del api_hm['pools']
|
||||
api_pool['healthmonitor'] = api_hm
|
||||
api_pool['members'] = []
|
||||
for member in listener.default_pool.members:
|
||||
api_member = member.to_api_dict()
|
||||
del api_member['pool_id']
|
||||
api_pool['members'].append(api_member)
|
||||
api_listener['default_pool'] = api_pool
|
||||
if listener.l7_policies and len(listener.l7_policies) > 0:
|
||||
api_l7policies = []
|
||||
for l7policy in listener.l7_policies:
|
||||
api_l7policy = l7policy.to_api_dict()
|
||||
del api_l7policy['redirect_pool_id']
|
||||
del api_l7policy['listeners']
|
||||
if l7policy.rules and len(l7policy.rules) > 0:
|
||||
api_l7rules = []
|
||||
for l7rule in l7policy.rules:
|
||||
api_l7rule = l7rule.to_api_dict()
|
||||
del api_l7rule['policies']
|
||||
api_l7rules.append(api_l7rule)
|
||||
api_l7policy['rules'] = api_l7rules
|
||||
if l7policy.redirect_pool:
|
||||
api_r_pool = l7policy.redirect_pool.to_api_dict()
|
||||
if l7policy.redirect_pool.healthmonitor:
|
||||
api_r_hm = (l7policy.redirect_pool.healthmonitor.
|
||||
to_api_dict())
|
||||
del api_r_hm['pools']
|
||||
api_r_pool['healthmonitor'] = api_r_hm
|
||||
api_r_pool['members'] = []
|
||||
for r_member in l7policy.redirect_pool.members:
|
||||
api_r_member = r_member.to_api_dict()
|
||||
del api_r_member['pool_id']
|
||||
api_r_pool['members'].append(api_r_member)
|
||||
del api_r_pool['listeners']
|
||||
del api_r_pool['listener']
|
||||
del api_r_pool['listener_id']
|
||||
del api_r_pool['healthmonitor_id']
|
||||
del api_r_pool['loadbalancers']
|
||||
del api_r_pool['l7_policies']
|
||||
del api_r_pool['sessionpersistence']
|
||||
api_l7policy['redirect_pool'] = api_r_pool
|
||||
api_l7policies.append(api_l7policy)
|
||||
api_listener['l7policies'] = api_l7policies
|
||||
api_listeners.append(api_listener)
|
||||
return api_listeners
|
||||
|
||||
def to_api_dict(self, full_graph=False):
|
||||
ret_dict = super(LoadBalancer, self).to_dict(
|
||||
vip_port=False, stats=False, listeners=False)
|
||||
if full_graph:
|
||||
ret_dict['listeners'] = self._construct_full_graph_api_dict()
|
||||
del ret_dict['pools']
|
||||
else:
|
||||
ret_dict['listeners'] = [{'id': listener.id}
|
||||
for listener in self.listeners]
|
||||
ret_dict['pools'] = [{'id': pool.id} for pool in self.pools]
|
||||
|
||||
if self.provider:
|
||||
ret_dict['provider'] = self.provider.provider_name
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
# 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 six
|
||||
import copy
|
||||
|
||||
from neutron.api.v2 import attributes as attrs
|
||||
from neutron.api.v2 import base as napi_base
|
||||
from neutron import context as ncontext
|
||||
from neutron.db import servicetype_db as st_db
|
||||
from neutron.extensions import flavors
|
||||
|
@ -29,6 +29,7 @@ from oslo_config import cfg
|
|||
from oslo_log import log as logging
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import excutils
|
||||
import six
|
||||
|
||||
from neutron_lbaas._i18n import _LI, _LE
|
||||
from neutron_lbaas import agent_scheduler as agent_scheduler_v2
|
||||
|
@ -37,6 +38,8 @@ from neutron_lbaas.common.tls_utils import cert_parser
|
|||
from neutron_lbaas.db.loadbalancer import loadbalancer_db as ldb
|
||||
from neutron_lbaas.db.loadbalancer import loadbalancer_dbv2 as ldbv2
|
||||
from neutron_lbaas.db.loadbalancer import models
|
||||
from neutron_lbaas.extensions import l7
|
||||
from neutron_lbaas.extensions import lb_graph as lb_graph_ext
|
||||
from neutron_lbaas.extensions import lbaas_agentschedulerv2
|
||||
from neutron_lbaas.extensions import loadbalancer as lb_ext
|
||||
from neutron_lbaas.extensions import loadbalancerv2
|
||||
|
@ -386,7 +389,8 @@ class LoadBalancerPluginv2(loadbalancerv2.LoadBalancerPluginBaseV2):
|
|||
"shared_pools",
|
||||
"l7",
|
||||
"lbaas_agent_schedulerv2",
|
||||
"service-type"]
|
||||
"service-type",
|
||||
"lb-graph"]
|
||||
path_prefix = loadbalancerv2.LOADBALANCERV2_PREFIX
|
||||
|
||||
agent_notifiers = (
|
||||
|
@ -475,15 +479,15 @@ class LoadBalancerPluginv2(loadbalancerv2.LoadBalancerPluginBaseV2):
|
|||
return self.default_provider
|
||||
|
||||
def _call_driver_operation(self, context, driver_method, db_entity,
|
||||
old_db_entity=None):
|
||||
old_db_entity=None, **kwargs):
|
||||
manager_method = "%s.%s" % (driver_method.__self__.__class__.__name__,
|
||||
driver_method.__name__)
|
||||
LOG.info(_LI("Calling driver operation %s") % manager_method)
|
||||
try:
|
||||
if old_db_entity:
|
||||
driver_method(context, old_db_entity, db_entity)
|
||||
driver_method(context, old_db_entity, db_entity, **kwargs)
|
||||
else:
|
||||
driver_method(context, db_entity)
|
||||
driver_method(context, db_entity, **kwargs)
|
||||
# catching and reraising agent issues
|
||||
except (lbaas_agentschedulerv2.NoEligibleLbaasAgent,
|
||||
lbaas_agentschedulerv2.NoActiveLbaasAgent) as no_agent:
|
||||
|
@ -562,6 +566,113 @@ class LoadBalancerPluginv2(loadbalancerv2.LoadBalancerPluginBaseV2):
|
|||
|
||||
loadbalancer['provider'] = provider
|
||||
|
||||
def _get_tweaked_resource_attribute_map(self):
|
||||
memo = {id(attrs.ATTR_NOT_SPECIFIED): attrs.ATTR_NOT_SPECIFIED}
|
||||
ram = copy.deepcopy(attrs.RESOURCE_ATTRIBUTE_MAP, memo=memo)
|
||||
del ram['listeners']['loadbalancer_id']
|
||||
del ram['pools']['listener_id']
|
||||
del ram['healthmonitors']['pool_id']
|
||||
for resource in ram:
|
||||
if resource in lb_graph_ext.EXISTING_ATTR_GRAPH_ATTR_MAP:
|
||||
ram[resource].update(
|
||||
lb_graph_ext.EXISTING_ATTR_GRAPH_ATTR_MAP[resource])
|
||||
return ram
|
||||
|
||||
def _prepare_loadbalancer_graph(self, context, loadbalancer):
|
||||
"""Prepares the entire user requested body of a load balancer graph
|
||||
|
||||
To minimize code duplication, this method reuses the neutron API
|
||||
controller's method to do all the validation, conversion, and
|
||||
defaulting of each resource. This reuses the RESOURCE_ATTRIBUTE_MAP
|
||||
and SUB_RESOURCE_ATTRIBUTE_MAP from the extension to enable this.
|
||||
"""
|
||||
# NOTE(blogan): it is assumed the loadbalancer attributes have already
|
||||
# passed through the prepare_request_body method by nature of the
|
||||
# normal neutron wsgi workflow. So we start with listeners since
|
||||
# that probably has not passed through the neutron wsgi workflow.
|
||||
ram = self._get_tweaked_resource_attribute_map()
|
||||
# NOTE(blogan): members are not populated in the attributes.RAM so
|
||||
# our only option is to use the original extension definition of member
|
||||
# to validate. If members ever need something added to it then it too
|
||||
# will need to be added here.
|
||||
prepped_lb = napi_base.Controller.prepare_request_body(
|
||||
context, {'loadbalancer': loadbalancer}, True, 'loadbalancer',
|
||||
ram['loadbalancers']
|
||||
)
|
||||
sub_ram = loadbalancerv2.SUB_RESOURCE_ATTRIBUTE_MAP
|
||||
sub_ram.update(l7.SUB_RESOURCE_ATTRIBUTE_MAP)
|
||||
prepped_listeners = []
|
||||
for listener in loadbalancer.get('listeners', []):
|
||||
prepped_listener = napi_base.Controller.prepare_request_body(
|
||||
context, {'listener': listener}, True, 'listener',
|
||||
ram['listeners'])
|
||||
l7policies = listener.get('l7policies')
|
||||
if l7policies and l7policies != attrs.ATTR_NOT_SPECIFIED:
|
||||
prepped_policies = []
|
||||
for policy in l7policies:
|
||||
prepped_policy = napi_base.Controller.prepare_request_body(
|
||||
context, {'l7policy': policy}, True, 'l7policy',
|
||||
ram['l7policies'])
|
||||
l7rules = policy.get('rules')
|
||||
redirect_pool = policy.get('redirect_pool')
|
||||
if l7rules and l7rules != attrs.ATTR_NOT_SPECIFIED:
|
||||
prepped_rules = []
|
||||
for rule in l7rules:
|
||||
prepped_rule = (
|
||||
napi_base.Controller.prepare_request_body(
|
||||
context, {'l7rule': rule}, True, 'l7rule',
|
||||
sub_ram['rules']['parameters']))
|
||||
prepped_rules.append(prepped_rule)
|
||||
prepped_policy['l7_rules'] = prepped_rules
|
||||
if (redirect_pool and
|
||||
redirect_pool != attrs.ATTR_NOT_SPECIFIED):
|
||||
prepped_r_pool = (
|
||||
napi_base.Controller.prepare_request_body(
|
||||
context, {'pool': redirect_pool}, True, 'pool',
|
||||
ram['pools']))
|
||||
prepped_r_members = []
|
||||
for member in redirect_pool.get('members', []):
|
||||
prepped_r_member = (
|
||||
napi_base.Controller.prepare_request_body(
|
||||
context, {'member': member},
|
||||
True, 'member',
|
||||
sub_ram['members']['parameters']))
|
||||
prepped_r_members.append(prepped_r_member)
|
||||
prepped_r_pool['members'] = prepped_r_members
|
||||
r_hm = redirect_pool.get('healthmonitor')
|
||||
if r_hm and r_hm != attrs.ATTR_NOT_SPECIFIED:
|
||||
prepped_r_hm = (
|
||||
napi_base.Controller.prepare_request_body(
|
||||
context, {'healthmonitor': r_hm},
|
||||
True, 'healthmonitor',
|
||||
ram['healthmonitors']))
|
||||
prepped_r_pool['healthmonitor'] = prepped_r_hm
|
||||
prepped_policy['redirect_pool'] = redirect_pool
|
||||
prepped_policies.append(prepped_policy)
|
||||
prepped_listener['l7_policies'] = prepped_policies
|
||||
pool = listener.get('default_pool')
|
||||
if pool and pool != attrs.ATTR_NOT_SPECIFIED:
|
||||
prepped_pool = napi_base.Controller.prepare_request_body(
|
||||
context, {'pool': pool}, True, 'pool',
|
||||
ram['pools'])
|
||||
prepped_members = []
|
||||
for member in pool.get('members', []):
|
||||
prepped_member = napi_base.Controller.prepare_request_body(
|
||||
context, {'member': member}, True, 'member',
|
||||
sub_ram['members']['parameters'])
|
||||
prepped_members.append(prepped_member)
|
||||
prepped_pool['members'] = prepped_members
|
||||
hm = pool.get('healthmonitor')
|
||||
if hm and hm != attrs.ATTR_NOT_SPECIFIED:
|
||||
prepped_hm = napi_base.Controller.prepare_request_body(
|
||||
context, {'healthmonitor': hm}, True, 'healthmonitor',
|
||||
ram['healthmonitors'])
|
||||
prepped_pool['healthmonitor'] = prepped_hm
|
||||
prepped_listener['default_pool'] = prepped_pool
|
||||
prepped_listeners.append(prepped_listener)
|
||||
prepped_lb['listeners'] = prepped_listeners
|
||||
return loadbalancer
|
||||
|
||||
def create_loadbalancer(self, context, loadbalancer):
|
||||
loadbalancer = loadbalancer.get('loadbalancer')
|
||||
if loadbalancer['flavor_id'] != attrs.ATTR_NOT_SPECIFIED:
|
||||
|
@ -583,6 +694,30 @@ class LoadBalancerPluginv2(loadbalancerv2.LoadBalancerPluginBaseV2):
|
|||
self._call_driver_operation(context, create_method, lb_db)
|
||||
return self.db.get_loadbalancer(context, lb_db.id).to_api_dict()
|
||||
|
||||
def create_graph(self, context, graph):
|
||||
loadbalancer = graph.get('graph', {}).get('loadbalancer')
|
||||
loadbalancer = self._prepare_loadbalancer_graph(context, loadbalancer)
|
||||
if loadbalancer['flavor_id'] != attrs.ATTR_NOT_SPECIFIED:
|
||||
self._insert_provider_name_from_flavor(context, loadbalancer)
|
||||
else:
|
||||
del loadbalancer['flavor_id']
|
||||
provider_name = self._get_provider_name(loadbalancer)
|
||||
driver = self.drivers[provider_name]
|
||||
if not driver.load_balancer.allows_create_graph:
|
||||
raise lb_graph_ext.ProviderCannotCreateLoadBalancerGraph
|
||||
lb_db = self.db.create_loadbalancer_graph(
|
||||
context, loadbalancer,
|
||||
allocate_vip=not driver.load_balancer.allocates_vip)
|
||||
self.service_type_manager.add_resource_association(
|
||||
context, constants.LOADBALANCERV2, provider_name, lb_db.id)
|
||||
create_method = (driver.load_balancer.create_and_allocate_vip
|
||||
if driver.load_balancer.allocates_vip
|
||||
else driver.load_balancer.create)
|
||||
self._call_driver_operation(context, create_method, lb_db)
|
||||
api_lb = {'loadbalancer': self.db.get_loadbalancer(
|
||||
context, lb_db.id).to_api_dict(full_graph=True)}
|
||||
return api_lb
|
||||
|
||||
def update_loadbalancer(self, context, id, loadbalancer):
|
||||
loadbalancer = loadbalancer.get('loadbalancer')
|
||||
old_lb = self.db.get_loadbalancer(context, id)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#
|
||||
|
||||
import mock
|
||||
import webob
|
||||
|
||||
from neutron.db import servicetype_db as st_db
|
||||
from neutron.tests import base as n_base
|
||||
|
@ -206,6 +207,17 @@ class NeutronDbPluginV2TestCase(
|
|||
expected_res.reverse()
|
||||
self.assertEqual(expected_res, [n['id'] for n in item_res])
|
||||
|
||||
def _delete(self, collection, id,
|
||||
expected_code=webob.exc.HTTPNoContent.code,
|
||||
neutron_context=None, subresource=None, sub_id=None):
|
||||
req = self.new_delete_request(collection, id, subresource=subresource,
|
||||
sub_id=sub_id)
|
||||
if neutron_context:
|
||||
# create a specific auth context for this request
|
||||
req.environ['neutron.context'] = neutron_context
|
||||
res = req.get_response(self._api_for_resource(collection))
|
||||
self.assertEqual(res.status_int, expected_code)
|
||||
|
||||
|
||||
class ExtensionTestCase(ext_base.ExtensionTestCase):
|
||||
pass
|
||||
|
|
|
@ -40,6 +40,7 @@ from neutron_lbaas.db.loadbalancer import models
|
|||
from neutron_lbaas.drivers.logging_noop import driver as noop_driver
|
||||
import neutron_lbaas.extensions
|
||||
from neutron_lbaas.extensions import l7
|
||||
from neutron_lbaas.extensions import lb_graph
|
||||
from neutron_lbaas.extensions import loadbalancerv2
|
||||
from neutron_lbaas.extensions import sharedpools
|
||||
from neutron_lbaas.services.loadbalancer import constants as lb_const
|
||||
|
@ -64,12 +65,14 @@ _subnet_id = "0c798ed8-33ba-11e2-8b28-000c291c4d14"
|
|||
class LbaasTestMixin(object):
|
||||
resource_keys = list(loadbalancerv2.RESOURCE_ATTRIBUTE_MAP.keys())
|
||||
resource_keys.extend(l7.RESOURCE_ATTRIBUTE_MAP.keys())
|
||||
resource_keys.extend(lb_graph.RESOURCE_ATTRIBUTE_MAP.keys())
|
||||
resource_prefix_map = dict(
|
||||
(k, loadbalancerv2.LOADBALANCERV2_PREFIX)
|
||||
for k in resource_keys)
|
||||
|
||||
def _get_loadbalancer_optional_args(self):
|
||||
return 'description', 'vip_address', 'admin_state_up', 'name'
|
||||
return ('description', 'vip_address', 'admin_state_up', 'name',
|
||||
'listeners')
|
||||
|
||||
def _create_loadbalancer(self, fmt, subnet_id,
|
||||
expected_res_status=None, **kwargs):
|
||||
|
@ -87,6 +90,22 @@ class LbaasTestMixin(object):
|
|||
|
||||
return lb_res
|
||||
|
||||
def _create_graph(self, fmt, subnet_id, expected_res_status=None,
|
||||
**kwargs):
|
||||
data = {'vip_subnet_id': subnet_id, 'tenant_id': self._tenant_id}
|
||||
args = self._get_loadbalancer_optional_args()
|
||||
for arg in args:
|
||||
if arg in kwargs and kwargs[arg] is not None:
|
||||
data[arg] = kwargs[arg]
|
||||
|
||||
data = {'graph': {'loadbalancer': data, 'tenant_id': self._tenant_id}}
|
||||
lb_req = self.new_create_request('graphs', data, fmt)
|
||||
lb_res = lb_req.get_response(self.ext_api)
|
||||
if expected_res_status:
|
||||
self.assertEqual(expected_res_status, lb_res.status_int)
|
||||
|
||||
return lb_res
|
||||
|
||||
def _get_listener_optional_args(self):
|
||||
return ('name', 'description', 'default_pool_id', 'loadbalancer_id',
|
||||
'connection_limit', 'admin_state_up',
|
||||
|
@ -249,15 +268,70 @@ class LbaasTestMixin(object):
|
|||
tmp_subnet['subnet']['id'],
|
||||
**kwargs)
|
||||
if res.status_int >= webob.exc.HTTPClientError.code:
|
||||
raise webob.exc.HTTPClientError(
|
||||
exc = webob.exc.HTTPClientError(
|
||||
explanation=_("Unexpected error code: %s") %
|
||||
res.status_int
|
||||
)
|
||||
exc.code = res.status_int
|
||||
exc.status_code = res.status_int
|
||||
raise exc
|
||||
lb = self.deserialize(fmt or self.fmt, res)
|
||||
yield lb
|
||||
if not no_delete:
|
||||
self._delete('loadbalancers', lb['loadbalancer']['id'])
|
||||
|
||||
@contextlib.contextmanager
|
||||
def graph(self, fmt=None, subnet=None, no_delete=False, **kwargs):
|
||||
if not fmt:
|
||||
fmt = self.fmt
|
||||
|
||||
with test_db_base_plugin_v2.optional_ctx(
|
||||
subnet, self.subnet) as tmp_subnet:
|
||||
|
||||
res = self._create_graph(fmt, tmp_subnet['subnet']['id'],
|
||||
**kwargs)
|
||||
if res.status_int >= webob.exc.HTTPClientError.code:
|
||||
exc = webob.exc.HTTPClientError(
|
||||
explanation=_("Unexpected error code: %s") %
|
||||
res.status_int
|
||||
)
|
||||
exc.code = res.status_int
|
||||
exc.status_code = res.status_int
|
||||
raise exc
|
||||
graph = self.deserialize(fmt or self.fmt, res)
|
||||
yield graph
|
||||
if not no_delete:
|
||||
# delete loadbalancer children if this was a loadbalancer
|
||||
# graph create call
|
||||
lb = graph['graph']['loadbalancer']
|
||||
for listener in lb.get('listeners', []):
|
||||
pool = listener.get('default_pool')
|
||||
if pool:
|
||||
hm = pool.get('healthmonitor')
|
||||
if hm:
|
||||
self._delete('healthmonitors', hm['id'])
|
||||
members = pool.get('members', [])
|
||||
for member in members:
|
||||
self._delete('pools', pool['id'],
|
||||
subresource='members',
|
||||
sub_id=member['id'])
|
||||
self._delete('pools', pool['id'])
|
||||
policies = listener.get('l7policies', [])
|
||||
for policy in policies:
|
||||
r_pool = policy.get('redirect_pool')
|
||||
if r_pool:
|
||||
r_hm = r_pool.get('healthmonitor')
|
||||
if r_hm:
|
||||
self._delete('healthmonitors', r_hm['id'])
|
||||
r_members = r_pool.get('members', [])
|
||||
for r_member in r_members:
|
||||
self._delete('pools', r_pool['id'],
|
||||
subresource='members',
|
||||
sub_id=r_member['id'])
|
||||
self._delete('pools', r_pool['id'])
|
||||
self._delete('listeners', listener['id'])
|
||||
self._delete('loadbalancers', lb['id'])
|
||||
|
||||
@contextlib.contextmanager
|
||||
def listener(self, fmt=None, protocol='HTTP', loadbalancer_id=None,
|
||||
protocol_port=80, default_pool_id=None, no_delete=False,
|
||||
|
@ -348,14 +422,8 @@ class LbaasTestMixin(object):
|
|||
member = self.deserialize(fmt or self.fmt, res)
|
||||
yield member
|
||||
if not no_delete:
|
||||
del_req = self.new_delete_request(
|
||||
'pools',
|
||||
fmt=fmt,
|
||||
id=pool_id,
|
||||
subresource='members',
|
||||
self._delete('pools', id=pool_id, subresource='members',
|
||||
sub_id=member['member']['id'])
|
||||
del_res = del_req.get_response(self.ext_api)
|
||||
self.assertEqual(webob.exc.HTTPNoContent.code, del_res.status_int)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def healthmonitor(self, fmt=None, pool_id='pool1id', type='TCP', delay=1,
|
||||
|
@ -461,6 +529,8 @@ class ExtendedPluginAwareExtensionManager(object):
|
|||
extensions_list.append(sharedpools)
|
||||
if 'l7' in self.extension_aliases:
|
||||
extensions_list.append(l7)
|
||||
if 'lb-graph' in self.extension_aliases:
|
||||
extensions_list.append(lb_graph)
|
||||
for extension in extensions_list:
|
||||
if 'RESOURCE_ATTRIBUTE_MAP' in extension.__dict__:
|
||||
loadbalancerv2.RESOURCE_ATTRIBUTE_MAP.update(
|
||||
|
@ -959,6 +1029,336 @@ class LoadBalancerDelegateVIPCreation(LbaasPluginDbTestCase):
|
|||
self.assertIsNotNone(port)
|
||||
|
||||
|
||||
class TestLoadBalancerGraphCreation(LbaasPluginDbTestCase):
|
||||
|
||||
def _assert_graphs_equal(self, expected_graph, observed_graph):
|
||||
observed_graph_copy = copy.deepcopy(observed_graph)
|
||||
for k in ('id', 'vip_address', 'vip_subnet_id'):
|
||||
self.assertTrue(observed_graph_copy.get(k, None))
|
||||
|
||||
expected_graph['id'] = observed_graph_copy['id']
|
||||
expected_graph['vip_port_id'] = observed_graph_copy['vip_port_id']
|
||||
expected_listeners = expected_graph.pop('listeners', [])
|
||||
observed_listeners = observed_graph_copy.pop('listeners', [])
|
||||
actual = dict((k, v)
|
||||
for k, v in observed_graph_copy.items()
|
||||
if k in expected_graph)
|
||||
self.assertEqual(expected_graph, actual)
|
||||
for observed_listener in observed_listeners:
|
||||
self.assertTrue(observed_listener.get('id'))
|
||||
observed_listener.pop('id')
|
||||
default_pool = observed_listener.get('default_pool')
|
||||
l7_policies = observed_listener.get('l7policies')
|
||||
if default_pool:
|
||||
self.assertTrue(default_pool.get('id'))
|
||||
default_pool.pop('id')
|
||||
hm = default_pool.get('healthmonitor')
|
||||
if hm:
|
||||
self.assertTrue(hm.get('id'))
|
||||
hm.pop('id')
|
||||
for member in default_pool.get('members', []):
|
||||
self.assertTrue(member.get('id'))
|
||||
member.pop('id')
|
||||
if l7_policies:
|
||||
for policy in l7_policies:
|
||||
self.assertTrue(policy.get('id'))
|
||||
policy.pop('id')
|
||||
r_pool = policy.get('redirect_pool')
|
||||
rules = policy.get('rules')
|
||||
if r_pool:
|
||||
self.assertTrue(r_pool.get('id'))
|
||||
r_pool.pop('id')
|
||||
r_hm = r_pool.get('healthmonitor')
|
||||
if r_hm:
|
||||
self.assertTrue(r_hm.get('id'))
|
||||
r_hm.pop('id')
|
||||
for r_member in r_pool.get('members', []):
|
||||
self.assertTrue(r_member.get('id'))
|
||||
r_member.pop('id')
|
||||
if rules:
|
||||
for rule in rules:
|
||||
self.assertTrue(rule.get('id'))
|
||||
rule.pop('id')
|
||||
self.assertIn(observed_listener, expected_listeners)
|
||||
|
||||
def _validate_graph_statuses(self, graph):
|
||||
lb_id = graph['id']
|
||||
for listener in graph.get('listeners', []):
|
||||
kwargs = {'listener_id': listener['id']}
|
||||
pool = listener.get('default_pool')
|
||||
if pool:
|
||||
kwargs['pool_id'] = pool['id']
|
||||
hm = pool.get('health_monitor')
|
||||
if hm:
|
||||
kwargs['hm_id'] = hm['id']
|
||||
for member in pool.get('members', []):
|
||||
kwargs['member_id'] = member['id']
|
||||
self._validate_statuses(lb_id, **kwargs)
|
||||
if pool.get('members'):
|
||||
continue
|
||||
self._validate_statuses(lb_id, **kwargs)
|
||||
|
||||
def _get_expected_lb(self, expected_listeners):
|
||||
expected_lb = {
|
||||
'name': 'vip1',
|
||||
'description': '',
|
||||
'admin_state_up': True,
|
||||
'provisioning_status': constants.ACTIVE,
|
||||
'operating_status': lb_const.ONLINE,
|
||||
'tenant_id': self._tenant_id,
|
||||
'listeners': expected_listeners,
|
||||
'provider': 'lbaas'
|
||||
}
|
||||
return expected_lb
|
||||
|
||||
def _get_listener_bodies(self, name='listener1', protocol_port=80,
|
||||
create_default_pool=None,
|
||||
expected_default_pool=None,
|
||||
create_l7_policies=None,
|
||||
expected_l7_policies=None):
|
||||
create_listener = {
|
||||
'name': name,
|
||||
'protocol_port': protocol_port,
|
||||
'protocol': lb_const.PROTOCOL_HTTP,
|
||||
'tenant_id': self._tenant_id,
|
||||
}
|
||||
if create_default_pool:
|
||||
create_listener['default_pool'] = create_default_pool
|
||||
if create_l7_policies:
|
||||
create_listener['l7policies'] = create_l7_policies
|
||||
expected_listener = {
|
||||
'description': '',
|
||||
'default_tls_container_ref': None,
|
||||
'sni_container_refs': [],
|
||||
'connection_limit': -1,
|
||||
'admin_state_up': True,
|
||||
'l7policies': []
|
||||
}
|
||||
expected_listener.update(create_listener)
|
||||
if expected_default_pool:
|
||||
expected_listener['default_pool'] = expected_default_pool
|
||||
expected_listener['default_tls_container_id'] = None
|
||||
expected_listener['l7policies'] = expected_l7_policies or []
|
||||
return create_listener, expected_listener
|
||||
|
||||
def _get_pool_bodies(self, name='pool1', create_members=None,
|
||||
expected_members=None, create_hm=None,
|
||||
expected_hm=None):
|
||||
create_pool = {
|
||||
'name': name,
|
||||
'protocol': lb_const.PROTOCOL_HTTP,
|
||||
'lb_algorithm': lb_const.LB_METHOD_ROUND_ROBIN,
|
||||
'tenant_id': self._tenant_id
|
||||
}
|
||||
if create_members:
|
||||
create_pool['members'] = create_members
|
||||
if create_hm:
|
||||
create_pool['healthmonitor'] = create_hm
|
||||
expected_pool = {
|
||||
'description': '',
|
||||
'session_persistence': None,
|
||||
'members': [],
|
||||
'admin_state_up': True
|
||||
}
|
||||
expected_pool.update(create_pool)
|
||||
if expected_members:
|
||||
expected_pool['members'] = expected_members
|
||||
if expected_hm:
|
||||
expected_pool['healthmonitor'] = expected_hm
|
||||
return create_pool, expected_pool
|
||||
|
||||
def _get_member_bodies(self, name='member1'):
|
||||
create_member = {
|
||||
'name': name,
|
||||
'address': '10.0.0.1',
|
||||
'protocol_port': 80,
|
||||
'subnet_id': self._subnet_id,
|
||||
'tenant_id': self._tenant_id
|
||||
}
|
||||
expected_member = {
|
||||
'weight': 1,
|
||||
'admin_state_up': True,
|
||||
}
|
||||
expected_member.update(create_member)
|
||||
return create_member, expected_member
|
||||
|
||||
def _get_hm_bodies(self, name='hm1'):
|
||||
create_hm = {
|
||||
'name': name,
|
||||
'type': lb_const.HEALTH_MONITOR_HTTP,
|
||||
'delay': 1,
|
||||
'timeout': 1,
|
||||
'max_retries': 1,
|
||||
'tenant_id': self._tenant_id
|
||||
}
|
||||
expected_hm = {
|
||||
'http_method': 'GET',
|
||||
'url_path': '/',
|
||||
'expected_codes': '200',
|
||||
'admin_state_up': True
|
||||
}
|
||||
expected_hm.update(create_hm)
|
||||
return create_hm, expected_hm
|
||||
|
||||
def _get_l7policies_bodies(self, name='l7policy_name', create_rules=None,
|
||||
expected_rules=None, create_r_pool=None,
|
||||
expected_r_pool=None):
|
||||
c_policy = {
|
||||
'name': name,
|
||||
'action': lb_const.L7_POLICY_ACTION_REDIRECT_TO_POOL,
|
||||
'admin_state_up': True,
|
||||
'tenant_id': self._tenant_id
|
||||
}
|
||||
if create_r_pool:
|
||||
c_policy['redirect_pool'] = create_r_pool
|
||||
if create_rules:
|
||||
c_policy['rules'] = create_rules
|
||||
e_policy = {
|
||||
'description': '',
|
||||
'position': 1
|
||||
}
|
||||
e_policy.update(c_policy)
|
||||
if expected_r_pool:
|
||||
e_policy['redirect_pool'] = expected_r_pool
|
||||
if expected_rules:
|
||||
e_policy['rules'] = expected_rules
|
||||
create_l7policies = [c_policy]
|
||||
expected_l7policies = [e_policy]
|
||||
return create_l7policies, expected_l7policies
|
||||
|
||||
def _get_l7rules_bodes(self):
|
||||
create_rule = {
|
||||
'compare_type': lb_const.L7_RULE_COMPARE_TYPE_EQUAL_TO,
|
||||
'type': lb_const.L7_RULE_TYPE_HOST_NAME,
|
||||
'invert': False,
|
||||
'value': 'localhost',
|
||||
'admin_state_up': True,
|
||||
'tenant_id': self._tenant_id
|
||||
}
|
||||
create_rules = [create_rule]
|
||||
expected_rule = {
|
||||
'key': None
|
||||
}
|
||||
expected_rule.update(create_rule)
|
||||
expected_rules = [expected_rule]
|
||||
return create_rules, expected_rules
|
||||
|
||||
def create_graph(self, expected_lb_graph, listeners):
|
||||
with self.subnet() as subnet:
|
||||
expected_lb_graph['vip_subnet_id'] = subnet['subnet']['id']
|
||||
for listener in listeners:
|
||||
for member in listener.get('default_pool',
|
||||
{}).get('members', []):
|
||||
member['subnet_id'] = subnet['subnet']['id']
|
||||
for listener in expected_lb_graph.get('listeners', []):
|
||||
for member in listener.get('default_pool',
|
||||
{}).get('members', []):
|
||||
member['subnet_id'] = subnet['subnet']['id']
|
||||
name = expected_lb_graph.get('name')
|
||||
kwargs = {'name': name, 'subnet': subnet, 'listeners': listeners}
|
||||
with self.graph(**kwargs) as graph:
|
||||
lb = graph['graph']['loadbalancer']
|
||||
self._assert_graphs_equal(expected_lb_graph, lb)
|
||||
self._validate_graph_statuses(lb)
|
||||
return graph
|
||||
|
||||
def test_with_one_listener(self):
|
||||
create_listener, expected_listener = self._get_listener_bodies()
|
||||
expected_lb = self._get_expected_lb([expected_listener])
|
||||
self.create_graph(expected_lb, [create_listener])
|
||||
|
||||
def test_with_many_listeners(self):
|
||||
create_listener1, expected_listener1 = self._get_listener_bodies()
|
||||
create_listener2, expected_listener2 = self._get_listener_bodies(
|
||||
name='listener2', protocol_port=81)
|
||||
expected_lb = self._get_expected_lb(
|
||||
[expected_listener1, expected_listener2])
|
||||
self.create_graph(expected_lb,
|
||||
[create_listener1, create_listener2])
|
||||
|
||||
def test_with_many_listeners_same_port(self):
|
||||
create_listener1, expected_listener1 = self._get_listener_bodies()
|
||||
create_listener2, expected_listener2 = self._get_listener_bodies()
|
||||
try:
|
||||
self.create_graph(
|
||||
{}, [create_listener1, create_listener2])
|
||||
except webob.exc.HTTPClientError as exc:
|
||||
self.assertEqual(exc.status_code, 409)
|
||||
|
||||
def test_with_one_listener_one_pool(self):
|
||||
create_pool, expected_pool = self._get_pool_bodies()
|
||||
create_listener, expected_listener = self._get_listener_bodies(
|
||||
create_default_pool=create_pool,
|
||||
expected_default_pool=expected_pool)
|
||||
expected_lb = self._get_expected_lb([expected_listener])
|
||||
self.create_graph(expected_lb, [create_listener])
|
||||
|
||||
def test_with_many_listeners_many_pools(self):
|
||||
create_pool1, expected_pool1 = self._get_pool_bodies()
|
||||
create_pool2, expected_pool2 = self._get_pool_bodies(name='pool2')
|
||||
create_listener1, expected_listener1 = self._get_listener_bodies(
|
||||
create_default_pool=create_pool1,
|
||||
expected_default_pool=expected_pool1)
|
||||
create_listener2, expected_listener2 = self._get_listener_bodies(
|
||||
name='listener2', protocol_port=81,
|
||||
create_default_pool=create_pool2,
|
||||
expected_default_pool=expected_pool2)
|
||||
expected_lb = self._get_expected_lb(
|
||||
[expected_listener1, expected_listener2])
|
||||
self.create_graph(
|
||||
expected_lb, [create_listener1, create_listener2])
|
||||
|
||||
def test_with_one_listener_one_member(self):
|
||||
create_member, expected_member = self._get_member_bodies()
|
||||
create_pool, expected_pool = self._get_pool_bodies(
|
||||
create_members=[create_member],
|
||||
expected_members=[expected_member])
|
||||
create_listener, expected_listener = self._get_listener_bodies(
|
||||
create_default_pool=create_pool,
|
||||
expected_default_pool=expected_pool)
|
||||
expected_lb = self._get_expected_lb([expected_listener])
|
||||
self.create_graph(expected_lb, [create_listener])
|
||||
|
||||
def test_with_one_listener_one_hm(self):
|
||||
create_hm, expected_hm = self._get_hm_bodies()
|
||||
create_pool, expected_pool = self._get_pool_bodies(
|
||||
create_hm=create_hm,
|
||||
expected_hm=expected_hm)
|
||||
create_listener, expected_listener = self._get_listener_bodies(
|
||||
create_default_pool=create_pool,
|
||||
expected_default_pool=expected_pool)
|
||||
expected_lb = self._get_expected_lb([expected_listener])
|
||||
self.create_graph(expected_lb, [create_listener])
|
||||
|
||||
def test_with_one_of_everything(self):
|
||||
create_member, expected_member = self._get_member_bodies()
|
||||
create_hm, expected_hm = self._get_hm_bodies()
|
||||
create_pool, expected_pool = self._get_pool_bodies(
|
||||
create_members=[create_member],
|
||||
expected_members=[expected_member],
|
||||
create_hm=create_hm,
|
||||
expected_hm=expected_hm)
|
||||
create_r_member, expected_r_member = self._get_member_bodies(
|
||||
name='r_member1')
|
||||
create_r_hm, expected_r_hm = self._get_hm_bodies(name='r_hm1')
|
||||
create_r_pool, expected_r_pool = self._get_pool_bodies(
|
||||
create_members=[create_r_member],
|
||||
expected_members=[expected_r_member],
|
||||
create_hm=create_r_hm,
|
||||
expected_hm=expected_r_hm)
|
||||
create_rules, expected_rules = self._get_l7rules_bodes()
|
||||
create_l7_policies, expected_l7_policies = self._get_l7policies_bodies(
|
||||
create_rules=create_rules, expected_rules=expected_rules,
|
||||
create_r_pool=create_r_pool, expected_r_pool=expected_r_pool)
|
||||
create_listener, expected_listener = self._get_listener_bodies(
|
||||
create_default_pool=create_pool,
|
||||
expected_default_pool=expected_pool,
|
||||
create_l7_policies=create_l7_policies,
|
||||
expected_l7_policies=expected_l7_policies)
|
||||
expected_lb = self._get_expected_lb([expected_listener])
|
||||
self.create_graph(expected_lb, [create_listener])
|
||||
|
||||
|
||||
class ListenerTestBase(LbaasPluginDbTestCase):
|
||||
def setUp(self):
|
||||
super(ListenerTestBase, self).setUp()
|
||||
|
|
|
@ -59,31 +59,45 @@ class ManagerTest(object):
|
|||
class BaseOctaviaDriverTest(test_db_loadbalancerv2.LbaasPluginDbTestCase):
|
||||
|
||||
# Copied it from Brocade's test code :/
|
||||
def _create_fake_models(self):
|
||||
def _create_fake_models(self, children=True, graph=False):
|
||||
# This id is used for all the entities.
|
||||
id = 'test_id'
|
||||
lb = data_models.LoadBalancer(id=id)
|
||||
if not children:
|
||||
return lb
|
||||
sni_container = data_models.SNI(listener_id=id)
|
||||
listener = data_models.Listener(id=id, loadbalancer=lb,
|
||||
sni_containers=[sni_container])
|
||||
pool = data_models.Pool(id=id, loadbalancer=lb)
|
||||
member = data_models.Member(id=id, pool=pool)
|
||||
hm = data_models.HealthMonitor(id=id, pool=pool)
|
||||
sp = data_models.SessionPersistence(pool_id=pool.id, pool=pool)
|
||||
l7policy = data_models.L7Policy(
|
||||
id=id, listener=listener, redirect_pool_id=pool.id,
|
||||
id=id, listener=listener,
|
||||
action=constants.L7_POLICY_ACTION_REDIRECT_TO_POOL)
|
||||
l7rule = data_models.L7Rule(
|
||||
id=id, policy=l7policy,
|
||||
type=constants.L7_RULE_TYPE_PATH,
|
||||
id=id, policy=l7policy, type=constants.L7_RULE_TYPE_PATH,
|
||||
compare_type=constants.L7_RULE_COMPARE_TYPE_STARTS_WITH,
|
||||
value='/api')
|
||||
lb.listeners = [listener]
|
||||
lb.pools = [pool]
|
||||
if graph:
|
||||
r_pool = data_models.Pool(id=id, loadbalancer=lb)
|
||||
r_member = data_models.Member(id=id, pool=r_pool)
|
||||
r_pool.members = [r_member]
|
||||
l7policy.redirect_pool = r_pool
|
||||
l7policy.redirect_pool_id = r_pool.id
|
||||
lb.pools.append(r_pool)
|
||||
else:
|
||||
l7policy.redirect_pool = pool
|
||||
l7policy.redirect_pool_id = pool.id
|
||||
listener.default_pool = pool
|
||||
listener.l7policies = [l7policy]
|
||||
listener.l7_policies = [l7policy]
|
||||
l7policy.rules = [l7rule]
|
||||
pool.members = [member]
|
||||
pool.session_persistence = sp
|
||||
pool.healthmonitor = hm
|
||||
|
||||
return lb
|
||||
|
||||
def setUp(self):
|
||||
|
@ -105,11 +119,124 @@ class TestOctaviaDriver(BaseOctaviaDriverTest):
|
|||
test_driver = driver.OctaviaDriver(self.plugin)
|
||||
self.assertTrue(test_driver.load_balancer.allocates_vip)
|
||||
|
||||
def test_create_load_balancer_graph(self):
|
||||
m = ManagerTest(self, self.driver.load_balancer,
|
||||
self.driver.req)
|
||||
lb = self._create_fake_models(children=True, graph=True)
|
||||
listener = lb.listeners[0]
|
||||
sni_container = listener.sni_containers[0]
|
||||
policy = listener.l7_policies[0]
|
||||
r_pool = policy.redirect_pool
|
||||
r_member = r_pool.members[0]
|
||||
rule = policy.rules[0]
|
||||
pool = listener.default_pool
|
||||
member = pool.members[0]
|
||||
sp = pool.session_persistence
|
||||
hm = pool.healthmonitor
|
||||
|
||||
lb_url = '/v1/loadbalancers'
|
||||
|
||||
args = {
|
||||
"id": lb.id,
|
||||
"name": lb.name,
|
||||
"enabled": lb.admin_state_up,
|
||||
"vip": {
|
||||
"subnet_id": lb.vip_subnet_id,
|
||||
"port_id": lb.vip_port_id,
|
||||
"ip_address": lb.vip_address
|
||||
},
|
||||
"listeners": [{
|
||||
"id": listener.id,
|
||||
"protocol": listener.protocol,
|
||||
"enabled": listener.admin_state_up,
|
||||
"sni_containers": [sni_container.tls_container_id],
|
||||
"tls_certificate_id": listener.default_tls_container_id,
|
||||
"l7policies": [{
|
||||
"id": policy.id,
|
||||
"name": policy.name,
|
||||
"redirect_pool": {
|
||||
"id": r_pool.id,
|
||||
"lb_algorithm": r_pool.lb_algorithm,
|
||||
"protocol": r_pool.protocol,
|
||||
"name": r_pool.name,
|
||||
"enabled": r_pool.admin_state_up,
|
||||
"session_persistence": r_pool.session_persistence,
|
||||
"members": [{
|
||||
"project_id": r_member.tenant_id,
|
||||
"weight": r_member.weight,
|
||||
"subnet_id": r_member.subnet_id,
|
||||
"ip_address": r_member.address,
|
||||
"protocol_port": r_member.protocol_port,
|
||||
"enabled": r_member.admin_state_up,
|
||||
"id": r_member.id
|
||||
}],
|
||||
"project_id": r_pool.tenant_id,
|
||||
"description": r_pool.description
|
||||
},
|
||||
"l7rules": [{
|
||||
"id": rule.id,
|
||||
"type": rule.type,
|
||||
"compare_type": rule.compare_type,
|
||||
"key": rule.key,
|
||||
"value": rule.value,
|
||||
"invert": rule.invert
|
||||
}],
|
||||
"enabled": policy.admin_state_up,
|
||||
"action": policy.action,
|
||||
"position": policy.position,
|
||||
"description": policy.description
|
||||
}],
|
||||
"name": listener.name,
|
||||
"description": listener.description,
|
||||
"default_pool": {
|
||||
"id": pool.id,
|
||||
"lb_algorithm": pool.lb_algorithm,
|
||||
"protocol": pool.protocol,
|
||||
"name": pool.name,
|
||||
"enabled": pool.admin_state_up,
|
||||
"session_persistence": {
|
||||
"cookie_name": sp.cookie_name,
|
||||
"type": sp.type
|
||||
},
|
||||
"members": [{
|
||||
"project_id": member.tenant_id,
|
||||
"weight": member.weight,
|
||||
"subnet_id": member.subnet_id,
|
||||
"ip_address": member.address,
|
||||
"protocol_port": member.protocol_port,
|
||||
"enabled": member.admin_state_up,
|
||||
"id": member.id
|
||||
}],
|
||||
"health_monitor": {
|
||||
'type': hm.type,
|
||||
'delay': hm.delay,
|
||||
'timeout': hm.timeout,
|
||||
'rise_threshold': hm.max_retries,
|
||||
'fall_threshold': hm.max_retries,
|
||||
'http_method': hm.http_method,
|
||||
'url_path': hm.url_path,
|
||||
'expected_codes': hm.expected_codes,
|
||||
'enabled': hm.admin_state_up,
|
||||
'project_id': hm.tenant_id
|
||||
},
|
||||
"project_id": pool.tenant_id,
|
||||
"description": pool.description
|
||||
},
|
||||
"connection_limit": listener.connection_limit,
|
||||
"protocol_port": listener.protocol_port,
|
||||
"project_id": listener.tenant_id
|
||||
}],
|
||||
"project_id": lb.tenant_id,
|
||||
"description": lb.description
|
||||
}
|
||||
|
||||
m.create(lb, lb_url, args)
|
||||
|
||||
def test_load_balancer_ops(self):
|
||||
m = ManagerTest(self, self.driver.load_balancer,
|
||||
self.driver.req)
|
||||
|
||||
lb = self.lb
|
||||
lb = self._create_fake_models(children=False)
|
||||
|
||||
# urls for assert test.
|
||||
lb_url = '/v1/loadbalancers'
|
||||
|
@ -133,7 +260,7 @@ class TestOctaviaDriver(BaseOctaviaDriverTest):
|
|||
|
||||
# Update LB test
|
||||
# args for update assert.
|
||||
args = args = {
|
||||
args = {
|
||||
'name': lb.name,
|
||||
'description': lb.description,
|
||||
'enabled': lb.admin_state_up,
|
||||
|
@ -298,7 +425,7 @@ class TestOctaviaDriver(BaseOctaviaDriverTest):
|
|||
m = ManagerTest(self, self.driver.l7policy,
|
||||
self.driver.req)
|
||||
|
||||
l7p = copy.deepcopy(self.lb.listeners[0].l7policies[0])
|
||||
l7p = copy.deepcopy(self.lb.listeners[0].l7_policies[0])
|
||||
l7p.action = constants.L7_POLICY_ACTION_REJECT
|
||||
|
||||
# urls for assert.
|
||||
|
@ -330,7 +457,7 @@ class TestOctaviaDriver(BaseOctaviaDriverTest):
|
|||
m = ManagerTest(self, self.driver.l7policy,
|
||||
self.driver.req)
|
||||
|
||||
l7p = copy.deepcopy(self.lb.listeners[0].l7policies[0])
|
||||
l7p = copy.deepcopy(self.lb.listeners[0].l7_policies[0])
|
||||
l7p.action = constants.L7_POLICY_ACTION_REDIRECT_TO_POOL
|
||||
|
||||
# urls for assert.
|
||||
|
@ -363,7 +490,7 @@ class TestOctaviaDriver(BaseOctaviaDriverTest):
|
|||
m = ManagerTest(self, self.driver.l7policy,
|
||||
self.driver.req)
|
||||
|
||||
l7p = copy.deepcopy(self.lb.listeners[0].l7policies[0])
|
||||
l7p = copy.deepcopy(self.lb.listeners[0].l7_policies[0])
|
||||
l7p.action = constants.L7_POLICY_ACTION_REDIRECT_TO_URL
|
||||
|
||||
# urls for assert.
|
||||
|
@ -396,7 +523,7 @@ class TestOctaviaDriver(BaseOctaviaDriverTest):
|
|||
m = ManagerTest(self, self.driver.l7rule,
|
||||
self.driver.req)
|
||||
|
||||
l7r = self.lb.listeners[0].l7policies[0].rules[0]
|
||||
l7r = self.lb.listeners[0].l7_policies[0].rules[0]
|
||||
|
||||
# urls for assert.
|
||||
l7r_url = '/v1/loadbalancers/%s/listeners/%s/l7policies/%s/l7rules' % (
|
||||
|
|
|
@ -32,11 +32,11 @@ _uuid = uuidutils.generate_uuid
|
|||
_get_path = test_base._get_path
|
||||
|
||||
|
||||
class LoadBalancerExtensionTestCase(base.ExtensionTestCase):
|
||||
class TestLoadBalancerExtensionTestCase(base.ExtensionTestCase):
|
||||
fmt = 'json'
|
||||
|
||||
def setUp(self):
|
||||
super(LoadBalancerExtensionTestCase, self).setUp()
|
||||
super(TestLoadBalancerExtensionTestCase, self).setUp()
|
||||
self._setUpExtension(
|
||||
'neutron_lbaas.extensions.loadbalancer.LoadBalancerPluginBase',
|
||||
constants.LOADBALANCER, loadbalancer.RESOURCE_ATTRIBUTE_MAP,
|
||||
|
@ -487,11 +487,11 @@ class LoadBalancerExtensionTestCase(base.ExtensionTestCase):
|
|||
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
|
||||
|
||||
|
||||
class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase):
|
||||
class TestLoadBalancerExtensionV2TestCase(base.ExtensionTestCase):
|
||||
fmt = 'json'
|
||||
|
||||
def setUp(self):
|
||||
super(LoadBalancerExtensionV2TestCase, self).setUp()
|
||||
super(TestLoadBalancerExtensionV2TestCase, self).setUp()
|
||||
resource_map = loadbalancerv2.RESOURCE_ATTRIBUTE_MAP.copy()
|
||||
for k in sharedpools.EXTENDED_ATTRIBUTES_2_0.keys():
|
||||
resource_map[k].update(sharedpools.EXTENDED_ATTRIBUTES_2_0[k])
|
||||
|
|
|
@ -176,7 +176,8 @@ class LBaaSAgentSchedulerTestCase(test_agent.AgentDBTestMixIn,
|
|||
'flavor_id': n_constants.ATTR_NOT_SPECIFIED,
|
||||
'vip_address': n_constants.ATTR_NOT_SPECIFIED,
|
||||
'admin_state_up': True,
|
||||
'tenant_id': self._tenant_id}}
|
||||
'tenant_id': self._tenant_id,
|
||||
'listeners': []}}
|
||||
self.assertRaises(lbaas_agentschedulerv2.NoEligibleLbaasAgent,
|
||||
self.lbaas_plugin.create_loadbalancer,
|
||||
self.adminContext, lb)
|
||||
|
@ -211,7 +212,8 @@ class LBaaSAgentSchedulerTestCase(test_agent.AgentDBTestMixIn,
|
|||
'flavor_id': n_constants.ATTR_NOT_SPECIFIED,
|
||||
'vip_address': n_constants.ATTR_NOT_SPECIFIED,
|
||||
'admin_state_up': True,
|
||||
'tenant_id': self._tenant_id}}
|
||||
'tenant_id': self._tenant_id,
|
||||
'listeners': []}}
|
||||
self.assertRaises(lbaas_agentschedulerv2.NoEligibleLbaasAgent,
|
||||
self.lbaas_plugin.create_loadbalancer,
|
||||
self.adminContext, lb)
|
||||
|
|
Loading…
Reference in New Issue