00f43c2b63
Since py2 is no longer supported, built in methods can replace the six package usage, as been done in the neutron project Change-Id: I922963fbbcc0ab263e1f6e56907b73b007015a75
532 lines
20 KiB
Python
532 lines
20 KiB
Python
# Copyright 2019 VMware, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from neutron.db.models import servicetype as st_db
|
|
from neutron.db import models_v2
|
|
from neutron_lib.db import constants as db_const
|
|
from neutron_lib.db import model_base
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.ext import orderinglist
|
|
from sqlalchemy import orm
|
|
|
|
from vmware_nsx.tests.unit.services.lbaas import lb_constants as lb_const
|
|
|
|
|
|
class SessionPersistenceV2(model_base.BASEV2):
|
|
|
|
NAME = 'session_persistence'
|
|
|
|
__tablename__ = "lbaas_sessionpersistences"
|
|
|
|
pool_id = sa.Column(sa.String(36),
|
|
sa.ForeignKey("lbaas_pools.id"),
|
|
primary_key=True,
|
|
nullable=False)
|
|
type = sa.Column(sa.Enum(*lb_const.SUPPORTED_SP_TYPES,
|
|
name="lbaas_sesssionpersistences_typev2"),
|
|
nullable=False)
|
|
cookie_name = sa.Column(sa.String(1024), nullable=True)
|
|
|
|
|
|
class LoadBalancerStatistics(model_base.BASEV2):
|
|
"""Represents load balancer statistics."""
|
|
|
|
NAME = 'loadbalancer_stats'
|
|
|
|
__tablename__ = "lbaas_loadbalancer_statistics"
|
|
|
|
loadbalancer_id = sa.Column(sa.String(36),
|
|
sa.ForeignKey("lbaas_loadbalancers.id"),
|
|
primary_key=True,
|
|
nullable=False)
|
|
bytes_in = sa.Column(sa.BigInteger, nullable=False)
|
|
bytes_out = sa.Column(sa.BigInteger, nullable=False)
|
|
active_connections = sa.Column(sa.BigInteger, nullable=False)
|
|
total_connections = sa.Column(sa.BigInteger, nullable=False)
|
|
|
|
@orm.validates('bytes_in', 'bytes_out',
|
|
'active_connections', 'total_connections')
|
|
def validate_non_negative_int(self, key, value):
|
|
if value < 0:
|
|
data = {'key': key, 'value': value}
|
|
raise ValueError('The %(key)s field can not have '
|
|
'negative value. '
|
|
'Current value is %(value)d.' % data)
|
|
return value
|
|
|
|
|
|
class MemberV2(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
|
"""Represents a v2 neutron load balancer member."""
|
|
|
|
NAME = 'member'
|
|
|
|
__tablename__ = "lbaas_members"
|
|
|
|
__table_args__ = (
|
|
sa.schema.UniqueConstraint('pool_id', 'address', 'protocol_port',
|
|
name='uniq_pool_address_port_v2'),
|
|
)
|
|
pool_id = sa.Column(sa.String(36), sa.ForeignKey("lbaas_pools.id"),
|
|
nullable=False)
|
|
address = sa.Column(sa.String(64), nullable=False)
|
|
protocol_port = sa.Column(sa.Integer, nullable=False)
|
|
weight = sa.Column(sa.Integer, nullable=True)
|
|
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
|
subnet_id = sa.Column(sa.String(36), nullable=True)
|
|
provisioning_status = sa.Column(sa.String(16), nullable=False)
|
|
operating_status = sa.Column(sa.String(16), nullable=False)
|
|
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE), nullable=True)
|
|
|
|
@property
|
|
def root_loadbalancer(self):
|
|
return self.pool.loadbalancer
|
|
|
|
@property
|
|
def to_api_dict(self):
|
|
def to_dict(sa_model, attributes):
|
|
ret = {}
|
|
for attr in attributes:
|
|
value = getattr(sa_model, attr)
|
|
ret[attr] = value
|
|
return ret
|
|
|
|
ret_dict = to_dict(self, [
|
|
'id', 'tenant_id', 'pool_id', 'address', 'protocol_port', 'weight',
|
|
'admin_state_up', 'subnet_id', 'name'])
|
|
|
|
return ret_dict
|
|
|
|
|
|
class HealthMonitorV2(model_base.BASEV2, model_base.HasId,
|
|
model_base.HasProject):
|
|
"""Represents a v2 neutron load balancer healthmonitor."""
|
|
|
|
NAME = 'healthmonitor'
|
|
|
|
__tablename__ = "lbaas_healthmonitors"
|
|
|
|
type = sa.Column(sa.Enum(*lb_const.SUPPORTED_HEALTH_MONITOR_TYPES,
|
|
name="healthmonitors_typev2"),
|
|
nullable=False)
|
|
delay = sa.Column(sa.Integer, nullable=False)
|
|
timeout = sa.Column(sa.Integer, nullable=False)
|
|
max_retries = sa.Column(sa.Integer, nullable=False)
|
|
http_method = sa.Column(sa.String(16), nullable=True)
|
|
url_path = sa.Column(sa.String(255), nullable=True)
|
|
expected_codes = sa.Column(sa.String(64), nullable=True)
|
|
provisioning_status = sa.Column(sa.String(16), nullable=False)
|
|
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
|
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE), nullable=True)
|
|
max_retries_down = sa.Column(sa.Integer, nullable=True)
|
|
|
|
@property
|
|
def root_loadbalancer(self):
|
|
return self.pool.loadbalancer
|
|
|
|
@property
|
|
def to_api_dict(self):
|
|
def to_dict(sa_model, attributes):
|
|
ret = {}
|
|
for attr in attributes:
|
|
value = getattr(sa_model, attr)
|
|
ret[attr] = value
|
|
return ret
|
|
|
|
ret_dict = to_dict(self, [
|
|
'id', 'tenant_id', 'type', 'delay', 'timeout', 'max_retries',
|
|
'http_method', 'url_path', 'expected_codes', 'admin_state_up',
|
|
'name', 'max_retries_down'])
|
|
|
|
ret_dict['pools'] = []
|
|
if self.pool:
|
|
ret_dict['pools'].append({'id': self.pool.id})
|
|
if self.type in [lb_const.HEALTH_MONITOR_TCP,
|
|
lb_const.HEALTH_MONITOR_PING]:
|
|
ret_dict.pop('http_method')
|
|
ret_dict.pop('url_path')
|
|
ret_dict.pop('expected_codes')
|
|
|
|
return ret_dict
|
|
|
|
|
|
class LoadBalancer(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
|
"""Represents a v2 neutron load balancer."""
|
|
|
|
NAME = 'loadbalancer'
|
|
|
|
__tablename__ = "lbaas_loadbalancers"
|
|
|
|
name = sa.Column(sa.String(255))
|
|
description = sa.Column(sa.String(255))
|
|
vip_subnet_id = sa.Column(sa.String(36), nullable=False)
|
|
vip_port_id = sa.Column(sa.String(36), sa.ForeignKey(
|
|
'ports.id', name='fk_lbaas_loadbalancers_ports_id'))
|
|
vip_address = sa.Column(sa.String(36))
|
|
provisioning_status = sa.Column(sa.String(16), nullable=False)
|
|
operating_status = sa.Column(sa.String(16), nullable=False)
|
|
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
|
vip_port = orm.relationship(models_v2.Port)
|
|
stats = orm.relationship(
|
|
LoadBalancerStatistics,
|
|
uselist=False,
|
|
backref=orm.backref("loadbalancer", uselist=False),
|
|
cascade="all, delete-orphan")
|
|
provider = orm.relationship(
|
|
st_db.ProviderResourceAssociation,
|
|
uselist=False,
|
|
primaryjoin="LoadBalancer.id==ProviderResourceAssociation.resource_id",
|
|
foreign_keys=[st_db.ProviderResourceAssociation.resource_id],
|
|
# NOTE(ihrachys) it's not exactly clear why we would need to have the
|
|
# backref created (and not e.g. just back_populates= link) since we
|
|
# don't use the reverse property anywhere, but it helps with
|
|
# accommodating to the new neutron code that automatically detects
|
|
# obsolete foreign key state and expires affected relationships. The
|
|
# code is located in neutron/db/api.py and assumes all relationships
|
|
# should have backrefs.
|
|
backref='loadbalancer',
|
|
# this is only for old API backwards compatibility because when a load
|
|
# balancer is deleted the pool ID should be the same as the load
|
|
# balancer ID and should not be cleared out in this table
|
|
viewonly=True)
|
|
flavor_id = sa.Column(sa.String(36), sa.ForeignKey(
|
|
'flavors.id', name='fk_lbaas_loadbalancers_flavors_id'))
|
|
|
|
@property
|
|
def root_loadbalancer(self):
|
|
return self
|
|
|
|
@property
|
|
def to_api_dict(self):
|
|
def to_dict(sa_model, attributes):
|
|
ret = {}
|
|
for attr in attributes:
|
|
value = getattr(sa_model, attr)
|
|
ret[attr] = value
|
|
return ret
|
|
|
|
ret_dict = to_dict(self, [
|
|
'id', 'tenant_id', 'name', 'description',
|
|
'vip_subnet_id', 'vip_port_id', 'vip_address', 'operating_status',
|
|
'provisioning_status', 'admin_state_up', 'flavor_id'])
|
|
ret_dict['listeners'] = [{'id': listener.id}
|
|
for listener in self.listeners]
|
|
ret_dict['pools'] = [{'id': pool.id} for pool in self.pools]
|
|
|
|
if self.provider:
|
|
ret_dict['provider'] = self.provider.provider_name
|
|
|
|
if not self.flavor_id:
|
|
del ret_dict['flavor_id']
|
|
|
|
return ret_dict
|
|
|
|
|
|
class PoolV2(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
|
"""Represents a v2 neutron load balancer pool."""
|
|
|
|
NAME = 'pool'
|
|
|
|
__tablename__ = "lbaas_pools"
|
|
|
|
name = sa.Column(sa.String(255), nullable=True)
|
|
description = sa.Column(sa.String(255), nullable=True)
|
|
loadbalancer_id = sa.Column(sa.String(36), sa.ForeignKey(
|
|
"lbaas_loadbalancers.id"))
|
|
healthmonitor_id = sa.Column(sa.String(36),
|
|
sa.ForeignKey("lbaas_healthmonitors.id"),
|
|
unique=True,
|
|
nullable=True)
|
|
protocol = sa.Column(sa.Enum(*lb_const.POOL_SUPPORTED_PROTOCOLS,
|
|
name="pool_protocolsv2"),
|
|
nullable=False)
|
|
lb_algorithm = sa.Column(sa.Enum(*lb_const.SUPPORTED_LB_ALGORITHMS,
|
|
name="lb_algorithmsv2"),
|
|
nullable=False)
|
|
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
|
provisioning_status = sa.Column(sa.String(16), nullable=False)
|
|
operating_status = sa.Column(sa.String(16), nullable=False)
|
|
members = orm.relationship(MemberV2,
|
|
backref=orm.backref("pool", uselist=False),
|
|
cascade="all, delete-orphan")
|
|
healthmonitor = orm.relationship(
|
|
HealthMonitorV2,
|
|
backref=orm.backref("pool", uselist=False))
|
|
session_persistence = orm.relationship(
|
|
SessionPersistenceV2,
|
|
uselist=False,
|
|
backref=orm.backref("pool", uselist=False),
|
|
cascade="all, delete-orphan")
|
|
loadbalancer = orm.relationship(
|
|
LoadBalancer, uselist=False,
|
|
backref=orm.backref("pools", uselist=True))
|
|
|
|
@property
|
|
def root_loadbalancer(self):
|
|
return self.loadbalancer
|
|
|
|
# No real relationship here. But we want to fake a pool having a
|
|
# 'listener_id' sometimes for API back-ward compatibility purposes.
|
|
@property
|
|
def listener(self):
|
|
if self.listeners:
|
|
return self.listeners[0]
|
|
else:
|
|
return None
|
|
|
|
@property
|
|
def to_api_dict(self):
|
|
def to_dict(sa_model, attributes):
|
|
ret = {}
|
|
for attr in attributes:
|
|
value = getattr(sa_model, attr)
|
|
ret[attr] = value
|
|
return ret
|
|
|
|
ret_dict = to_dict(self, [
|
|
'id', 'tenant_id', 'name', 'description',
|
|
'healthmonitor_id', 'protocol', 'lb_algorithm', 'admin_state_up'])
|
|
|
|
ret_dict['loadbalancers'] = []
|
|
if self.loadbalancer:
|
|
ret_dict['loadbalancers'].append({'id': self.loadbalancer.id})
|
|
ret_dict['session_persistence'] = None
|
|
if self.session_persistence:
|
|
ret_dict['session_persistence'] = (
|
|
to_dict(self.session_persistence, [
|
|
'type', 'cookie_name']))
|
|
ret_dict['members'] = [{'id': member.id} for member in self.members]
|
|
ret_dict['listeners'] = [{'id': listener.id}
|
|
for listener in self.listeners]
|
|
if self.listener:
|
|
ret_dict['listener_id'] = self.listener.id
|
|
else:
|
|
ret_dict['listener_id'] = None
|
|
ret_dict['l7_policies'] = [{'id': l7_policy.id}
|
|
for l7_policy in self.l7_policies]
|
|
return ret_dict
|
|
|
|
|
|
class SNI(model_base.BASEV2):
|
|
|
|
"""Many-to-many association between Listener and TLS container ids
|
|
Making the SNI certificates list, ordered using the position
|
|
"""
|
|
|
|
NAME = 'sni'
|
|
|
|
__tablename__ = "lbaas_sni"
|
|
|
|
listener_id = sa.Column(sa.String(36),
|
|
sa.ForeignKey("lbaas_listeners.id"),
|
|
primary_key=True,
|
|
nullable=False)
|
|
tls_container_id = sa.Column(sa.String(128),
|
|
primary_key=True,
|
|
nullable=False)
|
|
position = sa.Column(sa.Integer)
|
|
|
|
@property
|
|
def root_loadbalancer(self):
|
|
return self.listener.loadbalancer
|
|
|
|
|
|
class L7Rule(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
|
"""Represents L7 Rule."""
|
|
|
|
NAME = 'l7rule'
|
|
|
|
__tablename__ = "lbaas_l7rules"
|
|
|
|
l7policy_id = sa.Column(sa.String(36),
|
|
sa.ForeignKey("lbaas_l7policies.id"),
|
|
nullable=False)
|
|
type = sa.Column(sa.Enum(*lb_const.SUPPORTED_L7_RULE_TYPES,
|
|
name="l7rule_typesv2"),
|
|
nullable=False)
|
|
compare_type = sa.Column(sa.Enum(*lb_const.SUPPORTED_L7_RULE_COMPARE_TYPES,
|
|
name="l7rule_compare_typev2"),
|
|
nullable=False)
|
|
invert = sa.Column(sa.Boolean(), nullable=False)
|
|
key = sa.Column(sa.String(255), nullable=True)
|
|
value = sa.Column(sa.String(255), nullable=False)
|
|
provisioning_status = sa.Column(sa.String(16), nullable=False)
|
|
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
|
|
|
@property
|
|
def root_loadbalancer(self):
|
|
return self.policy.listener.loadbalancer
|
|
|
|
@property
|
|
def to_api_dict(self):
|
|
def to_dict(sa_model, attributes):
|
|
ret = {}
|
|
for attr in attributes:
|
|
value = getattr(sa_model, attr)
|
|
ret[attr] = value
|
|
return ret
|
|
|
|
ret_dict = to_dict(self, [
|
|
'id', 'tenant_id', 'type', 'compare_type', 'invert', 'key',
|
|
'value', 'admin_state_up'])
|
|
|
|
ret_dict['policies'] = []
|
|
if self.policy:
|
|
ret_dict['policies'].append({'id': self.policy.id})
|
|
return ret_dict
|
|
|
|
|
|
class L7Policy(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
|
"""Represents L7 Policy."""
|
|
|
|
NAME = 'l7policy'
|
|
|
|
__tablename__ = "lbaas_l7policies"
|
|
|
|
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("lbaas_listeners.id"),
|
|
nullable=False)
|
|
action = sa.Column(sa.Enum(*lb_const.SUPPORTED_L7_POLICY_ACTIONS,
|
|
name="l7policy_action_typesv2"),
|
|
nullable=False)
|
|
redirect_pool_id = sa.Column(sa.String(36),
|
|
sa.ForeignKey("lbaas_pools.id"),
|
|
nullable=True)
|
|
redirect_url = sa.Column(sa.String(255),
|
|
nullable=True)
|
|
position = sa.Column(sa.Integer, nullable=False)
|
|
provisioning_status = sa.Column(sa.String(16), nullable=False)
|
|
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
|
rules = orm.relationship(
|
|
L7Rule,
|
|
uselist=True,
|
|
primaryjoin="L7Policy.id==L7Rule.l7policy_id",
|
|
foreign_keys=[L7Rule.l7policy_id],
|
|
cascade="all, delete-orphan",
|
|
backref=orm.backref("policy")
|
|
)
|
|
redirect_pool = orm.relationship(
|
|
PoolV2, backref=orm.backref("l7_policies", uselist=True))
|
|
|
|
@property
|
|
def root_loadbalancer(self):
|
|
return self.listener.loadbalancer
|
|
|
|
@property
|
|
def to_api_dict(self):
|
|
def to_dict(sa_model, attributes):
|
|
ret = {}
|
|
for attr in attributes:
|
|
value = getattr(sa_model, attr)
|
|
ret[attr] = value
|
|
return ret
|
|
|
|
ret_dict = to_dict(self, [
|
|
'id', 'tenant_id', 'name', 'description', 'listener_id', 'action',
|
|
'redirect_pool_id', 'redirect_url', 'position', 'admin_state_up'])
|
|
|
|
ret_dict['listeners'] = [{'id': self.listener_id}]
|
|
ret_dict['rules'] = [{'id': rule.id} for rule in self.rules]
|
|
if (ret_dict.get('action') ==
|
|
lb_const.L7_POLICY_ACTION_REDIRECT_TO_POOL):
|
|
del ret_dict['redirect_url']
|
|
return ret_dict
|
|
|
|
|
|
class Listener(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
|
"""Represents a v2 neutron listener."""
|
|
|
|
NAME = 'listener'
|
|
|
|
__tablename__ = "lbaas_listeners"
|
|
|
|
__table_args__ = (
|
|
sa.schema.UniqueConstraint('loadbalancer_id', 'protocol_port',
|
|
name='uniq_loadbalancer_listener_port'),
|
|
)
|
|
|
|
name = sa.Column(sa.String(255))
|
|
description = sa.Column(sa.String(255))
|
|
default_pool_id = sa.Column(sa.String(36), sa.ForeignKey("lbaas_pools.id"),
|
|
nullable=True)
|
|
loadbalancer_id = sa.Column(sa.String(36), sa.ForeignKey(
|
|
"lbaas_loadbalancers.id"))
|
|
protocol = sa.Column(sa.Enum(*lb_const.LISTENER_SUPPORTED_PROTOCOLS,
|
|
name="listener_protocolsv2"),
|
|
nullable=False)
|
|
default_tls_container_id = sa.Column(sa.String(128),
|
|
default=None, nullable=True)
|
|
sni_containers = orm.relationship(
|
|
SNI,
|
|
backref=orm.backref("listener", uselist=False),
|
|
uselist=True,
|
|
primaryjoin="Listener.id==SNI.listener_id",
|
|
order_by='SNI.position',
|
|
collection_class=orderinglist.ordering_list(
|
|
'position'),
|
|
foreign_keys=[SNI.listener_id],
|
|
cascade="all, delete-orphan"
|
|
)
|
|
protocol_port = sa.Column(sa.Integer, nullable=False)
|
|
connection_limit = sa.Column(sa.Integer)
|
|
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
|
provisioning_status = sa.Column(sa.String(16), nullable=False)
|
|
operating_status = sa.Column(sa.String(16), nullable=False)
|
|
default_pool = orm.relationship(
|
|
PoolV2, backref=orm.backref("listeners"))
|
|
loadbalancer = orm.relationship(
|
|
LoadBalancer,
|
|
backref=orm.backref("listeners", uselist=True))
|
|
l7_policies = orm.relationship(
|
|
L7Policy,
|
|
uselist=True,
|
|
primaryjoin="Listener.id==L7Policy.listener_id",
|
|
order_by="L7Policy.position",
|
|
collection_class=orderinglist.ordering_list('position', count_from=1),
|
|
foreign_keys=[L7Policy.listener_id],
|
|
cascade="all, delete-orphan",
|
|
backref=orm.backref("listener"))
|
|
|
|
@property
|
|
def root_loadbalancer(self):
|
|
return self.loadbalancer
|
|
|
|
@property
|
|
def to_api_dict(self):
|
|
def to_dict(sa_model, attributes):
|
|
ret = {}
|
|
for attr in attributes:
|
|
value = getattr(sa_model, attr)
|
|
ret[attr] = value
|
|
return ret
|
|
|
|
ret_dict = to_dict(self, [
|
|
'id', 'tenant_id', 'name', 'description', 'default_pool_id',
|
|
'protocol', 'default_tls_container_id', 'protocol_port',
|
|
'connection_limit', 'admin_state_up'])
|
|
|
|
# NOTE(blogan): Returning a list to future proof for M:N objects
|
|
# that are not yet implemented.
|
|
ret_dict['loadbalancers'] = []
|
|
if self.loadbalancer:
|
|
ret_dict['loadbalancers'].append({'id': self.loadbalancer.id})
|
|
ret_dict['sni_container_refs'] = [container.tls_container_id
|
|
for container in self.sni_containers]
|
|
ret_dict['default_tls_container_ref'] = self.default_tls_container_id
|
|
ret_dict['l7policies'] = [{'id': l7_policy.id}
|
|
for l7_policy in self.l7_policies]
|
|
return ret_dict
|