Get Me A Load Balancer API
Change-Id: Id3a5ddb8efded8c6ad72a7118424ec01c777318d
This commit is contained in:
parent
06f46b7cec
commit
37186b62d1
|
@ -16,9 +16,6 @@ from oslo_config import cfg
|
|||
from pecan import rest
|
||||
from stevedore import driver as stevedore_driver
|
||||
|
||||
from octavia.api.v1.types import listener as listener_types
|
||||
from octavia.api.v1.types import load_balancer as lb_types
|
||||
from octavia.api.v1.types import pool as pool_types
|
||||
from octavia.db import repositories
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -35,7 +32,7 @@ class BaseController(rest.RestController):
|
|||
).driver
|
||||
|
||||
@staticmethod
|
||||
def _convert_db_to_type(db_entity, to_type):
|
||||
def _convert_db_to_type(db_entity, to_type, children=False):
|
||||
"""Converts a data model into a Octavia WSME type
|
||||
|
||||
:param db_entity: data model to convert
|
||||
|
@ -45,18 +42,7 @@ class BaseController(rest.RestController):
|
|||
to_type = to_type[0]
|
||||
|
||||
def _convert(db_obj):
|
||||
api_type = to_type.from_data_model(db_obj)
|
||||
if to_type == lb_types.LoadBalancerResponse:
|
||||
api_type.vip = lb_types.VIP.from_data_model(db_obj.vip)
|
||||
elif (to_type == pool_types.PoolResponse
|
||||
and db_obj.session_persistence):
|
||||
api_type.session_persistence = (
|
||||
pool_types.SessionPersistenceResponse.from_data_model(
|
||||
db_obj.session_persistence))
|
||||
elif to_type == listener_types.ListenerResponse:
|
||||
api_type.sni_containers = [sni_c.tls_container_id
|
||||
for sni_c in db_obj.sni_containers]
|
||||
return api_type
|
||||
return to_type.from_data_model(db_obj, children=children)
|
||||
if isinstance(db_entity, list):
|
||||
converted = [_convert(db_obj) for db_obj in db_entity]
|
||||
else:
|
||||
|
|
|
@ -24,6 +24,7 @@ from octavia.api.v1.types import health_monitor as hm_types
|
|||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import prepare as db_prepare
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
|
@ -66,8 +67,9 @@ class HealthMonitorController(base.BaseController):
|
|||
raise exceptions.DuplicateHealthMonitor()
|
||||
except exceptions.NotFound:
|
||||
pass
|
||||
hm_dict = health_monitor.to_dict()
|
||||
hm_dict['pool_id'] = self.pool_id
|
||||
hm_dict = db_prepare.create_health_monitor(
|
||||
health_monitor.to_dict(), self.pool_id)
|
||||
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
|
|
|
@ -27,6 +27,7 @@ from octavia.common import constants
|
|||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import api as db_api
|
||||
from octavia.db import prepare as db_prepare
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
|
@ -84,16 +85,15 @@ class ListenersController(base.BaseController):
|
|||
Update the load balancer db when provisioning status changes.
|
||||
"""
|
||||
try:
|
||||
sni_container_ids = listener_dict.pop('sni_containers')
|
||||
sni_containers = listener_dict.pop('sni_containers', [])
|
||||
db_listener = self.repositories.listener.create(
|
||||
context.session, **listener_dict)
|
||||
if sni_container_ids is not None:
|
||||
for container_id in sni_container_ids:
|
||||
sni_dict = {'listener_id': db_listener.id,
|
||||
'tls_container_id': container_id}
|
||||
self.repositories.sni.create(context.session, **sni_dict)
|
||||
db_listener = self.repositories.listener.get(context.session,
|
||||
id=db_listener.id)
|
||||
if sni_containers:
|
||||
for sni_container in sni_containers:
|
||||
self.repositories.sni.create(
|
||||
context.session, **sni_container)
|
||||
db_listener = self.repositories.listener.get(
|
||||
context.session, id=db_listener.id)
|
||||
except odb_exceptions.DBDuplicateEntry as de:
|
||||
# Setting LB back to active because this is just a validation
|
||||
# failure
|
||||
|
@ -133,14 +133,8 @@ class ListenersController(base.BaseController):
|
|||
self._secure_data(listener)
|
||||
lb_repo = self.repositories.load_balancer
|
||||
self._test_lb_status_post(context, lb_repo)
|
||||
listener_dict = listener.to_dict()
|
||||
listener_dict['load_balancer_id'] = self.load_balancer_id
|
||||
listener_dict['provisioning_status'] = constants.PENDING_CREATE
|
||||
listener_dict['operating_status'] = constants.OFFLINE
|
||||
# NOTE(blogan): Throwing away because we should not store secure data
|
||||
# in the database nor should we send it to a handler.
|
||||
if 'tls_termination' in listener_dict:
|
||||
del listener_dict['tls_termination']
|
||||
listener_dict = db_prepare.create_listener(
|
||||
listener.to_dict(), self.load_balancer_id)
|
||||
# This is the extra validation layer for wrong protocol or duplicate
|
||||
# listeners on the same load balancer.
|
||||
return self._validate_listeners(context, lb_repo, listener_dict)
|
||||
|
|
|
@ -26,6 +26,7 @@ from octavia.common import constants
|
|||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import api as db_api
|
||||
from octavia.db import prepare as db_prepare
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
|
@ -65,19 +66,38 @@ class LoadBalancersController(base.BaseController):
|
|||
return self._convert_db_to_type(load_balancers,
|
||||
[lb_types.LoadBalancerResponse])
|
||||
|
||||
def _create_load_balancer_tree(self, context, load_balancer):
|
||||
prepped_lb = db_prepare.create_load_balancer_tree(
|
||||
load_balancer.to_dict())
|
||||
try:
|
||||
db_lb = self.repositories.create_load_balancer_tree(
|
||||
context.session, prepped_lb)
|
||||
except Exception:
|
||||
# TODO(blogan): handle exceptions
|
||||
raise
|
||||
try:
|
||||
LOG.info(_LI("Sending full load balancer configuration %s to "
|
||||
"the handler"), db_lb.id)
|
||||
self.handler.create(db_lb)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
self.repositories.load_balancer.update(
|
||||
context.session, db_lb.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
return self._convert_db_to_type(db_lb, lb_types.LoadBalancerResponse,
|
||||
children=True)
|
||||
|
||||
@wsme_pecan.wsexpose(lb_types.LoadBalancerResponse,
|
||||
body=lb_types.LoadBalancerPOST, status_code=202)
|
||||
def post(self, load_balancer):
|
||||
"""Creates a load balancer."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
lb_dict = load_balancer.to_dict()
|
||||
vip_dict = lb_dict.pop('vip')
|
||||
lb_dict['provisioning_status'] = constants.PENDING_CREATE
|
||||
lb_dict['operating_status'] = constants.OFFLINE
|
||||
lb_dict['project_id'] = lb_dict.get('project_id') or context.project_id
|
||||
if load_balancer.listeners:
|
||||
return self._create_load_balancer_tree(context, load_balancer)
|
||||
lb_dict = db_prepare.create_load_balancer(load_balancer.to_dict())
|
||||
try:
|
||||
db_lb = self.repositories.create_load_balancer_and_vip(
|
||||
context.session, lb_dict, vip_dict)
|
||||
context.session, lb_dict, lb_dict.pop('vip', {}))
|
||||
except odb_exceptions.DBDuplicateEntry:
|
||||
raise exceptions.IDAlreadyExists()
|
||||
# Handler will be responsible for sending to controller
|
||||
|
|
|
@ -25,6 +25,7 @@ from octavia.api.v1.types import member as member_types
|
|||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import prepare as db_prepare
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
|
@ -65,9 +66,8 @@ class MembersController(base.BaseController):
|
|||
def post(self, member):
|
||||
"""Creates a pool member on a pool."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
member_dict = member.to_dict()
|
||||
member_dict['pool_id'] = self.pool_id
|
||||
member_dict['operating_status'] = constants.OFFLINE
|
||||
member_dict = db_prepare.create_member(member.to_dict(), self.pool_id)
|
||||
|
||||
# Verify load balancer is in a mutable status. If so it can be assumed
|
||||
# that the listener is also in a mutable status because a load balancer
|
||||
# will only be ACTIVE when all its listeners as ACTIVE.
|
||||
|
|
|
@ -27,6 +27,7 @@ from octavia.api.v1.types import pool as pool_types
|
|||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import prepare as db_prepare
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
|
@ -131,9 +132,8 @@ class PoolsController(base.BaseController):
|
|||
# will only be ACTIVE when all it's listeners as ACTIVE.
|
||||
|
||||
self._test_lb_status(context.session)
|
||||
pool_dict = pool.to_dict()
|
||||
pool_dict = db_prepare.create_pool(pool.to_dict())
|
||||
sp_dict = pool_dict.pop('session_persistence', None)
|
||||
pool_dict['operating_status'] = constants.OFFLINE
|
||||
|
||||
return self._validate_create_pool(context.session, sp_dict, pool_dict)
|
||||
|
||||
|
|
|
@ -40,10 +40,11 @@ class IPAddressType(wtypes.UserType):
|
|||
|
||||
class BaseType(wtypes.Base):
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model):
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
"""Converts data_model to Octavia WSME type.
|
||||
|
||||
:param data_model: data model to convert from
|
||||
:param children: convert child data models
|
||||
"""
|
||||
return cls(**data_model.to_dict())
|
||||
|
||||
|
@ -62,6 +63,9 @@ class BaseType(wtypes.Base):
|
|||
continue
|
||||
if value and isinstance(value, BaseType):
|
||||
value = value.to_dict()
|
||||
if value and isinstance(value, list):
|
||||
value = [val.to_dict() if isinstance(val, BaseType) else val
|
||||
for val in value]
|
||||
if isinstance(value, wtypes.UnsetType):
|
||||
if render_unsets:
|
||||
value = None
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
from wsme import types as wtypes
|
||||
|
||||
from octavia.api.v1.types import base
|
||||
from octavia.api.v1.types import pool
|
||||
|
||||
|
||||
class TLSTermination(base.BaseType):
|
||||
|
@ -38,6 +39,27 @@ class ListenerResponse(base.BaseType):
|
|||
tls_certificate_id = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
sni_containers = [wtypes.StringType(max_length=255)]
|
||||
project_id = wtypes.wsattr(wtypes.UuidType())
|
||||
default_pool = wtypes.wsattr(pool.PoolResponse)
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
listener = super(ListenerResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
# NOTE(blogan): we should show sni_containers for every call to show
|
||||
# a listener
|
||||
listener.sni_containers = [sni_c.tls_container_id
|
||||
for sni_c in data_model.sni_containers]
|
||||
if not children:
|
||||
# NOTE(blogan): do not show default_pool if the request does not
|
||||
# want to see children
|
||||
del listener.default_pool
|
||||
return listener
|
||||
if data_model.default_pool:
|
||||
listener.default_pool = pool.PoolResponse.from_data_model(
|
||||
data_model.default_pool, children=children)
|
||||
if not listener.default_pool:
|
||||
del listener.default_pool
|
||||
return listener
|
||||
|
||||
|
||||
class ListenerPOST(base.BaseType):
|
||||
|
@ -53,6 +75,7 @@ class ListenerPOST(base.BaseType):
|
|||
tls_termination = wtypes.wsattr(TLSTermination)
|
||||
sni_containers = [wtypes.StringType(max_length=255)]
|
||||
project_id = wtypes.wsattr(wtypes.UuidType())
|
||||
default_pool = wtypes.wsattr(pool.PoolPOST)
|
||||
|
||||
|
||||
class ListenerPUT(base.BaseType):
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
from wsme import types as wtypes
|
||||
|
||||
from octavia.api.v1.types import base
|
||||
from octavia.api.v1.types import listener
|
||||
|
||||
|
||||
class VIP(base.BaseType):
|
||||
|
@ -34,6 +35,26 @@ class LoadBalancerResponse(base.BaseType):
|
|||
enabled = wtypes.wsattr(bool)
|
||||
vip = wtypes.wsattr(VIP)
|
||||
project_id = wtypes.wsattr(wtypes.UuidType())
|
||||
listeners = wtypes.wsattr([listener.ListenerResponse])
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
lb = super(LoadBalancerResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
# NOTE(blogan): VIP is technically a child but its the main piece of
|
||||
# a load balancer so it makes sense to show it no matter what.
|
||||
lb.vip = VIP.from_data_model(data_model.vip)
|
||||
if not children:
|
||||
# NOTE(blogan): don't show listeners if the request does not want
|
||||
# to see children
|
||||
del lb.listeners
|
||||
return lb
|
||||
lb.listeners = [
|
||||
listener.ListenerResponse.from_data_model(
|
||||
listener_dm, children=children)
|
||||
for listener_dm in data_model.listeners
|
||||
]
|
||||
return lb
|
||||
|
||||
|
||||
class LoadBalancerPOST(base.BaseType):
|
||||
|
@ -44,6 +65,7 @@ class LoadBalancerPOST(base.BaseType):
|
|||
enabled = wtypes.wsattr(bool, default=True)
|
||||
vip = wtypes.wsattr(VIP, mandatory=True)
|
||||
project_id = wtypes.wsattr(wtypes.UuidType())
|
||||
listeners = wtypes.wsattr([listener.ListenerPOST], default=[])
|
||||
|
||||
|
||||
class LoadBalancerPUT(base.BaseType):
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
from wsme import types as wtypes
|
||||
|
||||
from octavia.api.v1.types import base
|
||||
from octavia.api.v1.types import health_monitor
|
||||
from octavia.api.v1.types import member
|
||||
|
||||
|
||||
class SessionPersistenceResponse(base.BaseType):
|
||||
|
@ -46,6 +48,36 @@ class PoolResponse(base.BaseType):
|
|||
lb_algorithm = wtypes.wsattr(wtypes.text)
|
||||
session_persistence = wtypes.wsattr(SessionPersistenceResponse)
|
||||
project_id = wtypes.wsattr(wtypes.UuidType())
|
||||
health_monitor = wtypes.wsattr(health_monitor.HealthMonitorResponse)
|
||||
members = wtypes.wsattr([member.MemberResponse])
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
pool = super(PoolResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
# NOTE(blogan): we should show session persistence on every request
|
||||
# to show a pool
|
||||
if data_model.session_persistence:
|
||||
pool.session_persistence = (
|
||||
SessionPersistenceResponse.from_data_model(
|
||||
data_model.session_persistence))
|
||||
if not children:
|
||||
# NOTE(blogan): do not show members or health_monitor if the
|
||||
# request does not want to see children
|
||||
del pool.members
|
||||
del pool.health_monitor
|
||||
return pool
|
||||
pool.members = [
|
||||
member.MemberResponse.from_data_model(member_dm, children=children)
|
||||
for member_dm in data_model.members
|
||||
]
|
||||
if data_model.health_monitor:
|
||||
pool.health_monitor = (
|
||||
health_monitor.HealthMonitorResponse.from_data_model(
|
||||
data_model.health_monitor, children=children))
|
||||
if not pool.health_monitor:
|
||||
del pool.health_monitor
|
||||
return pool
|
||||
|
||||
|
||||
class PoolPOST(base.BaseType):
|
||||
|
@ -58,6 +90,8 @@ class PoolPOST(base.BaseType):
|
|||
lb_algorithm = wtypes.wsattr(wtypes.text, mandatory=True)
|
||||
session_persistence = wtypes.wsattr(SessionPersistencePOST)
|
||||
project_id = wtypes.wsattr(wtypes.UuidType())
|
||||
health_monitor = wtypes.wsattr(health_monitor.HealthMonitorPOST)
|
||||
members = wtypes.wsattr([member.MemberPOST])
|
||||
|
||||
|
||||
class PoolPUT(base.BaseType):
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
# Copyright 2015 Rackspace
|
||||
#
|
||||
# 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_utils import uuidutils
|
||||
|
||||
from octavia.common import constants
|
||||
|
||||
|
||||
def create_load_balancer_tree(lb_dict):
|
||||
listeners = lb_dict.pop('listeners', [])
|
||||
prepped_lb = create_load_balancer(lb_dict)
|
||||
prepped_lb['listeners'] = []
|
||||
for listener_dict in listeners:
|
||||
pool = listener_dict.pop('default_pool', None)
|
||||
prepped_listener = create_listener(listener_dict, prepped_lb.get('id'))
|
||||
if pool:
|
||||
hm = pool.pop('health_monitor', None)
|
||||
members = pool.pop('members', []) or []
|
||||
prepped_pool = create_pool(pool)
|
||||
pool_id = prepped_pool.get('id')
|
||||
if hm:
|
||||
prepped_hm = create_health_monitor(hm, pool_id)
|
||||
prepped_pool['health_monitor'] = prepped_hm
|
||||
prepped_pool['members'] = []
|
||||
for member_dict in members:
|
||||
prepped_pool['members'].append(
|
||||
create_member(member_dict, pool_id))
|
||||
prepped_listener['default_pool'] = prepped_pool
|
||||
prepped_lb['listeners'].append(prepped_listener)
|
||||
return prepped_lb
|
||||
|
||||
|
||||
def create_load_balancer(lb_dict):
|
||||
if not lb_dict.get('id'):
|
||||
lb_dict['id'] = uuidutils.generate_uuid()
|
||||
if lb_dict.get('vip'):
|
||||
lb_dict['vip']['load_balancer_id'] = lb_dict.get('id')
|
||||
lb_dict['provisioning_status'] = constants.PENDING_CREATE
|
||||
lb_dict['operating_status'] = constants.OFFLINE
|
||||
return lb_dict
|
||||
|
||||
|
||||
def create_listener(listener_dict, lb_id):
|
||||
if not listener_dict.get('id'):
|
||||
listener_dict['id'] = uuidutils.generate_uuid()
|
||||
listener_dict['load_balancer_id'] = lb_id
|
||||
listener_dict['provisioning_status'] = constants.PENDING_CREATE
|
||||
listener_dict['operating_status'] = constants.OFFLINE
|
||||
# NOTE(blogan): Throwing away because we should not store secure data
|
||||
# in the database nor should we send it to a handler.
|
||||
if 'tls_termination' in listener_dict:
|
||||
del listener_dict['tls_termination']
|
||||
sni_container_ids = listener_dict.pop('sni_containers', []) or []
|
||||
sni_containers = [{'listener_id': listener_dict.get('id'),
|
||||
'tls_container_id': sni_container_id}
|
||||
for sni_container_id in sni_container_ids]
|
||||
listener_dict['sni_containers'] = sni_containers
|
||||
return listener_dict
|
||||
|
||||
|
||||
def create_pool(pool_dict):
|
||||
if not pool_dict.get('id'):
|
||||
pool_dict['id'] = uuidutils.generate_uuid()
|
||||
if pool_dict.get('session_persistence'):
|
||||
pool_dict['session_persistence']['pool_id'] = pool_dict.get('id')
|
||||
if 'members' in pool_dict and not pool_dict.get('members'):
|
||||
del pool_dict['members']
|
||||
pool_dict['operating_status'] = constants.OFFLINE
|
||||
return pool_dict
|
||||
|
||||
|
||||
def create_member(member_dict, pool_id):
|
||||
member_dict['pool_id'] = pool_id
|
||||
member_dict['operating_status'] = constants.OFFLINE
|
||||
return member_dict
|
||||
|
||||
|
||||
def create_health_monitor(hm_dict, pool_id):
|
||||
hm_dict['pool_id'] = pool_id
|
||||
return hm_dict
|
|
@ -21,7 +21,6 @@ import datetime
|
|||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.common import exceptions
|
||||
|
@ -134,6 +133,37 @@ class Repositories(object):
|
|||
self.amphorahealth = AmphoraHealthRepository()
|
||||
self.vrrpgroup = VRRPGroupRepository()
|
||||
|
||||
def _create_lb_and_vip_transaction(self, session, lb_dict, vip_dict):
|
||||
with session.begin(subtransactions=True):
|
||||
lb = models.LoadBalancer(**lb_dict)
|
||||
session.add(lb)
|
||||
vip_dict['load_balancer_id'] = lb_dict['id']
|
||||
vip = models.Vip(**vip_dict)
|
||||
session.add(vip)
|
||||
return lb
|
||||
|
||||
def create_load_balancer_tree(self, session, lb_dict):
|
||||
listener_dicts = lb_dict.pop('listeners', [])
|
||||
vip_dict = lb_dict.pop('vip')
|
||||
with session.begin(subtransactions=True):
|
||||
lb_dm = self._create_lb_and_vip_transaction(
|
||||
session, lb_dict, vip_dict)
|
||||
for listener_dict in listener_dicts:
|
||||
pool_dict = listener_dict.pop('default_pool', None)
|
||||
if pool_dict:
|
||||
hm_dict = pool_dict.pop('health_monitor', None)
|
||||
member_dicts = pool_dict.pop('members', [])
|
||||
pool_dm = self.pool.create(session, **pool_dict)
|
||||
if hm_dict:
|
||||
hm_dict['pool_id'] = pool_dm.id
|
||||
self.health_monitor.create(session, **hm_dict)
|
||||
for member_dict in member_dicts:
|
||||
member_dict['pool_id'] = pool_dm.id
|
||||
self.member.create(session, **member_dict)
|
||||
listener_dict['default_pool_id'] = pool_dm.id
|
||||
self.listener.create(session, **listener_dict)
|
||||
return self.load_balancer.get(session, id=lb_dm.id)
|
||||
|
||||
def create_load_balancer_and_vip(self, session, lb_dict, vip_dict):
|
||||
"""Inserts load balancer and vip entities into the database.
|
||||
|
||||
|
@ -145,14 +175,9 @@ class Repositories(object):
|
|||
:param vip_dict: Dictionary representation of a vip
|
||||
:returns: octava.common.data_models.LoadBalancer
|
||||
"""
|
||||
with session.begin():
|
||||
if not lb_dict.get('id'):
|
||||
lb_dict['id'] = uuidutils.generate_uuid()
|
||||
lb = models.LoadBalancer(**lb_dict)
|
||||
session.add(lb)
|
||||
vip_dict['load_balancer_id'] = lb_dict['id']
|
||||
vip = models.Vip(**vip_dict)
|
||||
session.add(vip)
|
||||
with session.begin(subtransactions=True):
|
||||
lb = self._create_lb_and_vip_transaction(
|
||||
session, lb_dict, vip_dict)
|
||||
return self.load_balancer.get(session, id=lb.id)
|
||||
|
||||
def create_pool_on_listener(self, session, listener_id,
|
||||
|
@ -166,11 +191,8 @@ class Repositories(object):
|
|||
:returns: octavia.common.data_models.Pool
|
||||
"""
|
||||
with session.begin(subtransactions=True):
|
||||
if not pool_dict.get('id'):
|
||||
pool_dict['id'] = uuidutils.generate_uuid()
|
||||
db_pool = self.pool.create(session, **pool_dict)
|
||||
if sp_dict:
|
||||
sp_dict['pool_id'] = pool_dict['id']
|
||||
self.session_persistence.create(session, **sp_dict)
|
||||
self.listener.update(session, listener_id,
|
||||
default_pool_id=pool_dict['id'])
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
# 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 copy
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from octavia.common import constants
|
||||
|
@ -225,3 +228,233 @@ class TestLoadBalancer(base.BaseAPITest):
|
|||
def test_delete_bad_lb_id(self):
|
||||
path = self.LB_PATH.format(lb_id='bad_uuid')
|
||||
self.delete(path, status=404)
|
||||
|
||||
|
||||
class TestLoadBalancerGraph(base.BaseAPITest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestLoadBalancerGraph, self).setUp()
|
||||
self._project_id = uuidutils.generate_uuid()
|
||||
|
||||
def _assert_graphs_equal(self, expected_graph, observed_graph):
|
||||
observed_graph_copy = copy.deepcopy(observed_graph)
|
||||
obs_lb_id = observed_graph_copy.pop('id')
|
||||
self.assertTrue(uuidutils.is_uuid_like(obs_lb_id))
|
||||
expected_listeners = expected_graph.pop('listeners', [])
|
||||
observed_listeners = observed_graph_copy.pop('listeners', [])
|
||||
self.assertEqual(expected_graph, observed_graph_copy)
|
||||
for observed_listener in observed_listeners:
|
||||
self.assertTrue(uuidutils.is_uuid_like(
|
||||
observed_listener.pop('id')))
|
||||
default_pool = observed_listener.get('default_pool')
|
||||
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')
|
||||
self.assertIn(observed_listener, expected_listeners)
|
||||
|
||||
def _get_lb_bodies(self, create_listeners, expected_listeners):
|
||||
create_lb = {
|
||||
'name': 'lb1',
|
||||
'project_id': self._project_id,
|
||||
'vip': {},
|
||||
'listeners': create_listeners
|
||||
}
|
||||
expected_lb = {
|
||||
'description': None,
|
||||
'enabled': True,
|
||||
'provisioning_status': constants.PENDING_CREATE,
|
||||
'operating_status': constants.OFFLINE
|
||||
}
|
||||
expected_lb.update(create_lb)
|
||||
expected_lb['listeners'] = expected_listeners
|
||||
expected_lb['vip'] = {'ip_address': None, 'port_id': None,
|
||||
'subnet_id': None}
|
||||
return create_lb, expected_lb
|
||||
|
||||
def _get_listener_bodies(self, name='listener1', protocol_port=80,
|
||||
create_default_pool=None,
|
||||
expected_default_pool=None):
|
||||
create_listener = {
|
||||
'name': name,
|
||||
'protocol_port': protocol_port,
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'project_id': self._project_id
|
||||
}
|
||||
if create_default_pool:
|
||||
create_listener['default_pool'] = create_default_pool
|
||||
expected_listener = {
|
||||
'description': None,
|
||||
'tls_certificate_id': None,
|
||||
'sni_containers': [],
|
||||
'connection_limit': None,
|
||||
'enabled': True,
|
||||
'provisioning_status': constants.PENDING_CREATE,
|
||||
'operating_status': constants.OFFLINE
|
||||
}
|
||||
expected_listener.update(create_listener)
|
||||
if expected_default_pool:
|
||||
expected_listener['default_pool'] = expected_default_pool
|
||||
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': constants.PROTOCOL_HTTP,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'project_id': self._project_id
|
||||
}
|
||||
if create_members:
|
||||
create_pool['members'] = create_members
|
||||
if create_hm:
|
||||
create_pool['health_monitor'] = create_hm
|
||||
expected_pool = {
|
||||
'description': None,
|
||||
'session_persistence': None,
|
||||
'members': [],
|
||||
'enabled': True,
|
||||
'operating_status': constants.OFFLINE
|
||||
}
|
||||
expected_pool.update(create_pool)
|
||||
if expected_members:
|
||||
expected_pool['members'] = expected_members
|
||||
if expected_hm:
|
||||
expected_pool['health_monitor'] = expected_hm
|
||||
return create_pool, expected_pool
|
||||
|
||||
def _get_member_bodies(self):
|
||||
create_member = {
|
||||
'ip_address': '10.0.0.1',
|
||||
'protocol_port': 80,
|
||||
'project_id': self._project_id
|
||||
}
|
||||
expected_member = {
|
||||
'weight': 1,
|
||||
'enabled': True,
|
||||
'subnet_id': None,
|
||||
'operating_status': constants.OFFLINE
|
||||
}
|
||||
expected_member.update(create_member)
|
||||
return create_member, expected_member
|
||||
|
||||
def _get_hm_bodies(self):
|
||||
create_hm = {
|
||||
'type': constants.HEALTH_MONITOR_PING,
|
||||
'delay': 1,
|
||||
'timeout': 1,
|
||||
'fall_threshold': 1,
|
||||
'rise_threshold': 1,
|
||||
'project_id': self._project_id
|
||||
}
|
||||
expected_hm = {
|
||||
'http_method': None,
|
||||
'url_path': None,
|
||||
'expected_codes': None,
|
||||
'enabled': True
|
||||
}
|
||||
expected_hm.update(create_hm)
|
||||
return create_hm, expected_hm
|
||||
|
||||
def test_with_one_listener(self):
|
||||
create_listener, expected_listener = self._get_listener_bodies()
|
||||
create_lb, expected_lb = self._get_lb_bodies([create_listener],
|
||||
[expected_listener])
|
||||
response = self.post(self.LBS_PATH, create_lb)
|
||||
api_lb = response.json
|
||||
self._assert_graphs_equal(expected_lb, api_lb)
|
||||
|
||||
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
|
||||
)
|
||||
create_lb, expected_lb = self._get_lb_bodies(
|
||||
[create_listener1, create_listener2],
|
||||
[expected_listener1, expected_listener2])
|
||||
response = self.post(self.LBS_PATH, create_lb)
|
||||
api_lb = response.json
|
||||
self._assert_graphs_equal(expected_lb, api_lb)
|
||||
|
||||
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
|
||||
)
|
||||
create_lb, expected_lb = self._get_lb_bodies([create_listener],
|
||||
[expected_listener])
|
||||
response = self.post(self.LBS_PATH, create_lb)
|
||||
api_lb = response.json
|
||||
self._assert_graphs_equal(expected_lb, api_lb)
|
||||
|
||||
def test_with_many_listeners_one_pool(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(
|
||||
create_default_pool=create_pool2,
|
||||
expected_default_pool=expected_pool2,
|
||||
name='listener2', protocol_port=81
|
||||
)
|
||||
create_lb, expected_lb = self._get_lb_bodies(
|
||||
[create_listener1, create_listener2],
|
||||
[expected_listener1, expected_listener2])
|
||||
response = self.post(self.LBS_PATH, create_lb)
|
||||
api_lb = response.json
|
||||
self._assert_graphs_equal(expected_lb, api_lb)
|
||||
|
||||
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)
|
||||
create_lb, expected_lb = self._get_lb_bodies([create_listener],
|
||||
[expected_listener])
|
||||
response = self.post(self.LBS_PATH, create_lb)
|
||||
api_lb = response.json
|
||||
self._assert_graphs_equal(expected_lb, api_lb)
|
||||
|
||||
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)
|
||||
create_lb, expected_lb = self._get_lb_bodies([create_listener],
|
||||
[expected_listener])
|
||||
response = self.post(self.LBS_PATH, create_lb)
|
||||
api_lb = response.json
|
||||
self._assert_graphs_equal(expected_lb, api_lb)
|
||||
|
||||
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_listener, expected_listener = self._get_listener_bodies(
|
||||
create_default_pool=create_pool,
|
||||
expected_default_pool=expected_pool)
|
||||
create_lb, expected_lb = self._get_lb_bodies([create_listener],
|
||||
[expected_listener])
|
||||
response = self.post(self.LBS_PATH, create_lb)
|
||||
api_lb = response.json
|
||||
self._assert_graphs_equal(expected_lb, api_lb)
|
||||
|
|
|
@ -109,7 +109,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'operating_status': constants.OFFLINE,
|
||||
'topology': constants.TOPOLOGY_ACTIVE_STANDBY,
|
||||
'vrrp_group': None,
|
||||
'project_id': uuidutils.generate_uuid()}
|
||||
'project_id': uuidutils.generate_uuid(),
|
||||
'id': uuidutils.generate_uuid()}
|
||||
vip = {'ip_address': '10.0.0.1',
|
||||
'port_id': uuidutils.generate_uuid(),
|
||||
'subnet_id': uuidutils.generate_uuid()}
|
||||
|
@ -129,7 +130,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE,
|
||||
'project_id': uuidutils.generate_uuid()}
|
||||
'project_id': uuidutils.generate_uuid(),
|
||||
'id': uuidutils.generate_uuid()}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool)
|
||||
|
@ -148,9 +150,11 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE,
|
||||
'project_id': uuidutils.generate_uuid()}
|
||||
'project_id': uuidutils.generate_uuid(),
|
||||
'id': uuidutils.generate_uuid()}
|
||||
sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
'cookie_name': 'cookie_monster'}
|
||||
'cookie_name': 'cookie_monster',
|
||||
'pool_id': pool['id']}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool, sp_dict=sp)
|
||||
|
@ -176,7 +180,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE,
|
||||
'project_id': uuidutils.generate_uuid()}
|
||||
'project_id': uuidutils.generate_uuid(),
|
||||
'id': uuidutils.generate_uuid()}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id, pool)
|
||||
update_pool = {'protocol': constants.PROTOCOL_TCP, 'name': 'up_pool'}
|
||||
|
@ -196,9 +201,11 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE,
|
||||
'project_id': uuidutils.generate_uuid()}
|
||||
'project_id': uuidutils.generate_uuid(),
|
||||
'id': uuidutils.generate_uuid()}
|
||||
sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
'cookie_name': 'cookie_monster'}
|
||||
'cookie_name': 'cookie_monster',
|
||||
'pool_id': pool['id']}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool, sp_dict=sp)
|
||||
|
@ -224,7 +231,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE,
|
||||
'project_id': uuidutils.generate_uuid()}
|
||||
'project_id': uuidutils.generate_uuid(),
|
||||
'id': uuidutils.generate_uuid()}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool)
|
||||
|
@ -244,7 +252,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE,
|
||||
'project_id': uuidutils.generate_uuid()}
|
||||
'project_id': uuidutils.generate_uuid(),
|
||||
'id': uuidutils.generate_uuid()}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool)
|
||||
|
@ -258,9 +267,11 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'description': 'desc1',
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
'enabled': True, 'operating_status': constants.ONLINE,
|
||||
'project_id': uuidutils.generate_uuid()}
|
||||
'project_id': uuidutils.generate_uuid(),
|
||||
'id': uuidutils.generate_uuid()}
|
||||
sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
'cookie_name': 'cookie_monster'}
|
||||
'cookie_name': 'cookie_monster',
|
||||
'pool_id': pool['id']}
|
||||
pool_dm = self.repos.create_pool_on_listener(self.session,
|
||||
self.listener.id,
|
||||
pool, sp_dict=sp)
|
||||
|
|
Loading…
Reference in New Issue