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:
parent
1b992d1e12
commit
038cd180bf
|
@ -78,6 +78,34 @@ SUPPORTED_OPERATING_STATUSES = (ONLINE, OFFLINE, DEGRADED, ERROR, NO_MONITOR)
|
||||||
AMPHORA_VM = 'VM'
|
AMPHORA_VM = 'VM'
|
||||||
SUPPORTED_AMPHORA_TYPES = (AMPHORA_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
|
# Task/Flow constants
|
||||||
AMPHORA = 'amphora'
|
AMPHORA = 'amphora'
|
||||||
FAILED_AMPHORA = 'failed_amphora'
|
FAILED_AMPHORA = 'failed_amphora'
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright (c) 2014 Rackspace
|
# Copyright (c) 2014 Rackspace
|
||||||
|
# Copyright (c) 2016 Blue Box, an IBM Company
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -17,6 +18,8 @@ import re
|
||||||
|
|
||||||
from sqlalchemy.orm import collections
|
from sqlalchemy.orm import collections
|
||||||
|
|
||||||
|
from octavia.common import constants
|
||||||
|
|
||||||
|
|
||||||
class BaseDataModel(object):
|
class BaseDataModel(object):
|
||||||
|
|
||||||
|
@ -55,7 +58,8 @@ class BaseDataModel(object):
|
||||||
# First handle all objects with their own ID, then handle subordinate
|
# First handle all objects with their own ID, then handle subordinate
|
||||||
# objects.
|
# objects.
|
||||||
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
|
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
|
||||||
'Listener', 'Amphora']:
|
'Listener', 'Amphora', 'L7Policy',
|
||||||
|
'L7Rule']:
|
||||||
return obj.__class__.__name__ + obj.id
|
return obj.__class__.__name__ + obj.id
|
||||||
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
|
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
|
||||||
return obj.__class__.__name__ + obj.pool_id
|
return obj.__class__.__name__ + obj.pool_id
|
||||||
|
@ -169,7 +173,7 @@ class Pool(BaseDataModel):
|
||||||
protocol=None, lb_algorithm=None, enabled=None,
|
protocol=None, lb_algorithm=None, enabled=None,
|
||||||
operating_status=None, members=None, health_monitor=None,
|
operating_status=None, members=None, health_monitor=None,
|
||||||
session_persistence=None, load_balancer_id=None,
|
session_persistence=None, load_balancer_id=None,
|
||||||
load_balancer=None, listeners=None):
|
load_balancer=None, listeners=None, l7policies=None):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.project_id = project_id
|
self.project_id = project_id
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -184,6 +188,7 @@ class Pool(BaseDataModel):
|
||||||
self.health_monitor = health_monitor
|
self.health_monitor = health_monitor
|
||||||
self.session_persistence = session_persistence
|
self.session_persistence = session_persistence
|
||||||
self.listeners = listeners or []
|
self.listeners = listeners or []
|
||||||
|
self.l7policies = l7policies or []
|
||||||
|
|
||||||
def update(self, update_dict):
|
def update(self, update_dict):
|
||||||
for key, value in update_dict.items():
|
for key, value in update_dict.items():
|
||||||
|
@ -197,7 +202,6 @@ class Pool(BaseDataModel):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
# TODO(sbalukoff): Clean up L7Policies that reference this pool
|
|
||||||
for listener in self.listeners:
|
for listener in self.listeners:
|
||||||
if listener.default_pool_id == self.id:
|
if listener.default_pool_id == self.id:
|
||||||
listener.default_pool = None
|
listener.default_pool = None
|
||||||
|
@ -210,6 +214,11 @@ class Pool(BaseDataModel):
|
||||||
if pool.id == self.id:
|
if pool.id == self.id:
|
||||||
self.load_balancer.pools.remove(pool)
|
self.load_balancer.pools.remove(pool)
|
||||||
break
|
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):
|
class Member(BaseDataModel):
|
||||||
|
@ -243,7 +252,7 @@ class Listener(BaseDataModel):
|
||||||
enabled=None, provisioning_status=None, operating_status=None,
|
enabled=None, provisioning_status=None, operating_status=None,
|
||||||
tls_certificate_id=None, stats=None, default_pool=None,
|
tls_certificate_id=None, stats=None, default_pool=None,
|
||||||
load_balancer=None, sni_containers=None, peer_port=None,
|
load_balancer=None, sni_containers=None, peer_port=None,
|
||||||
pools=None):
|
l7policies=None, pools=None):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.project_id = project_id
|
self.project_id = project_id
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -262,12 +271,21 @@ class Listener(BaseDataModel):
|
||||||
self.load_balancer = load_balancer
|
self.load_balancer = load_balancer
|
||||||
self.sni_containers = sni_containers or []
|
self.sni_containers = sni_containers or []
|
||||||
self.peer_port = peer_port
|
self.peer_port = peer_port
|
||||||
|
self.l7policies = l7policies or []
|
||||||
self.pools = pools or []
|
self.pools = pools or []
|
||||||
|
|
||||||
def update(self, update_dict):
|
def update(self, update_dict):
|
||||||
for key, value in update_dict.items():
|
for key, value in update_dict.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
if key == 'default_pool_id':
|
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:
|
if value is not None:
|
||||||
pool = self._find_in_graph('Pool' + value)
|
pool = self._find_in_graph('Pool' + value)
|
||||||
if pool not in self.pools:
|
if pool not in self.pools:
|
||||||
|
@ -393,3 +411,126 @@ class AmphoraHealth(BaseDataModel):
|
||||||
self.amphora_id = amphora_id
|
self.amphora_id = amphora_id
|
||||||
self.last_update = last_update
|
self.last_update = last_update
|
||||||
self.busy = busy
|
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
|
||||||
|
|
|
@ -29,7 +29,8 @@ class OctaviaBase(models.ModelBase):
|
||||||
# First handle all objects with their own ID, then handle subordinate
|
# First handle all objects with their own ID, then handle subordinate
|
||||||
# objects.
|
# objects.
|
||||||
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
|
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
|
||||||
'Listener', 'Amphora']:
|
'Listener', 'Amphora', 'L7Policy',
|
||||||
|
'L7Rule']:
|
||||||
return obj.__class__.__name__ + obj.id
|
return obj.__class__.__name__ + obj.id
|
||||||
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
|
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
|
||||||
return obj.__class__.__name__ + obj.pool_id
|
return obj.__class__.__name__ + obj.pool_id
|
||||||
|
|
|
@ -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')
|
||||||
|
)
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright 2014 Rackspace
|
# Copyright 2014 Rackspace
|
||||||
|
# Copyright 2016 Blue Box, an IBM Company
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
|
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.ext import orderinglist
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
from sqlalchemy.orm import validates
|
from sqlalchemy.orm import validates
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
|
@ -67,6 +69,21 @@ class VRRPAuthMethod(base_models.BASE, base_models.LookupTableMixin):
|
||||||
__tablename__ = "vrrp_auth_method"
|
__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):
|
class SessionPersistence(base_models.BASE):
|
||||||
|
|
||||||
__data_model__ = data_models.SessionPersistence
|
__data_model__ = data_models.SessionPersistence
|
||||||
|
@ -215,12 +232,22 @@ class Pool(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin):
|
||||||
uselist=True,
|
uselist=True,
|
||||||
cascade="delete"))
|
cascade="delete"))
|
||||||
|
|
||||||
# Defining this as a custom method instead of an SQLAlchemy relationship
|
# This property should be a unique list of any listeners that reference
|
||||||
# for now. When L7 gets added, this list will also include any listeners
|
# this pool as its default_pool and any listeners referenced by enabled
|
||||||
# referenced by enabled L7policies
|
# 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
|
@property
|
||||||
def listeners(self):
|
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,
|
class LoadBalancer(base_models.BASE, base_models.IdMixin,
|
||||||
|
@ -338,22 +365,29 @@ class Listener(base_models.BASE, base_models.IdMixin,
|
||||||
backref=orm.backref("listeners",
|
backref=orm.backref("listeners",
|
||||||
uselist=True,
|
uselist=True,
|
||||||
cascade="delete"))
|
cascade="delete"))
|
||||||
# _default_listeners backref is used to generate part of pool.listeners
|
|
||||||
# list.
|
|
||||||
default_pool = orm.relationship("Pool", uselist=False,
|
default_pool = orm.relationship("Pool", uselist=False,
|
||||||
backref=orm.backref("_default_listeners",
|
backref=orm.backref("_default_listeners",
|
||||||
uselist=True))
|
uselist=True))
|
||||||
peer_port = sa.Column(sa.Integer(), nullable=True)
|
peer_port = sa.Column(sa.Integer(), nullable=True)
|
||||||
|
|
||||||
# Defining this as a custom method instead of an SQLAlchemy relationship
|
# This property should be a unique list of the default_pool and anything
|
||||||
# for now. When L7 gets added, this list will also include any pools
|
# referenced by enabled L7Policies with at least one rule that also
|
||||||
# referenced by enabled L7policies
|
# reference this listener. The intent is that listener.pools should be a
|
||||||
|
# unique list of pools this listener is *actually* using.
|
||||||
@property
|
@property
|
||||||
def pools(self):
|
def pools(self):
|
||||||
_pools = []
|
_pools = []
|
||||||
_p_ids = [p.id for p in _pools]
|
_p_ids = []
|
||||||
if self.default_pool and self.default_pool.id not in _p_ids:
|
if self.default_pool:
|
||||||
_pools.append(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
|
return _pools
|
||||||
|
|
||||||
|
|
||||||
|
@ -421,3 +455,75 @@ class AmphoraHealth(base_models.BASE):
|
||||||
nullable=False)
|
nullable=False)
|
||||||
|
|
||||||
busy = sa.Column(sa.Boolean(), default=False, 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))
|
||||||
|
|
|
@ -71,6 +71,13 @@ class OctaviaDBTestBase(test_base.DbTestCase):
|
||||||
models.LBTopology)
|
models.LBTopology)
|
||||||
self._seed_lookup_table(session, constants.SUPPORTED_VRRP_AUTH,
|
self._seed_lookup_table(session, constants.SUPPORTED_VRRP_AUTH,
|
||||||
models.VRRPAuthMethod)
|
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):
|
def _seed_lookup_table(self, session, name_list, model_cls):
|
||||||
for name in name_list:
|
for name in name_list:
|
||||||
|
|
|
@ -139,6 +139,24 @@ class ModelTestMixin(object):
|
||||||
kwargs.update(overrides)
|
kwargs.update(overrides)
|
||||||
return self._insert(session, models.AmphoraHealth, kwargs)
|
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):
|
class PoolModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
|
|
||||||
|
@ -195,12 +213,13 @@ class PoolModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
|
|
||||||
def test_listener_relationship(self):
|
def test_listener_relationship(self):
|
||||||
pool = self.create_pool(self.session)
|
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(
|
new_pool = self.session.query(models.Pool).filter_by(
|
||||||
id=pool.id).first()
|
id=pool.id).first()
|
||||||
self.assertIsNotNone(new_pool.listeners)
|
self.assertIsNotNone(new_pool.listeners)
|
||||||
self.assertIsInstance(new_pool.listeners, list)
|
self.assertIsInstance(new_pool.listeners, list)
|
||||||
self.assertIsInstance(new_pool.listeners[0], models.Listener)
|
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):
|
class MemberModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
|
@ -316,13 +335,15 @@ class ListenerModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
self.assertIsNotNone(new_listener.stats)
|
self.assertIsNotNone(new_listener.stats)
|
||||||
self.assertIsInstance(new_listener.stats, models.ListenerStatistics)
|
self.assertIsInstance(new_listener.stats, models.ListenerStatistics)
|
||||||
|
|
||||||
def test_pool_relationship(self):
|
def test_default_pool_relationship(self):
|
||||||
pool = self.create_pool(self.session)
|
pool = self.create_pool(self.session)
|
||||||
listener = self.create_listener(self.session, default_pool_id=pool.id)
|
listener = self.create_listener(self.session, default_pool_id=pool.id)
|
||||||
new_listener = self.session.query(models.Listener).filter_by(
|
new_listener = self.session.query(models.Listener).filter_by(
|
||||||
id=listener.id).first()
|
id=listener.id).first()
|
||||||
self.assertIsNotNone(new_listener.default_pool)
|
self.assertIsNotNone(new_listener.default_pool)
|
||||||
self.assertIsInstance(new_listener.default_pool, models.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):
|
def test_sni_relationship(self):
|
||||||
listener = self.create_listener(self.session)
|
listener = self.create_listener(self.session)
|
||||||
|
@ -335,6 +356,15 @@ class ListenerModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
self.assertIsNotNone(new_listener.sni_containers)
|
self.assertIsNotNone(new_listener.sni_containers)
|
||||||
self.assertEqual(2, len(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):
|
class ListenerStatisticsModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
|
|
||||||
|
@ -586,6 +616,169 @@ class AmphoraHealthModelTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
self.assertIsNone(new_amphora_health)
|
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):
|
class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -604,6 +797,12 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
self.stats = self.create_listener_statistics(self.session,
|
self.stats = self.create_listener_statistics(self.session,
|
||||||
self.listener.id)
|
self.listener.id)
|
||||||
self.sni = self.create_sni(self.session, listener_id=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
|
@staticmethod
|
||||||
def _get_unique_key(obj):
|
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
|
# First handle all objects with their own ID, then handle subordinate
|
||||||
# objects.
|
# objects.
|
||||||
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
|
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
|
||||||
'Listener', 'Amphora']:
|
'Listener', 'Amphora', 'L7Policy',
|
||||||
|
'L7Rule']:
|
||||||
return obj.__class__.__name__ + obj.id
|
return obj.__class__.__name__ + obj.id
|
||||||
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
|
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
|
||||||
return obj.__class__.__name__ + obj.pool_id
|
return obj.__class__.__name__ + obj.pool_id
|
||||||
|
@ -758,6 +958,16 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
id=self.member.id).first()
|
id=self.member.id).first()
|
||||||
self.check_member(member_db.to_data_model())
|
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,
|
def check_load_balancer(self, lb, check_listeners=True,
|
||||||
check_amphorae=True, check_vip=True,
|
check_amphorae=True, check_vip=True,
|
||||||
check_pools=True):
|
check_pools=True):
|
||||||
|
@ -804,7 +1014,8 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
self.check_load_balancer(amphora.load_balancer)
|
self.check_load_balancer(amphora.load_balancer)
|
||||||
|
|
||||||
def check_listener(self, listener, check_sni=True, check_pools=True,
|
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.assertIsInstance(listener, data_models.Listener)
|
||||||
self.check_listener_data_model(listener)
|
self.check_listener_data_model(listener)
|
||||||
if check_lb:
|
if check_lb:
|
||||||
|
@ -822,6 +1033,12 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
if check_statistics:
|
if check_statistics:
|
||||||
self.check_listener_statistics(listener.stats,
|
self.check_listener_statistics(listener.stats,
|
||||||
check_listener=False)
|
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):
|
def check_session_persistence(self, session_persistence, check_pool=True):
|
||||||
self.assertIsInstance(session_persistence,
|
self.assertIsInstance(session_persistence,
|
||||||
|
@ -836,6 +1053,31 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
if check_pool:
|
if check_pool:
|
||||||
self.check_pool(member.pool, check_members=False)
|
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):
|
def check_health_monitor(self, health_monitor, check_pool=True):
|
||||||
self.assertIsInstance(health_monitor, data_models.HealthMonitor)
|
self.assertIsInstance(health_monitor, data_models.HealthMonitor)
|
||||||
self.check_health_monitor_data_model(health_monitor)
|
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)
|
self.check_pool(health_monitor.pool, check_hm=False)
|
||||||
|
|
||||||
def check_pool(self, pool, check_listeners=True, check_sp=True,
|
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.assertIsInstance(pool, data_models.Pool)
|
||||||
self.check_pool_data_model(pool)
|
self.check_pool_data_model(pool)
|
||||||
if check_listeners:
|
if check_listeners:
|
||||||
|
@ -864,6 +1107,13 @@ class DataModelConversionTest(base.OctaviaDBTestBase, ModelTestMixin):
|
||||||
if check_lb:
|
if check_lb:
|
||||||
self.check_load_balancer(pool.load_balancer, check_pools=False,
|
self.check_load_balancer(pool.load_balancer, check_pools=False,
|
||||||
check_listeners=check_listeners)
|
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):
|
def check_load_balancer_data_model(self, lb):
|
||||||
self.assertEqual(self.FAKE_UUID_1, lb.project_id)
|
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.assertEqual(constants.ONLINE, member.operating_status)
|
||||||
self.assertTrue(member.enabled)
|
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):
|
def check_amphora_data_model(self, amphora):
|
||||||
self.assertEqual(self.FAKE_UUID_1, amphora.id)
|
self.assertEqual(self.FAKE_UUID_1, amphora.id)
|
||||||
self.assertEqual(self.FAKE_UUID_1, amphora.compute_id)
|
self.assertEqual(self.FAKE_UUID_1, amphora.compute_id)
|
||||||
|
|
|
@ -151,6 +151,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
||||||
del pool_dm_dict['listeners']
|
del pool_dm_dict['listeners']
|
||||||
del pool_dm_dict['load_balancer']
|
del pool_dm_dict['load_balancer']
|
||||||
del pool_dm_dict['load_balancer_id']
|
del pool_dm_dict['load_balancer_id']
|
||||||
|
del pool_dm_dict['l7policies']
|
||||||
self.assertEqual(pool, pool_dm_dict)
|
self.assertEqual(pool, pool_dm_dict)
|
||||||
new_listener = self.repos.listener.get(self.session,
|
new_listener = self.repos.listener.get(self.session,
|
||||||
id=self.listener.id)
|
id=self.listener.id)
|
||||||
|
@ -173,6 +174,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
||||||
del pool_dm_dict['listeners']
|
del pool_dm_dict['listeners']
|
||||||
del pool_dm_dict['load_balancer']
|
del pool_dm_dict['load_balancer']
|
||||||
del pool_dm_dict['load_balancer_id']
|
del pool_dm_dict['load_balancer_id']
|
||||||
|
del pool_dm_dict['l7policies']
|
||||||
self.assertEqual(pool, pool_dm_dict)
|
self.assertEqual(pool, pool_dm_dict)
|
||||||
sp_dm_dict = pool_dm.session_persistence.to_dict()
|
sp_dm_dict = pool_dm.session_persistence.to_dict()
|
||||||
del sp_dm_dict['pool']
|
del sp_dm_dict['pool']
|
||||||
|
@ -203,6 +205,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
||||||
del pool_dm_dict['listeners']
|
del pool_dm_dict['listeners']
|
||||||
del pool_dm_dict['load_balancer']
|
del pool_dm_dict['load_balancer']
|
||||||
del pool_dm_dict['load_balancer_id']
|
del pool_dm_dict['load_balancer_id']
|
||||||
|
del pool_dm_dict['l7policies']
|
||||||
pool.update(update_pool)
|
pool.update(update_pool)
|
||||||
self.assertEqual(pool, pool_dm_dict)
|
self.assertEqual(pool, pool_dm_dict)
|
||||||
self.assertIsNone(new_pool_dm.session_persistence)
|
self.assertIsNone(new_pool_dm.session_persistence)
|
||||||
|
@ -228,6 +231,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
||||||
del pool_dm_dict['listeners']
|
del pool_dm_dict['listeners']
|
||||||
del pool_dm_dict['load_balancer']
|
del pool_dm_dict['load_balancer']
|
||||||
del pool_dm_dict['load_balancer_id']
|
del pool_dm_dict['load_balancer_id']
|
||||||
|
del pool_dm_dict['l7policies']
|
||||||
pool.update(update_pool)
|
pool.update(update_pool)
|
||||||
self.assertEqual(pool, pool_dm_dict)
|
self.assertEqual(pool, pool_dm_dict)
|
||||||
sp_dm_dict = new_pool_dm.session_persistence.to_dict()
|
sp_dm_dict = new_pool_dm.session_persistence.to_dict()
|
||||||
|
|
Loading…
Reference in New Issue