Add L7 database structures

This commit adds the database structures necessary for adding
L7 functionality to Octavia. In the interest of limiting the size of
this commit, note that repository, API, jinja template, and
documentation updates will come in subsequent commits.

Partially-Implements: blueprint lbaas-l7-rules
Partially-Implements: blueprint layer-7-switching
Change-Id: I2ec4ea11dc7d8ff6ec78d79473f1ceb68e713b90
This commit is contained in:
Stephen Balukoff 2016-01-07 19:03:08 -08:00
parent 1b992d1e12
commit 038cd180bf
8 changed files with 724 additions and 21 deletions

View File

@ -78,6 +78,34 @@ SUPPORTED_OPERATING_STATUSES = (ONLINE, OFFLINE, DEGRADED, ERROR, NO_MONITOR)
AMPHORA_VM = 'VM'
SUPPORTED_AMPHORA_TYPES = (AMPHORA_VM,)
# L7 constants
L7RULE_TYPE_HOST_NAME = 'HOST_NAME'
L7RULE_TYPE_PATH = 'PATH'
L7RULE_TYPE_FILE_TYPE = 'FILE_TYPE'
L7RULE_TYPE_HEADER = 'HEADER'
L7RULE_TYPE_COOKIE = 'COOKIE'
SUPPORTED_L7RULE_TYPES = (L7RULE_TYPE_HOST_NAME, L7RULE_TYPE_PATH,
L7RULE_TYPE_FILE_TYPE, L7RULE_TYPE_HEADER,
L7RULE_TYPE_COOKIE)
L7RULE_COMPARE_TYPE_REGEX = 'REGEX'
L7RULE_COMPARE_TYPE_STARTS_WITH = 'STARTS_WITH'
L7RULE_COMPARE_TYPE_ENDS_WITH = 'ENDS_WITH'
L7RULE_COMPARE_TYPE_CONTAINS = 'CONTAINS'
L7RULE_COMPARE_TYPE_EQUAL_TO = 'EQUAL_TO'
SUPPORTED_L7RULE_COMPARE_TYPES = (L7RULE_COMPARE_TYPE_REGEX,
L7RULE_COMPARE_TYPE_STARTS_WITH,
L7RULE_COMPARE_TYPE_ENDS_WITH,
L7RULE_COMPARE_TYPE_CONTAINS,
L7RULE_COMPARE_TYPE_EQUAL_TO)
L7POLICY_ACTION_REJECT = 'REJECT'
L7POLICY_ACTION_REDIRECT_TO_URL = 'REDIRECT_TO_URL'
L7POLICY_ACTION_REDIRECT_TO_POOL = 'REDIRECT_TO_POOL'
SUPPORTED_L7POLICY_ACTIONS = (L7POLICY_ACTION_REJECT,
L7POLICY_ACTION_REDIRECT_TO_URL,
L7POLICY_ACTION_REDIRECT_TO_POOL)
# Task/Flow constants
AMPHORA = 'amphora'
FAILED_AMPHORA = 'failed_amphora'

View File

@ -1,4 +1,5 @@
# Copyright (c) 2014 Rackspace
# Copyright (c) 2016 Blue Box, an IBM Company
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -17,6 +18,8 @@ import re
from sqlalchemy.orm import collections
from octavia.common import constants
class BaseDataModel(object):
@ -55,7 +58,8 @@ class BaseDataModel(object):
# First handle all objects with their own ID, then handle subordinate
# objects.
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
'Listener', 'Amphora']:
'Listener', 'Amphora', 'L7Policy',
'L7Rule']:
return obj.__class__.__name__ + obj.id
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
return obj.__class__.__name__ + obj.pool_id
@ -169,7 +173,7 @@ class Pool(BaseDataModel):
protocol=None, lb_algorithm=None, enabled=None,
operating_status=None, members=None, health_monitor=None,
session_persistence=None, load_balancer_id=None,
load_balancer=None, listeners=None):
load_balancer=None, listeners=None, l7policies=None):
self.id = id
self.project_id = project_id
self.name = name
@ -184,6 +188,7 @@ class Pool(BaseDataModel):
self.health_monitor = health_monitor
self.session_persistence = session_persistence
self.listeners = listeners or []
self.l7policies = l7policies or []
def update(self, update_dict):
for key, value in update_dict.items():
@ -197,7 +202,6 @@ class Pool(BaseDataModel):
setattr(self, key, value)
def delete(self):
# TODO(sbalukoff): Clean up L7Policies that reference this pool
for listener in self.listeners:
if listener.default_pool_id == self.id:
listener.default_pool = None
@ -210,6 +214,11 @@ class Pool(BaseDataModel):
if pool.id == self.id:
self.load_balancer.pools.remove(pool)
break
for l7policy in self.l7policies:
if l7policy.redirect_pool_id == self.id:
l7policy.action = constants.L7POLICY_ACTION_REJECT
l7policy.redirect_pool = None
l7policy.redirect_pool_id = None
class Member(BaseDataModel):
@ -243,7 +252,7 @@ class Listener(BaseDataModel):
enabled=None, provisioning_status=None, operating_status=None,
tls_certificate_id=None, stats=None, default_pool=None,
load_balancer=None, sni_containers=None, peer_port=None,
pools=None):
l7policies=None, pools=None):
self.id = id
self.project_id = project_id
self.name = name
@ -262,12 +271,21 @@ class Listener(BaseDataModel):
self.load_balancer = load_balancer
self.sni_containers = sni_containers or []
self.peer_port = peer_port
self.l7policies = l7policies or []
self.pools = pools or []
def update(self, update_dict):
for key, value in update_dict.items():
setattr(self, key, value)
if key == 'default_pool_id':
if self.default_pool is not None:
l7_pool_ids = [p.redirect_pool_id for p in self.l7policies
if p.redirect_pool_id is not None and
len(p.l7rules) > 0 and p.enabled is True]
old_pool = self.default_pool
if old_pool.id not in l7_pool_ids:
self.pools.remove(old_pool)
old_pool.listeners.remove(self)
if value is not None:
pool = self._find_in_graph('Pool' + value)
if pool not in self.pools:
@ -393,3 +411,126 @@ class AmphoraHealth(BaseDataModel):
self.amphora_id = amphora_id
self.last_update = last_update
self.busy = busy
class L7Rule(BaseDataModel):
def __init__(self, id=None, l7policy_id=None, type=None,
compare_type=None, key=None, value=None, l7policy=None,
invert=False):
self.id = id
self.l7policy_id = l7policy_id
self.type = type
self.compare_type = compare_type
self.key = key
self.value = value
self.l7policy = l7policy
self.invert = invert
def delete(self):
if len(self.l7policy.l7rules) == 1:
# l7policy should disappear from pool and listener lists. Since
# we are operating only on the data model, we can fake this by
# calling the policy's delete method.
self.l7policy.delete()
for r in self.l7policy.l7rules:
if r.id == self.id:
self.l7policy.l7rules.remove(r)
break
class L7Policy(BaseDataModel):
def __init__(self, id=None, name=None, description=None, listener_id=None,
action=None, redirect_pool_id=None, redirect_url=None,
position=None, listener=None, redirect_pool=None,
enabled=None, l7rules=None):
self.id = id
self.name = name
self.description = description
self.listener_id = listener_id
self.action = action
self.redirect_pool_id = redirect_pool_id
self.redirect_url = redirect_url
self.position = position
self.listener = listener
self.redirect_pool = redirect_pool
self.enabled = enabled
self.l7rules = l7rules or []
def _conditionally_remove_pool_links(self, pool):
"""Removes links to the given pool from parent objects.
Note this only happens if our listener isn't referencing the pool
via its default_pool or another active l7policy's redirect_pool_id.
"""
if (self.listener.default_pool is not None and
pool is not None and
pool.id != self.listener.default_pool.id and
pool in self.listener.pools):
listener_l7pools = [
p.redirect_pool for p in self.listener.l7policies
if p.redirect_pool is not None and
len(p.l7rules) > 0 and p.enabled is True and
p.id != self.id]
if pool not in listener_l7pools:
self.listener.pools.remove(pool)
pool.listeners.remove(self.listener)
def update(self, update_dict):
for key, value in update_dict.items():
if key == 'redirect_pool_id':
self._conditionally_remove_pool_links(self.redirect_pool)
self.action = constants.L7POLICY_ACTION_REDIRECT_TO_POOL
self.redirect_url = None
pool = self._find_in_graph('Pool' + value)
self.redirect_pool = pool
if len(self.l7rules) > 0 and (self.enabled is True or
('enabled' in update_dict.keys()
and update_dict['enabled']
is True)):
if pool not in self.listener.pools:
self.listener.pools.append(pool)
if self.listener not in pool.listeners:
pool.listeners.append(self.listener)
elif key == 'redirect_url':
self.action = constants.L7POLICY_ACTION_REDIRECT_TO_URL
self._conditionally_remove_pool_links(self.redirect_pool)
self.redirect_pool = None
self.redirect_pool_id = None
elif key == 'action' and value == constants.L7POLICY_ACTION_REJECT:
self.redirect_url = None
self._conditionally_remove_pool_links(self.redirect_pool)
self.redirect_pool = None
self.redirect_pool_id = None
elif key == 'position':
self.listener.l7policies.remove(self)
self.listener.l7policies.insert(value - 1, self)
elif (key == 'enabled'
and (self.action ==
constants.L7POLICY_ACTION_REDIRECT_TO_POOL
or ('action' in update_dict.keys()
and update_dict['action'] ==
constants.L7POLICY_ACTION_REDIRECT_TO_POOL))
and (self.redirect_pool is not None
or ('redirect_pool_id' in update_dict.keys() and
self._find_in_graph(
'Pool' + update_dict['redirect_pool_id'])
is not None))):
if self.redirect_pool is None:
self.redirect_pool = self._find_in_graph(
'Pool' + update_dict['redirect_pool_id'])
self.listener.pools.append(self.redirect_pool)
self.redirect_pool.listeners.append(self.listener)
setattr(self, key, value)
def delete(self):
self._conditionally_remove_pool_links(self.redirect_pool)
if self.redirect_pool:
for p in self.redirect_pool.l7policies:
if p.id == self.id:
self.redirect_pool.l7policies.remove(p)
for p in self.listener.l7policies:
if p.id == self.id:
self.listener.l7policies.remove(p)
break

View File

@ -29,7 +29,8 @@ class OctaviaBase(models.ModelBase):
# First handle all objects with their own ID, then handle subordinate
# objects.
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
'Listener', 'Amphora']:
'Listener', 'Amphora', 'L7Policy',
'L7Rule']:
return obj.__class__.__name__ + obj.id
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
return obj.__class__.__name__ + obj.pool_id

View File

@ -0,0 +1,149 @@
# Copyright 2015 Blue Box, an IBM Company
#
# 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.
"""L7 Policies and Rules
Revision ID: 458c9ee2a011
Revises: 29ff921a6eb
Create Date: 2016-01-07 11:45:45.391851
"""
# revision identifiers, used by Alembic.
revision = '458c9ee2a011'
down_revision = '29ff921a6eb'
from alembic import op
import sqlalchemy as sa
from sqlalchemy import sql
def upgrade():
# L7 Rule Types
op.create_table(
u'l7rule_type',
sa.Column(u'name', sa.String(36), primary_key=True),
sa.Column(u'description', sa.String(255), nullable=True)
)
# Create temporary table for table data seeding
insert_table = sql.table(
u'l7rule_type',
sql.column(u'name', sa.String),
sql.column(u'description', sa.String)
)
op.bulk_insert(
insert_table,
[
{'name': 'HOST_NAME'},
{'name': 'PATH'},
{'name': 'FILE_TYPE'},
{'name': 'HEADER'},
{'name': 'COOKIE'}
]
)
# L7 Rule Compare Types
op.create_table(
u'l7rule_compare_type',
sa.Column(u'name', sa.String(36), primary_key=True),
sa.Column(u'description', sa.String(255), nullable=True)
)
insert_table = sql.table(
u'l7rule_compare_type',
sql.column(u'name', sa.String),
sql.column(u'description', sa.String)
)
op.bulk_insert(
insert_table,
[
{'name': 'REGEX'},
{'name': 'STARTS_WITH'},
{'name': 'ENDS_WITH'},
{'name': 'CONTAINS'},
{'name': 'EQUAL_TO'}
]
)
# L7 Policy Actions
op.create_table(
u'l7policy_action',
sa.Column(u'name', sa.String(36), primary_key=True),
sa.Column(u'description', sa.String(255), nullable=True)
)
insert_table = sql.table(
u'l7policy_action',
sql.column(u'name', sa.String),
sql.column(u'description', sa.String)
)
op.bulk_insert(
insert_table,
[
{'name': 'REJECT'},
{'name': 'REDIRECT_TO_URL'},
{'name': 'REDIRECT_TO_POOL'}
]
)
# L7 Policies
op.create_table(
u'l7policy',
sa.Column(u'id', sa.String(36), nullable=False),
sa.Column(u'name', sa.String(255), nullable=True),
sa.Column(u'description', sa.String(255), nullable=True),
sa.Column(u'listener_id', sa.String(36), nullable=False),
sa.Column(u'action', sa.String(36), nullable=False),
sa.Column(u'redirect_pool_id', sa.String(36), nullable=True),
sa.Column(u'redirect_url', sa.String(255), nullable=True),
sa.Column(u'position', sa.Integer, nullable=False),
sa.Column(u'enabled', sa.Boolean(), default=True, nullable=False),
sa.PrimaryKeyConstraint(u'id'),
sa.ForeignKeyConstraint([u'listener_id'],
[u'listener.id'],
name=u'fk_l7policy_listener_id'),
sa.ForeignKeyConstraint([u'redirect_pool_id'],
[u'pool.id'],
name=u'fk_l7policy_pool_id'),
sa.ForeignKeyConstraint([u'action'],
[u'l7policy_action.name'],
name=u'fk_l7policy_l7policy_action_name')
)
# L7 Rules
op.create_table(
u'l7rule',
sa.Column(u'id', sa.String(36), nullable=False),
sa.Column(u'l7policy_id', sa.String(36), nullable=False),
sa.Column(u'type', sa.String(36), nullable=False),
sa.Column(u'compare_type', sa.String(36), nullable=False),
sa.Column(u'key', sa.String(255), nullable=True),
sa.Column(u'value', sa.String(255), nullable=False),
sa.Column(u'invert', sa.Boolean(), default=False, nullable=False),
sa.PrimaryKeyConstraint(u'id'),
sa.ForeignKeyConstraint([u'l7policy_id'],
[u'l7policy.id'],
name=u'fk_l7rule_l7policy_id'),
sa.ForeignKeyConstraint([u'type'],
[u'l7rule_type.name'],
name=u'fk_l7rule_l7rule_type_name'),
sa.ForeignKeyConstraint([u'compare_type'],
[u'l7rule_compare_type.name'],
name=u'fk_l7rule_l7rule_compare_type_name')
)

View File

@ -1,4 +1,5 @@
# Copyright 2014 Rackspace
# Copyright 2016 Blue Box, an IBM Company
#
# 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
@ -14,6 +15,7 @@
import sqlalchemy as sa
from sqlalchemy.ext import orderinglist
from sqlalchemy import orm
from sqlalchemy.orm import validates
from sqlalchemy.sql import func
@ -67,6 +69,21 @@ class VRRPAuthMethod(base_models.BASE, base_models.LookupTableMixin):
__tablename__ = "vrrp_auth_method"
class L7RuleType(base_models.BASE, base_models.LookupTableMixin):
__tablename__ = "l7rule_type"
class L7RuleCompareType(base_models.BASE, base_models.LookupTableMixin):
__tablename__ = "l7rule_compare_type"
class L7PolicyAction(base_models.BASE, base_models.LookupTableMixin):
__tablename__ = "l7policy_action"
class SessionPersistence(base_models.BASE):
__data_model__ = data_models.SessionPersistence
@ -215,12 +232,22 @@ class Pool(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin):
uselist=True,
cascade="delete"))
# Defining this as a custom method instead of an SQLAlchemy relationship
# for now. When L7 gets added, this list will also include any listeners
# referenced by enabled L7policies
# This property should be a unique list of any listeners that reference
# this pool as its default_pool and any listeners referenced by enabled
# L7Policies with at least one l7rule which also reference this pool. The
# intent is that pool.listeners should be a unique list of listeners
# *actually* using the pool.
@property
def listeners(self):
return self._default_listeners
_listeners = self._default_listeners[:]
_l_ids = [l.id for l in _listeners]
l7_listeners = [p.listener for p in self.l7policies
if len(p.l7rules) > 0 and p.enabled is True]
for l in l7_listeners:
if l.id not in _l_ids:
_listeners.append(l)
_l_ids.append(l.id)
return _listeners
class LoadBalancer(base_models.BASE, base_models.IdMixin,
@ -338,22 +365,29 @@ class Listener(base_models.BASE, base_models.IdMixin,
backref=orm.backref("listeners",
uselist=True,
cascade="delete"))
# _default_listeners backref is used to generate part of pool.listeners
# list.
default_pool = orm.relationship("Pool", uselist=False,
backref=orm.backref("_default_listeners",
uselist=True))
peer_port = sa.Column(sa.Integer(), nullable=True)
# Defining this as a custom method instead of an SQLAlchemy relationship
# for now. When L7 gets added, this list will also include any pools
# referenced by enabled L7policies
# This property should be a unique list of the default_pool and anything
# referenced by enabled L7Policies with at least one rule that also
# reference this listener. The intent is that listener.pools should be a
# unique list of pools this listener is *actually* using.
@property
def pools(self):
_pools = []
_p_ids = [p.id for p in _pools]
if self.default_pool and self.default_pool.id not in _p_ids:
_p_ids = []
if self.default_pool:
_pools.append(self.default_pool)
_p_ids.append(self.default_pool.id)
l7_pools = [p.redirect_pool for p in self.l7policies
if p.redirect_pool is not None and len(p.l7rules) > 0 and
p.enabled is True]
for p in l7_pools:
if p.id not in _p_ids:
_pools.append(p)
_p_ids.append(p.id)
return _pools
@ -421,3 +455,75 @@ class AmphoraHealth(base_models.BASE):
nullable=False)
busy = sa.Column(sa.Boolean(), default=False, nullable=False)
class L7Rule(base_models.BASE, base_models.IdMixin):
__data_model__ = data_models.L7Rule
__tablename__ = "l7rule"
l7policy_id = sa.Column(
sa.String(36),
sa.ForeignKey("l7policy.id", name="fk_l7rule_l7policy_id"),
nullable=False)
type = sa.Column(
sa.String(36),
sa.ForeignKey(
"l7rule_type.name",
name="fk_l7rule_l7rule_type_name"),
nullable=False)
compare_type = sa.Column(
sa.String(36),
sa.ForeignKey(
"l7rule_compare_type.name",
name="fk_l7rule_l7rule_compare_type_name"),
nullable=False)
key = sa.Column(sa.String(255), nullable=True)
value = sa.Column(sa.String(255), nullable=False)
invert = sa.Column(sa.Boolean(), default=False, nullable=False)
l7policy = orm.relationship("L7Policy", uselist=False,
backref=orm.backref("l7rules",
uselist=True,
cascade="delete"))
class L7Policy(base_models.BASE, base_models.IdMixin):
__data_model__ = data_models.L7Policy
__tablename__ = "l7policy"
name = sa.Column(sa.String(255), nullable=True)
description = sa.Column(sa.String(255), nullable=True)
listener_id = sa.Column(
sa.String(36),
sa.ForeignKey("listener.id", name="fk_l7policy_listener_id"),
nullable=False)
action = sa.Column(
sa.String(36),
sa.ForeignKey(
"l7policy_action.name",
name="fk_l7policy_l7policy_action_name"),
nullable=False)
redirect_pool_id = sa.Column(
sa.String(36),
sa.ForeignKey("pool.id", name="fk_l7policy_pool_id"),
nullable=True)
redirect_url = sa.Column(
sa.String(255),
nullable=True)
position = sa.Column(sa.Integer, nullable=False)
enabled = sa.Column(sa.Boolean(), nullable=False)
listener = orm.relationship(
"Listener", uselist=False,
backref=orm.backref(
"l7policies",
uselist=True,
order_by="L7Policy.position",
collection_class=orderinglist.ordering_list('position',
count_from=1),
cascade="delete"))
redirect_pool = orm.relationship("Pool", uselist=False,
backref=orm.backref("l7policies",
uselist=True))

View File

@ -71,6 +71,13 @@ class OctaviaDBTestBase(test_base.DbTestCase):
models.LBTopology)
self._seed_lookup_table(session, constants.SUPPORTED_VRRP_AUTH,
models.VRRPAuthMethod)
self._seed_lookup_table(session, constants.SUPPORTED_L7RULE_TYPES,
models.L7RuleType)
self._seed_lookup_table(session,
constants.SUPPORTED_L7RULE_COMPARE_TYPES,
models.L7RuleCompareType)
self._seed_lookup_table(session, constants.SUPPORTED_L7POLICY_ACTIONS,
models.L7PolicyAction)
def _seed_lookup_table(self, session, name_list, model_cls):
for name in name_list:

View File

@ -139,6 +139,24 @@ class ModelTestMixin(object):
kwargs.update(overrides)
return self._insert(session, models.AmphoraHealth, kwargs)
def create_l7policy(self, session, listener_id, **overrides):
kwargs = {'id': self.FAKE_UUID_1,
'listener_id': listener_id,
'action': constants.L7POLICY_ACTION_REJECT,
'position': 0,
'enabled': True}
kwargs.update(overrides)
return self._insert(session, models.L7Policy, kwargs)
def create_l7rule(self, session, l7policy_id, **overrides):
kwargs = {'id': self.FAKE_UUID_1,
'l7policy_id': l7policy_id,
'type': constants.L7RULE_TYPE_PATH,
'compare_type': constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
'value': '/api'}
kwargs.update(overrides)
return self._insert(session, models.L7Rule, kwargs)
class PoolModelTest(base.OctaviaDBTestBase, ModelTestMixin):
@ -195,12 +213,13 @@ class PoolModelTest(base.OctaviaDBTestBase, ModelTestMixin):
def test_listener_relationship(self):
pool = self.create_pool(self.session)
self.create_listener(self.session, default_pool_id=pool.id)
listener = self.create_listener(self.session, default_pool_id=pool.id)
new_pool = self.session.query(models.Pool).filter_by(
id=pool.id).first()
self.assertIsNotNone(new_pool.listeners)
self.assertIsInstance(new_pool.listeners, list)
self.assertIsInstance(new_pool.listeners[0], models.Listener)
self.assertIn(listener.id, [l.id for l in new_pool.listeners])
class MemberModelTest(base.OctaviaDBTestBase, ModelTestMixin):
@ -316,13 +335,15 @@ class ListenerModelTest(base.OctaviaDBTestBase, ModelTestMixin):
self.assertIsNotNone(new_listener.stats)
self.assertIsInstance(new_listener.stats, models.ListenerStatistics)
def test_pool_relationship(self):
def test_default_pool_relationship(self):
pool = self.create_pool(self.session)
listener = self.create_listener(self.session, default_pool_id=pool.id)
new_listener = self.session.query(models.Listener).filter_by(
id=listener.id).first()
self.assertIsNotNone(new_listener.default_pool)
self.assertIsInstance(new_listener.default_pool, models.Pool)
self.assertIsInstance(new_listener.pools, list)
self.assertIn(pool.id, [p.id for p in new_listener.pools])
def test_sni_relationship(self):
listener = self.create_listener(self.session)
@ -335,6 +356,15 @@ class ListenerModelTest(base.OctaviaDBTestBase, ModelTestMixin):
self.assertIsNotNone(new_listener.sni_containers)
self.assertEqual(2, len(new_listener.sni_containers))
def test_pools_list(self):
pool = self.create_pool(self.session)
listener = self.create_listener(self.session, default_pool_id=pool.id)
new_listener = self.session.query(models.Listener).filter_by(
id=listener.id).first()
self.assertIsNotNone(new_listener.pools)
self.assertIsInstance(new_listener.pools, list)
self.assertIsInstance(new_listener.pools[0], models.Pool)
class ListenerStatisticsModelTest(base.OctaviaDBTestBase, ModelTestMixin):
@ -586,6 +616,169 @@ class AmphoraHealthModelTest(base.OctaviaDBTestBase, ModelTestMixin):
self.assertIsNone(new_amphora_health)
class L7PolicyModelTest(base.OctaviaDBTestBase, ModelTestMixin):
def setUp(self):
super(L7PolicyModelTest, self).setUp()
self.listener = self.create_listener(self.session)
def test_create(self):
l7policy = self.create_l7policy(self.session, self.listener.id)
self.assertIsInstance(l7policy, models.L7Policy)
def test_update(self):
l7policy = self.create_l7policy(self.session, self.listener.id)
pool = self.create_pool(self.session)
l7policy.action = constants.L7POLICY_ACTION_REDIRECT_TO_POOL
l7policy.redirect_pool_id = pool.id
new_l7policy = self.session.query(
models.L7Policy).filter_by(id=l7policy.id).first()
self.assertEqual(pool.id, new_l7policy.redirect_pool_id)
self.assertEqual(constants.L7POLICY_ACTION_REDIRECT_TO_POOL,
new_l7policy.action)
def test_delete(self):
l7policy = self.create_l7policy(self.session, self.listener.id)
l7policy_id = l7policy.id
with self.session.begin():
self.session.delete(l7policy)
self.session.flush()
new_l7policy = self.session.query(
models.L7Policy).filter_by(id=l7policy_id).first()
self.assertIsNone(new_l7policy)
def test_l7rule_relationship(self):
l7policy = self.create_l7policy(self.session, self.listener.id)
self.create_l7rule(
self.session, l7policy.id, id=self.FAKE_UUID_1,
type=constants.L7RULE_TYPE_HOST_NAME,
compare_type=constants.L7RULE_COMPARE_TYPE_EQUAL_TO,
value='www.example.com')
self.create_l7rule(
self.session, l7policy.id, id=self.FAKE_UUID_2,
type=constants.L7RULE_TYPE_PATH,
compare_type=constants.L7RULE_COMPARE_TYPE_EQUAL_TO,
value='/api')
new_l7policy = self.session.query(
models.L7Policy).filter_by(id=l7policy.id).first()
self.assertIsNotNone(new_l7policy.l7rules)
self.assertEqual(2, len(new_l7policy.l7rules))
self.assertIsInstance(new_l7policy.l7rules[0], models.L7Rule)
self.assertIsInstance(new_l7policy.l7rules[1], models.L7Rule)
def test_pool_relationship(self):
l7policy = self.create_l7policy(self.session, self.listener.id)
self.create_pool(self.session, id=self.FAKE_UUID_2)
l7policy.action = constants.L7POLICY_ACTION_REDIRECT_TO_POOL
l7policy.redirect_pool_id = self.FAKE_UUID_2
new_l7policy = self.session.query(
models.L7Policy).filter_by(id=l7policy.id).first()
self.assertIsNotNone(new_l7policy.redirect_pool)
self.assertIsInstance(new_l7policy.redirect_pool, models.Pool)
def test_listener_relationship(self):
l7policy = self.create_l7policy(self.session, self.listener.id,
id=self.FAKE_UUID_1)
self.create_l7policy(self.session, self.listener.id,
id=self.FAKE_UUID_2, position=1)
new_l7policy = self.session.query(models.L7Policy).filter_by(
id=l7policy.id).first()
self.assertIsNotNone(new_l7policy.listener)
self.assertIsInstance(new_l7policy.listener, models.Listener)
def test_listeners_pools_refs_with_l7policy_with_l7rule(self):
pool = self.create_pool(self.session, id=self.FAKE_UUID_2)
l7policy = self.create_l7policy(
self.session, self.listener.id,
action=constants.L7POLICY_ACTION_REDIRECT_TO_POOL,
redirect_pool_id=pool.id)
self.create_l7rule(self.session, l7policy.id, id=self.FAKE_UUID_1)
new_pool = self.session.query(models.Pool).filter_by(
id=pool.id).first()
new_listener = self.session.query(models.Listener).filter_by(
id=self.listener.id).first()
self.assertIsInstance(new_pool.listeners, list)
self.assertIn(new_listener.id, [l.id for l in new_pool.listeners])
self.assertIsInstance(new_listener.pools, list)
self.assertIn(new_pool.id, [p.id for p in new_listener.pools])
def test_listeners_pools_refs_with_l7policy_without_l7rule(self):
pool = self.create_pool(self.session, id=self.FAKE_UUID_2)
self.create_l7policy(
self.session, self.listener.id,
action=constants.L7POLICY_ACTION_REDIRECT_TO_POOL,
redirect_pool_id=pool.id)
new_pool = self.session.query(models.Pool).filter_by(
id=pool.id).first()
new_listener = self.session.query(models.Listener).filter_by(
id=self.listener.id).first()
self.assertIsInstance(new_pool.listeners, list)
self.assertNotIn(new_listener.id, [l.id for l in new_pool.listeners])
self.assertIsInstance(new_listener.pools, list)
self.assertNotIn(new_pool.id, [p.id for p in new_listener.pools])
def test_listeners_pools_refs_with_disabled_l7policy(self):
pool = self.create_pool(self.session, id=self.FAKE_UUID_2)
l7policy = self.create_l7policy(
self.session, self.listener.id,
action=constants.L7POLICY_ACTION_REDIRECT_TO_POOL,
redirect_pool_id=pool.id, enabled=False)
self.create_l7rule(self.session, l7policy.id, id=self.FAKE_UUID_1)
new_pool = self.session.query(models.Pool).filter_by(
id=pool.id).first()
new_listener = self.session.query(models.Listener).filter_by(
id=self.listener.id).first()
self.assertIsInstance(new_pool.listeners, list)
self.assertNotIn(new_listener.id, [l.id for l in new_pool.listeners])
self.assertIsInstance(new_listener.pools, list)
self.assertNotIn(new_pool.id, [p.id for p in new_listener.pools])
class L7RuleModelTest(base.OctaviaDBTestBase, ModelTestMixin):
def setUp(self):
super(L7RuleModelTest, self).setUp()
self.listener = self.create_listener(self.session)
self.l7policy = self.create_l7policy(self.session, self.listener.id)
def test_create(self):
l7rule = self.create_l7rule(self.session, self.l7policy.id)
self.assertIsInstance(l7rule, models.L7Rule)
def test_update(self):
l7rule = self.create_l7rule(self.session, self.l7policy.id)
l7rule_id = l7rule.id
l7rule.value = '/images'
new_l7rule = self.session.query(
models.L7Rule).filter_by(id=l7rule_id).first()
self.assertEqual('/images', new_l7rule.value)
def test_delete(self):
l7rule = self.create_l7rule(self.session, self.l7policy.id)
l7rule_id = l7rule.id
with self.session.begin():
self.session.delete(l7rule)
self.session.flush()
new_l7rule = self.session.query(
models.L7Rule).filter_by(id=l7rule_id).first()
self.assertIsNone(new_l7rule)
def test_l7policy_relationship(self):
l7rule = self.create_l7rule(
self.session, self.l7policy.id, id=self.FAKE_UUID_1,
type=constants.L7RULE_TYPE_HOST_NAME,
compare_type=constants.L7RULE_COMPARE_TYPE_EQUAL_TO,
value='www.example.com')
self.create_l7rule(
self.session, self.l7policy.id, id=self.FAKE_UUID_2,
type=constants.L7RULE_TYPE_PATH,
compare_type=constants.L7RULE_COMPARE_TYPE_EQUAL_TO,
value='/api')
new_l7rule = self.session.query(models.L7Rule).filter_by(
id=l7rule.id).first()
self.assertIsNotNone(new_l7rule.l7policy)
self.assertIsInstance(new_l7rule.l7policy, models.L7Policy)
class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
def setUp(self):
@ -604,6 +797,12 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
self.stats = self.create_listener_statistics(self.session,
self.listener.id)
self.sni = self.create_sni(self.session, listener_id=self.listener.id)
self.l7policy = self.create_l7policy(
self.session, listener_id=self.listener.id,
action=constants.L7POLICY_ACTION_REDIRECT_TO_POOL,
redirect_pool_id=self.pool.id)
self.l7rule = self.create_l7rule(self.session,
l7policy_id=self.l7policy.id)
@staticmethod
def _get_unique_key(obj):
@ -611,7 +810,8 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
# First handle all objects with their own ID, then handle subordinate
# objects.
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
'Listener', 'Amphora']:
'Listener', 'Amphora', 'L7Policy',
'L7Rule']:
return obj.__class__.__name__ + obj.id
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
return obj.__class__.__name__ + obj.pool_id
@ -758,6 +958,16 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
id=self.member.id).first()
self.check_member(member_db.to_data_model())
def test_l7policy_tree(self):
l7policy_db = self.session.query(models.L7Policy).filter_by(
id=self.l7policy.id).first()
self.check_l7policy(l7policy_db.to_data_model())
def test_l7rule_tree(self):
l7rule_db = self.session.query(models.L7Rule).filter_by(
id=self.l7rule.id).first()
self.check_l7rule(l7rule_db.to_data_model())
def check_load_balancer(self, lb, check_listeners=True,
check_amphorae=True, check_vip=True,
check_pools=True):
@ -804,7 +1014,8 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
self.check_load_balancer(amphora.load_balancer)
def check_listener(self, listener, check_sni=True, check_pools=True,
check_lb=True, check_statistics=True):
check_lb=True, check_statistics=True,
check_l7policies=True):
self.assertIsInstance(listener, data_models.Listener)
self.check_listener_data_model(listener)
if check_lb:
@ -822,6 +1033,12 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
if check_statistics:
self.check_listener_statistics(listener.stats,
check_listener=False)
if check_l7policies:
c_l7policies = listener.l7policies
self.assertIsInstance(c_l7policies, list)
for policy in c_l7policies:
self.check_l7policy(policy, check_listener=False,
check_pool=check_pools, check_lb=check_lb)
def check_session_persistence(self, session_persistence, check_pool=True):
self.assertIsInstance(session_persistence,
@ -836,6 +1053,31 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
if check_pool:
self.check_pool(member.pool, check_members=False)
def check_l7policy(self, l7policy, check_listener=True, check_pool=True,
check_l7rules=True, check_lb=True):
self.assertIsInstance(l7policy, data_models.L7Policy)
self.check_l7policy_data_model(l7policy)
if check_listener:
self.check_listener(l7policy.listener, check_l7policies=False,
check_pools=check_pool, check_lb=check_lb)
if check_l7rules:
c_l7rules = l7policy.l7rules
self.assertIsInstance(c_l7rules, list)
for rule in c_l7rules:
self.check_l7rule(rule, check_l7policy=False)
if check_pool and l7policy.redirect_pool is not None:
self.assertEqual(l7policy.action,
constants.L7POLICY_ACTION_REDIRECT_TO_POOL)
self.check_pool(l7policy.redirect_pool,
check_listeners=check_listener,
check_l7policies=False, check_lb=check_lb)
def check_l7rule(self, l7rule, check_l7policy=True):
self.assertIsInstance(l7rule, data_models.L7Rule)
self.check_l7rule_data_model(l7rule)
if check_l7policy:
self.check_l7policy(l7rule.l7policy)
def check_health_monitor(self, health_monitor, check_pool=True):
self.assertIsInstance(health_monitor, data_models.HealthMonitor)
self.check_health_monitor_data_model(health_monitor)
@ -843,7 +1085,8 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
self.check_pool(health_monitor.pool, check_hm=False)
def check_pool(self, pool, check_listeners=True, check_sp=True,
check_hm=True, check_members=True, check_lb=True):
check_hm=True, check_members=True, check_l7policies=True,
check_lb=True):
self.assertIsInstance(pool, data_models.Pool)
self.check_pool_data_model(pool)
if check_listeners:
@ -864,6 +1107,13 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
if check_lb:
self.check_load_balancer(pool.load_balancer, check_pools=False,
check_listeners=check_listeners)
if check_l7policies:
c_l7policies = pool.l7policies
self.assertIsInstance(c_l7policies, list)
for policy in c_l7policies:
self.check_l7policy(policy, check_pool=False,
check_listener=check_listeners,
check_lb=check_lb)
def check_load_balancer_data_model(self, lb):
self.assertEqual(self.FAKE_UUID_1, lb.project_id)
@ -924,6 +1174,23 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
self.assertEqual(constants.ONLINE, member.operating_status)
self.assertTrue(member.enabled)
def check_l7policy_data_model(self, l7policy):
self.assertEqual(self.FAKE_UUID_1, l7policy.id)
self.assertEqual(self.listener.id, l7policy.listener_id)
self.assertEqual(constants.L7POLICY_ACTION_REDIRECT_TO_POOL,
l7policy.action)
self.assertEqual(self.pool.id, l7policy.redirect_pool_id)
self.assertEqual(0, l7policy.position)
def check_l7rule_data_model(self, l7rule):
self.assertEqual(self.FAKE_UUID_1, l7rule.id)
self.assertEqual(self.l7policy.id, l7rule.l7policy_id)
self.assertEqual(constants.L7RULE_TYPE_PATH, l7rule.type)
self.assertEqual(constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
l7rule.compare_type)
self.assertEqual('/api', l7rule.value)
self.assertFalse(l7rule.invert)
def check_amphora_data_model(self, amphora):
self.assertEqual(self.FAKE_UUID_1, amphora.id)
self.assertEqual(self.FAKE_UUID_1, amphora.compute_id)

View File

@ -151,6 +151,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
del pool_dm_dict['listeners']
del pool_dm_dict['load_balancer']
del pool_dm_dict['load_balancer_id']
del pool_dm_dict['l7policies']
self.assertEqual(pool, pool_dm_dict)
new_listener = self.repos.listener.get(self.session,
id=self.listener.id)
@ -173,6 +174,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
del pool_dm_dict['listeners']
del pool_dm_dict['load_balancer']
del pool_dm_dict['load_balancer_id']
del pool_dm_dict['l7policies']
self.assertEqual(pool, pool_dm_dict)
sp_dm_dict = pool_dm.session_persistence.to_dict()
del sp_dm_dict['pool']
@ -203,6 +205,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
del pool_dm_dict['listeners']
del pool_dm_dict['load_balancer']
del pool_dm_dict['load_balancer_id']
del pool_dm_dict['l7policies']
pool.update(update_pool)
self.assertEqual(pool, pool_dm_dict)
self.assertIsNone(new_pool_dm.session_persistence)
@ -228,6 +231,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
del pool_dm_dict['listeners']
del pool_dm_dict['load_balancer']
del pool_dm_dict['load_balancer_id']
del pool_dm_dict['l7policies']
pool.update(update_pool)
self.assertEqual(pool, pool_dm_dict)
sp_dm_dict = new_pool_dm.session_persistence.to_dict()