# octavia.common.data_models

# Source code for octavia.common.data_models

    #    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
    #    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.

    import datetime
    import re
    import typing as tp

    from oslo_log import log as logging
    from sqlalchemy.orm import collections

    from octavia.common import constants

    LOG = logging.getLogger(__name__)

    class BaseDataModel:
        def _to_dict(self, value, calling_classes=None, recurse=False):
            calling_classes = calling_classes or []
            # We need to have json convertible data for storing it in
            # persistence jobboard backend.
            if isinstance(value, datetime.datetime):
                ret = value.isoformat()
            elif isinstance(value, bytes):
                ret = value.decode()
            elif recurse:
                if isinstance(value, list):
                    ret = []
                    for item in value:
                        if isinstance(item, BaseDataModel):
                            if type(self) not in calling_classes:
                                ret.append(
                                    item.to_dict(calling_classes=(
                                        calling_classes + [type(self)]),
                                        recurse=recurse))
                            else:
                                # TODO(rm_work): Is the idea that if this list
                                #  contains ANY BaseDataModel, that all of them
                                #  are data models, and we may as well quit?
                                #  Or, were we supposed to append a `None` for
                                #  each one? I assume the former?
                                ret = None
                                break
                        else:
                            ret.append(
                                self._to_dict(item,
                                              calling_classes=calling_classes,
                                              recurse=recurse))
                elif isinstance(value, BaseDataModel):
                    if type(self) not in calling_classes:
                        ret = value.to_dict(
                            calling_classes=calling_classes + [type(self)],
                            recurse=recurse)
                    else:
                        ret = None
                else:
                    ret = value
            else:
                if isinstance(value, BaseDataModel):
                    ret = None
                elif isinstance(value, list):
                    ret = []
                else:
                    ret = value
            return ret

        def to_dict(self, calling_classes=None, recurse=False, **kwargs):
            """Converts a data model to a dictionary."""
            calling_classes = calling_classes or []
            ret = {}
            for attr, value in self.__dict__.items():
                if attr.startswith('_') or not kwargs.get(attr, True):
                    continue
                if attr == 'tags':
                    # tags is a list, it doesn't need recurse
                    ret[attr] = value
                    continue
                ret[attr] = self._to_dict(value, calling_classes=calling_classes,
                                          recurse=recurse)

            return ret

        def __eq__(self, other):
            if isinstance(other, self.__class__):
                return self.to_dict() == other.to_dict()
            return False

        def __ne__(self, other):
            return not self.__eq__(other)

        @classmethod
        def from_dict(cls, dict):
            return cls(**dict)

        @classmethod
        def _name(cls):
            """Returns class name in a more human readable form."""
            # Split the class name up by capitalized words
            return ' '.join(re.findall('[A-Z][^A-Z]*', cls.__name__))

        def _get_unique_key(self, obj=None):
            """Returns a unique key for passed object for data model building."""
            obj = obj or self
            # First handle all objects with their own ID, then handle subordinate
            # objects.
            if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
                                          'Listener', 'Amphora', 'L7Policy',
                                          'L7Rule']:
                return obj.__class__.__name__ + obj.id
            if obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
                return obj.__class__.__name__ + obj.pool_id
            if obj.__class__.__name__ in ['ListenerStatistics']:
                return obj.__class__.__name__ + obj.listener_id + obj.amphora_id
            if obj.__class__.__name__ in ['ListenerCidr']:
                return obj.__class__.__name__ + obj.listener_id + obj.cidr
            if obj.__class__.__name__ in ['VRRPGroup', 'Vip']:
                return obj.__class__.__name__ + obj.load_balancer_id
            if obj.__class__.__name__ in ['AmphoraHealth']:
                return obj.__class__.__name__ + obj.amphora_id
            if obj.__class__.__name__ in ['SNI']:
                return (obj.__class__.__name__ +
                        obj.listener_id + obj.tls_container_id)
            raise NotImplementedError

        def _find_in_graph(self, key, _visited_nodes=None):
            """Locates an object with the given unique key in the current

            object graph and returns a reference to it.
            """
            _visited_nodes = _visited_nodes or []
            mykey = self._get_unique_key()
            if mykey in _visited_nodes:
                # Seen this node already, don't traverse further
                return None
            if mykey == key:
                return self
            _visited_nodes.append(mykey)
            attr_names = [attr_name for attr_name in dir(self)
                          if not attr_name.startswith('_')]
            for attr_name in attr_names:
                attr = getattr(self, attr_name)
                if isinstance(attr, BaseDataModel):
                    result = attr._find_in_graph(
                        key, _visited_nodes=_visited_nodes)
                    if result is not None:
                        return result
                elif isinstance(attr, (collections.InstrumentedList, list)):
                    for item in attr:
                        if isinstance(item, BaseDataModel):
                            result = item._find_in_graph(
                                key, _visited_nodes=_visited_nodes)
                            if result is not None:
                                return result
            # If we are here we didn't find it.
            return None

        def update(self, update_dict):
            """Generic update method which works for simple,

            non-relational attributes.
            """
            for key, value in update_dict.items():
                setattr(self, key, value)

    class SessionPersistence(BaseDataModel):

        def __init__(self, pool_id=None, type=None, cookie_name=None,
                     pool=None, persistence_timeout=None,
                     persistence_granularity=None):
            self.pool_id = pool_id
            self.type = type
            self.cookie_name = cookie_name
            self.pool = pool
            self.persistence_timeout = persistence_timeout
            self.persistence_granularity = persistence_granularity

        def delete(self):
            self.pool.session_persistence = None

    class ListenerStatistics(BaseDataModel):

        def __init__(self, listener_id=None, amphora_id=None, bytes_in=0,
                     bytes_out=0, active_connections=0,
                     total_connections=0, request_errors=0, received_time=0.0):
            self.listener_id = listener_id
            self.amphora_id = amphora_id
            self.bytes_in = bytes_in
            self.bytes_out = bytes_out
            self.active_connections = active_connections
            self.total_connections = total_connections
            self.request_errors = request_errors
            self.received_time = received_time

        def get_stats(self):
            stats = {
                'bytes_in': self.bytes_in,
                'bytes_out': self.bytes_out,
                'active_connections': self.active_connections,
                'total_connections': self.total_connections,
                'request_errors': self.request_errors,
            }
            return stats

        def db_fields(self):
            fields = self.to_dict()
            fields.pop('received_time')
            return fields

        def __iadd__(self, other):
            if isinstance(other, ListenerStatistics):
                self.bytes_in += other.bytes_in
                self.bytes_out += other.bytes_out
                self.request_errors += other.request_errors
                self.total_connections += other.total_connections
            else:
                raise TypeError(  # noqa: O342
                    "unsupported operand type(s) for +=: '{}' and '{}'".format(
                        type(self), type(other)))

            return self

    class LoadBalancerStatistics(BaseDataModel):

        def __init__(self, bytes_in=0, bytes_out=0, active_connections=0,
                     total_connections=0, request_errors=0, listeners=None):
            self.bytes_in = bytes_in
            self.bytes_out = bytes_out
            self.active_connections = active_connections
            self.total_connections = total_connections
            self.request_errors = request_errors
            self.listeners = listeners or []

        def get_stats(self):
            stats = {
                'bytes_in': self.bytes_in,
                'bytes_out': self.bytes_out,
                'active_connections': self.active_connections,
                'total_connections': self.total_connections,
                'request_errors': self.request_errors,
            }
            return stats

    class HealthMonitor(BaseDataModel):

        def __init__(self, id=None, project_id=None, pool_id=None, type=None,
                     delay=None, timeout=None, fall_threshold=None,
                     rise_threshold=None, http_method=None, url_path=None,
                     expected_codes=None, enabled=None, pool=None, name=None,
                     provisioning_status=None, operating_status=None,
                     created_at=None, updated_at=None, tags=None,
                     http_version=None, domain_name=None):
            self.id = id
            self.project_id = project_id
            self.pool_id = pool_id
            self.type = type
            self.delay = delay
            self.timeout = timeout
            self.fall_threshold = fall_threshold
            self.rise_threshold = rise_threshold
            self.http_method = http_method
            self.url_path = url_path
            self.expected_codes = expected_codes
            self.enabled = enabled
            self.pool = pool
            self.provisioning_status = provisioning_status
            self.operating_status = operating_status
            self.name = name
            self.created_at = created_at
            self.updated_at = updated_at
            self.tags = tags
            self.http_version = http_version
            self.domain_name = domain_name

        def delete(self):
            self.pool.health_monitor = None

    class Pool(BaseDataModel):
        def __init__(self, id=None, project_id=None, name=None, description=None,
                     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, l7policies=None,
                     created_at=None, updated_at=None, provisioning_status=None,
                     tags=None, tls_certificate_id=None,
                     ca_tls_certificate_id=None, crl_container_id=None,
                     tls_enabled=None, tls_ciphers=None, tls_versions=None,
                     alpn_protocols=None):
            self.id = id
            self.project_id = project_id
            self.name = name
            self.description = description
            self.load_balancer_id = load_balancer_id
            self.load_balancer = load_balancer
            self.protocol = protocol
            self.lb_algorithm = lb_algorithm
            self.enabled = enabled
            self.operating_status = operating_status
            self.members = members or []
            self.health_monitor = health_monitor
            self.session_persistence = session_persistence
            self.listeners = listeners or []
            self.l7policies = l7policies or []
            self.created_at = created_at
            self.updated_at = updated_at
            self.provisioning_status = provisioning_status
            self.tags = tags
            self.tls_certificate_id = tls_certificate_id
            self.ca_tls_certificate_id = ca_tls_certificate_id
            self.crl_container_id = crl_container_id
            self.tls_enabled = tls_enabled
            self.tls_ciphers = tls_ciphers
            self.tls_versions = tls_versions
            self.alpn_protocols = alpn_protocols

        def update(self, update_dict):
            for key, value in update_dict.items():
                if key == 'session_persistence':
                    if value is None or value == {}:
                        if self.session_persistence is not None:
                            self.session_persistence.delete()
                    elif self.session_persistence is not None:
                        self.session_persistence.update(value)
                    else:
                        value.update({'pool_id': self.id})
                        self.session_persistence = SessionPersistence(**value)
                else:
                    setattr(self, key, value)

        def delete(self):
            for listener in self.listeners:
                if listener.default_pool_id == self.id:
                    listener.default_pool = None
                    listener.default_pool_id = None
                for pool in listener.pools:
                    if pool.id == self.id:
                        listener.pools.remove(pool)
                        break
            for pool in self.load_balancer.pools:
                if pool.id == self.id:
                    try:
                        self.load_balancer.pools.remove(pool)
                    except ValueError:
                        LOG.debug("Pool %s has already been removed from load "
                                  "balancer pools list.", pool.id)
                    break
            for l7policy in self.l7policies:
                if l7policy.redirect_pool_id == self.id:
                    # Technically this should never happen, as we block deletion
                    # of pools in use by L7Policies at the API. However, we should
                    # probably keep this here in case the data model gets
                    # manipulated in some other way in the future.
                    l7policy.action = constants.L7POLICY_ACTION_REJECT
                    l7policy.redirect_pool = None
                    l7policy.redirect_pool_id = None

    class Member(BaseDataModel):

        def __init__(self, id=None, project_id=None, pool_id=None, ip_address=None,
                     protocol_port=None, weight=None, backup=None, enabled=None,
                     subnet_id=None, operating_status=None, pool=None,
                     created_at=None, updated_at=None, provisioning_status=None,
                     name=None, monitor_address=None, monitor_port=None,
                     tags=None, vnic_type=None):
            self.id = id
            self.project_id = project_id
            self.pool_id = pool_id
            self.ip_address = ip_address
            self.protocol_port = protocol_port
            self.weight = weight
            self.backup = backup
            self.enabled = enabled
            self.subnet_id = subnet_id
            self.operating_status = operating_status
            self.pool = pool
            self.created_at = created_at
            self.updated_at = updated_at
            self.provisioning_status = provisioning_status
            self.name = name
            self.monitor_address = monitor_address
            self.monitor_port = monitor_port
            self.tags = tags
            self.vnic_type = vnic_type

        def delete(self):
            for mem in self.pool.members:
                if mem.id == self.id:
                    self.pool.members.remove(mem)
                    break

    class Listener(BaseDataModel):

        def __init__(self, id=None, project_id=None, name=None, description=None,
                     default_pool_id=None, load_balancer_id=None, protocol=None,
                     protocol_port=None, connection_limit=None,
                     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,
                     l7policies=None, pools=None, insert_headers=None,
                     created_at=None, updated_at=None,
                     timeout_client_data=None, timeout_member_connect=None,
                     timeout_member_data=None, timeout_tcp_inspect=None,
                     tags=None, client_ca_tls_certificate_id=None,
                     client_authentication=None, client_crl_container_id=None,
                     allowed_cidrs=None, tls_ciphers=None, tls_versions=None,
                     alpn_protocols=None, hsts_max_age=None,
                     hsts_include_subdomains=None, hsts_preload=None):
            self.id = id
            self.project_id = project_id
            self.name = name
            self.description = description
            self.default_pool_id = default_pool_id
            self.load_balancer_id = load_balancer_id
            self.protocol = protocol
            self.protocol_port = protocol_port
            self.connection_limit = connection_limit
            self.enabled = enabled
            self.provisioning_status = provisioning_status
            self.operating_status = operating_status
            self.tls_certificate_id = tls_certificate_id
            self.stats = stats
            self.default_pool = default_pool
            self.load_balancer = load_balancer
            self.sni_containers = sni_containers or []
            self.peer_port = peer_port
            self.l7policies = l7policies or []
            self.insert_headers = insert_headers or {}
            self.pools = pools or []
            self.created_at = created_at
            self.updated_at = updated_at
            self.timeout_client_data = timeout_client_data
            self.timeout_member_connect = timeout_member_connect
            self.timeout_member_data = timeout_member_data
            self.timeout_tcp_inspect = timeout_tcp_inspect
            self.tags = tags
            self.client_ca_tls_certificate_id = client_ca_tls_certificate_id
            self.client_authentication = client_authentication
            self.client_crl_container_id = client_crl_container_id
            self.allowed_cidrs = allowed_cidrs or []
            self.tls_ciphers = tls_ciphers
            self.tls_versions = tls_versions
            self.alpn_protocols = alpn_protocols
            self.hsts_max_age: tp.Optional[int] = hsts_max_age
            self.hsts_include_subdomains: tp.Optional[bool] = (
                hsts_include_subdomains)
            self.hsts_preload: tp.Optional[bool] = hsts_preload

        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
                                       p.l7rules and p.enabled is True]
                        old_pool = self.default_pool
                        if old_pool.id not in l7_pool_ids:
                            if old_pool in self.pools:
                                self.pools.remove(old_pool)
                            if self in old_pool.listeners:
                                old_pool.listeners.remove(self)
                    if value is not None:
                        pool = self._find_in_graph('Pool' + value)
                        if pool not in self.pools:
                            self.pools.append(pool)
                        if self not in pool.listeners:
                            pool.listeners.append(self)
                    else:
                        pool = None
                    setattr(self, 'default_pool', pool)

        def delete(self):
            for listener in self.load_balancer.listeners:
                if listener.id == self.id:
                    self.load_balancer.listeners.remove(listener)
                    break
            for pool in self.pools:
                pool.listeners.remove(self)

    class LoadBalancer(BaseDataModel):

        def __init__(self, id=None, project_id=None, name=None, description=None,
                     provisioning_status=None, operating_status=None, enabled=None,
                     topology=None, vip=None, listeners=None, amphorae=None,
                     pools=None, vrrp_group=None, server_group_id=None,
                     created_at=None, updated_at=None, provider=None, tags=None,
                     flavor_id=None, availability_zone=None,
                     additional_vips=None):

            self.id = id
            self.project_id = project_id
            self.name = name
            self.description = description
            self.provisioning_status = provisioning_status
            self.operating_status = operating_status
            self.enabled = enabled
            self.vip = vip
            self.vrrp_group = vrrp_group
            self.topology = topology
            self.listeners = listeners or []
            self.amphorae = amphorae or []
            self.pools = pools or []
            self.server_group_id = server_group_id
            self.created_at = created_at
            self.updated_at = updated_at
            self.provider = provider
            self.tags = tags or []
            self.flavor_id = flavor_id
            self.availability_zone = availability_zone
            self.additional_vips = additional_vips or []

        def update(self, update_dict):
            for key, value in update_dict.items():
                if key == 'vip':
                    if self.vip is not None:
                        self.vip.update(value)
                    else:
                        value.update({'load_balancer_id': self.id})
                        self.vip = Vip(**value)
                else:
                    setattr(self, key, value)

    class VRRPGroup(BaseDataModel):

        def __init__(self, load_balancer_id=None, vrrp_group_name=None,
                     vrrp_auth_type=None, vrrp_auth_pass=None, advert_int=None,
                     smtp_server=None, smtp_connect_timeout=None,
                     load_balancer=None):
            self.load_balancer_id = load_balancer_id
            self.vrrp_group_name = vrrp_group_name
            self.vrrp_auth_type = vrrp_auth_type
            self.vrrp_auth_pass = vrrp_auth_pass
            self.advert_int = advert_int
            self.load_balancer = load_balancer

    class Vip(BaseDataModel):

        def __init__(self, load_balancer_id=None, ip_address=None,
                     subnet_id=None, network_id=None, port_id=None,
                     load_balancer=None, qos_policy_id=None, octavia_owned=None,
                     vnic_type=None, sg_ids=None):
            self.load_balancer_id = load_balancer_id
            self.ip_address = ip_address
            self.subnet_id = subnet_id
            self.network_id = network_id
            self.port_id = port_id
            self.load_balancer = load_balancer
            self.qos_policy_id = qos_policy_id
            self.octavia_owned = octavia_owned
            self.vnic_type = vnic_type
            self.sg_ids = sg_ids or []

        def to_dict(self, **kwargs):
            ret = super().to_dict(**kwargs)
            if kwargs.get('recurse') is not True:
                # NOTE(gthiemonge) we need to return the associated SG IDs but as
                # sg_ids is a list, they are only added with recurse=True, which
                # does a full recursion on the Vip, adding the associated
                # LoadBalancer, its Listeners, etc...
                # Adding it directly here avoids unnecessary recursion
                ret[constants.SG_IDS] = self.sg_ids
            return ret

    class AdditionalVip(BaseDataModel):

        def __init__(self, load_balancer_id=None, ip_address=None, subnet_id=None,
                     network_id=None, port_id=None, load_balancer=None):
            self.load_balancer_id = load_balancer_id
            self.ip_address = ip_address
            self.subnet_id = subnet_id
            self.network_id = network_id
            self.port_id = port_id
            self.load_balancer = load_balancer

    class SNI(BaseDataModel):

        def __init__(self, listener_id=None, position=None, listener=None,
                     tls_container_id=None):
            self.listener_id = listener_id
            self.position = position
            self.listener = listener
            self.tls_container_id = tls_container_id

        # SQLAlchemy kindly attaches the whole listener object so
        # let's keep this simple by overriding the to_dict for this
        # object. Otherwise we recurse down the "ghost" listener object.

        def to_dict(self, **kwargs):
            return {'tls_container_id': self.tls_container_id,
                    'listener_id': self.listener_id,
                    'position': self.position}

    class TLSContainer(BaseDataModel):

        def __init__(self, id=None, primary_cn=None, certificate=None,
                     private_key=None, passphrase=None, intermediates=None):
            self.id = id
            self.primary_cn = primary_cn
            self.certificate = certificate
            self.private_key = private_key
            self.passphrase = passphrase
            self.intermediates = intermediates or []

    class Amphora(BaseDataModel):

        def __init__(self, id=None, load_balancer_id=None, compute_id=None,
                     status=None, lb_network_ip=None, vrrp_ip=None,
                     ha_ip=None, vrrp_port_id=None, ha_port_id=None,
                     load_balancer=None, role=None, cert_expiration=None,
                     cert_busy=False, vrrp_interface=None, vrrp_id=None,
                     vrrp_priority=None, cached_zone=None, created_at=None,
                     updated_at=None, image_id=None, compute_flavor=None):
            self.id = id
            self.load_balancer_id = load_balancer_id
            self.compute_id = compute_id
            self.status = status
            self.lb_network_ip = lb_network_ip
            self.vrrp_ip = vrrp_ip
            self.ha_ip = ha_ip
            self.vrrp_port_id = vrrp_port_id
            self.ha_port_id = ha_port_id
            self.role = role
            self.vrrp_interface = vrrp_interface
            self.vrrp_id = vrrp_id
            self.vrrp_priority = vrrp_priority
            self.load_balancer = load_balancer
            self.cert_expiration = cert_expiration
            self.cert_busy = cert_busy
            self.cached_zone = cached_zone
            self.created_at = created_at
            self.updated_at = updated_at
            self.image_id = image_id
            self.compute_flavor = compute_flavor

        def delete(self):
            for amphora in self.load_balancer.amphorae:
                if amphora.id == self.id:
                    self.load_balancer.amphorae.remove(amphora)
                    break

    class AmphoraHealth(BaseDataModel):

        def __init__(self, amphora_id=None, last_update=None, busy=False):
            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, enabled=None,
                     compare_type=None, key=None, value=None, l7policy=None,
                     invert=False, provisioning_status=None, operating_status=None,
                     project_id=None, created_at=None, updated_at=None, tags=None):
            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
            self.provisioning_status = provisioning_status
            self.operating_status = operating_status
            self.project_id = project_id
            self.created_at = created_at
            self.updated_at = updated_at
            self.enabled = enabled
            self.tags = tags

        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, provisioning_status=None,
                     operating_status=None, project_id=None, created_at=None,
                     updated_at=None, redirect_prefix=None, tags=None,
                     redirect_http_code=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 []
            self.provisioning_status = provisioning_status
            self.operating_status = operating_status
            self.project_id = project_id
            self.created_at = created_at
            self.updated_at = updated_at
            self.redirect_prefix = redirect_prefix
            self.tags = tags
            self.redirect_http_code = redirect_http_code

        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
                    p.l7rules 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' and value is not None:
                    self._conditionally_remove_pool_links(self.redirect_pool)
                    self.action = constants.L7POLICY_ACTION_REDIRECT_TO_POOL
                    self.redirect_url = None
                    self.redirect_http_code = None
                    pool = self._find_in_graph('Pool' + value)
                    self.redirect_pool = pool
                    if self.l7rules 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' and value is not None:
                    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
                    self.redirect_http_code = None
                elif key == 'position':
                    self.listener.l7policies.remove(self)
                    self.listener.l7policies.insert(value - 1, self)
                elif key == 'enabled':
                    if (value is True and self.action ==
                            constants.L7POLICY_ACTION_REDIRECT_TO_POOL and
                            self.redirect_pool is not None and
                            self.l7rules and
                            self.redirect_pool not in self.listener.pools):
                        self.listener.pools.append(self.redirect_pool)
                        self.redirect_pool.listeners.append(self.listener)
                    elif (value is False and self.action ==
                            constants.L7POLICY_ACTION_REDIRECT_TO_POOL and
                            self.redirect_pool is not None):
                        self._conditionally_remove_pool_links(
                            self.redirect_pool)
                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

    class Quotas(BaseDataModel):

        def __init__(self,
                     project_id=None,
                     load_balancer=None,
                     listener=None,
                     pool=None,
                     health_monitor=None,
                     member=None,
                     l7policy=None,
                     l7rule=None,
                     in_use_health_monitor=None,
                     in_use_listener=None,
                     in_use_load_balancer=None,
                     in_use_member=None,
                     in_use_pool=None,
                     in_use_l7policy=None,
                     in_use_l7rule=None):
            self.project_id = project_id
            self.health_monitor = health_monitor
            self.listener = listener
            self.load_balancer = load_balancer
            self.pool = pool
            self.member = member
            self.l7policy = l7policy
            self.l7rule = l7rule
            self.in_use_health_monitor = in_use_health_monitor
            self.in_use_listener = in_use_listener
            self.in_use_load_balancer = in_use_load_balancer
            self.in_use_member = in_use_member
            self.in_use_pool = in_use_pool
            self.in_use_l7policy = in_use_l7policy
            self.in_use_l7rule = in_use_l7rule

    class Flavor(BaseDataModel):

        def __init__(self, id=None, name=None,
                     description=None, enabled=None,
                     flavor_profile_id=None):
            self.id = id
            self.name = name
            self.description = description
            self.enabled = enabled
            self.flavor_profile_id = flavor_profile_id

    class FlavorProfile(BaseDataModel):

        def __init__(self, id=None, name=None, provider_name=None,
                     flavor_data=None):
            self.id = id
            self.name = name
            self.provider_name = provider_name
            self.flavor_data = flavor_data

    class AvailabilityZone(BaseDataModel):

        def __init__(self, name=None, description=None, enabled=None,
                     availability_zone_profile_id=None):
            self.name = name
            self.description = description
            self.enabled = enabled
            self.availability_zone_profile_id = availability_zone_profile_id

    class AvailabilityZoneProfile(BaseDataModel):

        def __init__(self, id=None, name=None, provider_name=None,
                     availability_zone_data=None):
            self.id = id
            self.name = name
            self.provider_name = provider_name
            self.availability_zone_data = availability_zone_data

    class ListenerCidr(BaseDataModel):

        def __init__(self, listener_id=None, cidr=None):
            self.listener_id = listener_id
            self.cidr = cidr

        # SQLAlchemy kindly attaches the whole listener object so
        # let's keep this simple by overriding the to_dict for this
        # object. Otherwise we recurse down the "ghost" listener object.

        def to_dict(self, **kwargs):
            return {'cidr': self.cidr, 'listener_id': self.listener_id}

    class VipSecurityGroup(BaseDataModel):
        def __init__(self, load_balancer_id: str = None, sg_id: str = None):
            self.load_balancer_id = load_balancer_id
            self.sg_id = sg_id

    class AmphoraMemberPort(BaseDataModel):

        def __init__(self, port_id=None, amphora_id=None, network_id=None,
                     created_at=None, updated_at=None):
            self.port_id = port_id
            self.amphora_id = amphora_id
            self.network_id = network_id
            self.created_at = created_at
            self.updated_at = updated_at

this page last updated: 2025-10-06 11:22:08

Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.

found an error? report a bug