You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
7.2 KiB
Python
185 lines
7.2 KiB
Python
# Copyright 2014 Rackspace
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from oslo_db.sqlalchemy import models
|
|
from oslo_utils import strutils
|
|
from oslo_utils import uuidutils
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.ext import declarative
|
|
from sqlalchemy.orm import collections
|
|
|
|
|
|
class OctaviaBase(models.ModelBase):
|
|
|
|
__data_model__ = None
|
|
|
|
@staticmethod
|
|
def _get_unique_key(obj):
|
|
"""Returns a unique key for passed object for data model building."""
|
|
# First handle all objects with their own ID, then handle subordinate
|
|
# objects.
|
|
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
|
|
'Listener', 'Amphora', 'L7Policy',
|
|
'L7Rule', 'Flavor', 'FlavorProfile']:
|
|
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 ['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)
|
|
if obj.__class__.__name__ in ['Quotas']:
|
|
return obj.__class__.__name__ + obj.project_id
|
|
raise NotImplementedError
|
|
|
|
def to_data_model(self, _graph_nodes=None):
|
|
"""Converts to a data model graph.
|
|
|
|
In order to make the resulting data model graph usable no matter how
|
|
many internal references are followed, we generate a complete graph of
|
|
OctaviaBase nodes connected to the object passed to this method.
|
|
|
|
:param _graph_nodes: Used only for internal recursion of this
|
|
method. Should not be called from the outside.
|
|
Contains a dictionary of all OctaviaBase type
|
|
objects in the generated graph
|
|
"""
|
|
_graph_nodes = _graph_nodes or {}
|
|
if not self.__data_model__:
|
|
raise NotImplementedError
|
|
dm_kwargs = {}
|
|
for column in self.__table__.columns:
|
|
dm_kwargs[column.name] = getattr(self, column.name)
|
|
|
|
attr_names = [attr_name for attr_name in dir(self)
|
|
if not attr_name.startswith('_')]
|
|
# Appending early, as any unique ID should be defined already and
|
|
# the rest of this object will get filled out more fully later on,
|
|
# and we need to add ourselves to the _graph_nodes before we
|
|
# attempt recursion.
|
|
dm_self = self.__data_model__(**dm_kwargs)
|
|
dm_key = self._get_unique_key(dm_self)
|
|
_graph_nodes.update({dm_key: dm_self})
|
|
for attr_name in attr_names:
|
|
attr = getattr(self, attr_name)
|
|
if isinstance(attr, OctaviaBase) and attr.__class__:
|
|
# If this attr is already in the graph node list, just
|
|
# reference it there and don't recurse.
|
|
ukey = self._get_unique_key(attr)
|
|
if ukey in _graph_nodes.keys():
|
|
setattr(dm_self, attr_name, _graph_nodes[ukey])
|
|
else:
|
|
setattr(dm_self, attr_name, attr.to_data_model(
|
|
_graph_nodes=_graph_nodes))
|
|
elif isinstance(attr, (collections.InstrumentedList, list)):
|
|
setattr(dm_self, attr_name, [])
|
|
listref = getattr(dm_self, attr_name)
|
|
for item in attr:
|
|
if isinstance(item, OctaviaBase) and item.__class__:
|
|
ukey = self._get_unique_key(item)
|
|
if ukey in _graph_nodes.keys():
|
|
listref.append(_graph_nodes[ukey])
|
|
else:
|
|
listref.append(
|
|
item.to_data_model(_graph_nodes=_graph_nodes))
|
|
elif not isinstance(item, OctaviaBase):
|
|
listref.append(item)
|
|
return dm_self
|
|
|
|
@staticmethod
|
|
def apply_filter(query, model, filters):
|
|
translated_filters = {}
|
|
child_map = {}
|
|
# Convert enabled to proper type
|
|
if 'enabled' in filters:
|
|
filters['enabled'] = strutils.bool_from_string(
|
|
filters['enabled'])
|
|
for attr, name_map in model.__v2_wsme__._child_map.items():
|
|
for k, v in name_map.items():
|
|
if attr in filters and k in filters[attr]:
|
|
child_map.setdefault(attr, {}).update(
|
|
{k: filters[attr].pop(k)})
|
|
filters.pop(attr, None)
|
|
|
|
for k, v in model.__v2_wsme__._type_to_model_map.items():
|
|
if k in filters:
|
|
translated_filters[v] = filters.pop(k)
|
|
translated_filters.update(filters)
|
|
if translated_filters:
|
|
query = query.filter_by(**translated_filters)
|
|
for k, v in child_map.items():
|
|
query = query.join(getattr(model, k)).filter_by(**v)
|
|
return query
|
|
|
|
|
|
class LookupTableMixin(object):
|
|
"""Mixin to add to classes that are lookup tables."""
|
|
name = sa.Column(sa.String(255), primary_key=True, nullable=False)
|
|
description = sa.Column(sa.String(255), nullable=True)
|
|
|
|
|
|
class IdMixin(object):
|
|
"""Id mixin, add to subclasses that have an id."""
|
|
id = sa.Column(sa.String(36), primary_key=True,
|
|
default=uuidutils.generate_uuid)
|
|
|
|
|
|
class ProjectMixin(object):
|
|
"""Tenant mixin, add to subclasses that have a project."""
|
|
project_id = sa.Column(sa.String(36))
|
|
|
|
|
|
class NameMixin(object):
|
|
"""Name mixin to add to classes which need a name."""
|
|
name = sa.Column(sa.String(255), nullable=True)
|
|
|
|
|
|
class TagMixin(object):
|
|
"""Tags mixin to add to classes which need tags.
|
|
|
|
The class must realize the specified db relationship as well.
|
|
"""
|
|
|
|
@property
|
|
def tags(self):
|
|
if self._tags:
|
|
return [each_tag.tag for each_tag in self._tags]
|
|
return []
|
|
|
|
@tags.setter
|
|
def tags(self, values):
|
|
new_tags = []
|
|
if values:
|
|
for tag in values:
|
|
tag_ref = Tags()
|
|
tag_ref.resource_id = self.id
|
|
tag_ref.tag = tag
|
|
new_tags.append(tag_ref)
|
|
self._tags = new_tags
|
|
|
|
|
|
BASE = declarative.declarative_base(cls=OctaviaBase)
|
|
|
|
|
|
class Tags(BASE):
|
|
__tablename__ = "tags"
|
|
|
|
resource_id = sa.Column(sa.String(36), primary_key=True)
|
|
tag = sa.Column(sa.String(255), primary_key=True, index=True)
|