277 lines
11 KiB
Python
277 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2014 Objectif Libre
|
|
#
|
|
# 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.
|
|
#
|
|
# @author: Stéphane Albert
|
|
#
|
|
from oslo_db.sqlalchemy import models
|
|
import sqlalchemy
|
|
from sqlalchemy.ext import declarative
|
|
from sqlalchemy import orm
|
|
from sqlalchemy import schema
|
|
|
|
Base = declarative.declarative_base()
|
|
|
|
|
|
class HashMapBase(models.ModelBase):
|
|
__table_args__ = {'mysql_charset': "utf8",
|
|
'mysql_engine': "InnoDB"}
|
|
fk_to_resolve = {}
|
|
|
|
def save(self, session=None):
|
|
from cloudkitty import db
|
|
|
|
if session is None:
|
|
session = db.get_session()
|
|
|
|
super(HashMapBase, self).save(session=session)
|
|
|
|
def as_dict(self):
|
|
d = {}
|
|
for c in self.__table__.columns:
|
|
if c.name == 'id':
|
|
continue
|
|
d[c.name] = self[c.name]
|
|
return d
|
|
|
|
def _recursive_resolve(self, path):
|
|
obj = self
|
|
for attr in path.split('.'):
|
|
if hasattr(obj, attr):
|
|
obj = getattr(obj, attr)
|
|
else:
|
|
return None
|
|
return obj
|
|
|
|
def export_model(self):
|
|
res = self.as_dict()
|
|
for fk, mapping in self.fk_to_resolve.items():
|
|
res[fk] = self._recursive_resolve(mapping)
|
|
return res
|
|
|
|
|
|
class HashMapService(Base, HashMapBase):
|
|
"""An hashmap service.
|
|
|
|
"""
|
|
__tablename__ = 'hashmap_services'
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
primary_key=True)
|
|
service_id = sqlalchemy.Column(sqlalchemy.String(36),
|
|
nullable=False,
|
|
unique=True)
|
|
name = sqlalchemy.Column(
|
|
sqlalchemy.String(255),
|
|
nullable=False,
|
|
unique=True)
|
|
fields = orm.relationship('HashMapField',
|
|
backref=orm.backref(
|
|
'service',
|
|
lazy='immediate'))
|
|
mappings = orm.relationship('HashMapMapping',
|
|
backref=orm.backref(
|
|
'service',
|
|
lazy='immediate'))
|
|
thresholds = orm.relationship('HashMapThreshold',
|
|
backref=orm.backref(
|
|
'service',
|
|
lazy='immediate'))
|
|
|
|
def __repr__(self):
|
|
return ('<HashMapService[{uuid}]: '
|
|
'service={service}>').format(
|
|
uuid=self.service_id,
|
|
service=self.name)
|
|
|
|
|
|
class HashMapField(Base, HashMapBase):
|
|
"""An hashmap field.
|
|
|
|
"""
|
|
__tablename__ = 'hashmap_fields'
|
|
fk_to_resolve = {'service_id': 'service.service_id'}
|
|
|
|
@declarative.declared_attr
|
|
def __table_args__(cls):
|
|
args = (schema.UniqueConstraint('field_id', 'name',
|
|
name='uniq_field'),
|
|
schema.UniqueConstraint('service_id', 'name',
|
|
name='uniq_map_service_field'),
|
|
HashMapBase.__table_args__,)
|
|
return args
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
primary_key=True)
|
|
field_id = sqlalchemy.Column(sqlalchemy.String(36),
|
|
nullable=False,
|
|
unique=True)
|
|
name = sqlalchemy.Column(sqlalchemy.String(255),
|
|
nullable=False)
|
|
service_id = sqlalchemy.Column(
|
|
sqlalchemy.Integer,
|
|
sqlalchemy.ForeignKey('hashmap_services.id',
|
|
ondelete='CASCADE'),
|
|
nullable=False)
|
|
mappings = orm.relationship('HashMapMapping',
|
|
backref=orm.backref(
|
|
'field',
|
|
lazy='immediate'))
|
|
thresholds = orm.relationship('HashMapThreshold',
|
|
backref=orm.backref(
|
|
'field',
|
|
lazy='immediate'))
|
|
|
|
def __repr__(self):
|
|
return ('<HashMapField[{uuid}]: '
|
|
'field={field}>').format(
|
|
uuid=self.field_id,
|
|
field=self.name)
|
|
|
|
|
|
class HashMapGroup(Base, HashMapBase):
|
|
"""A grouping of hashmap calculations.
|
|
|
|
"""
|
|
__tablename__ = 'hashmap_groups'
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
primary_key=True)
|
|
group_id = sqlalchemy.Column(sqlalchemy.String(36),
|
|
nullable=False,
|
|
unique=True)
|
|
name = sqlalchemy.Column(sqlalchemy.String(255),
|
|
nullable=False,
|
|
unique=True)
|
|
mappings = orm.relationship('HashMapMapping',
|
|
backref=orm.backref(
|
|
'group',
|
|
lazy='immediate'))
|
|
thresholds = orm.relationship('HashMapThreshold',
|
|
backref=orm.backref(
|
|
'group',
|
|
lazy='immediate'))
|
|
|
|
def __repr__(self):
|
|
return ('<HashMapGroup[{uuid}]: '
|
|
'name={name}>').format(
|
|
uuid=self.group_id,
|
|
name=self.name)
|
|
|
|
|
|
class HashMapMapping(Base, HashMapBase):
|
|
"""A mapping between a field a value and a type.
|
|
|
|
"""
|
|
__tablename__ = 'hashmap_maps'
|
|
fk_to_resolve = {'service_id': 'service.service_id',
|
|
'field_id': 'field.field_id',
|
|
'group_id': 'group.group_id'}
|
|
|
|
@declarative.declared_attr
|
|
def __table_args__(cls):
|
|
args = (schema.UniqueConstraint('value', 'field_id',
|
|
name='uniq_field_mapping'),
|
|
schema.UniqueConstraint('value', 'service_id',
|
|
name='uniq_service_mapping'),
|
|
HashMapBase.__table_args__,)
|
|
return args
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
primary_key=True)
|
|
mapping_id = sqlalchemy.Column(sqlalchemy.String(36),
|
|
nullable=False,
|
|
unique=True)
|
|
value = sqlalchemy.Column(sqlalchemy.String(255),
|
|
nullable=True)
|
|
cost = sqlalchemy.Column(sqlalchemy.Numeric(20, 8),
|
|
nullable=False)
|
|
map_type = sqlalchemy.Column(sqlalchemy.Enum('flat',
|
|
'rate',
|
|
name='enum_map_type'),
|
|
nullable=False)
|
|
service_id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
sqlalchemy.ForeignKey('hashmap_services.id',
|
|
ondelete='CASCADE'),
|
|
nullable=True)
|
|
field_id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
sqlalchemy.ForeignKey('hashmap_fields.id',
|
|
ondelete='CASCADE'),
|
|
nullable=True)
|
|
group_id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
sqlalchemy.ForeignKey('hashmap_groups.id',
|
|
ondelete='SET NULL'),
|
|
nullable=True)
|
|
|
|
def __repr__(self):
|
|
return ('<HashMapMapping[{uuid}]: '
|
|
'type={map_type} {value}={cost}>').format(
|
|
uuid=self.mapping_id,
|
|
map_type=self.map_type,
|
|
value=self.value,
|
|
cost=self.cost)
|
|
|
|
|
|
class HashMapThreshold(Base, HashMapBase):
|
|
"""A threshold matching a service, a field with a level and a type.
|
|
|
|
"""
|
|
__tablename__ = 'hashmap_thresholds'
|
|
fk_to_resolve = {'service_id': 'service.service_id',
|
|
'field_id': 'field.field_id',
|
|
'group_id': 'group.group_id'}
|
|
|
|
@declarative.declared_attr
|
|
def __table_args__(cls):
|
|
args = (schema.UniqueConstraint('level', 'field_id',
|
|
name='uniq_field_threshold'),
|
|
schema.UniqueConstraint('level', 'service_id',
|
|
name='uniq_service_threshold'),
|
|
HashMapBase.__table_args__,)
|
|
return args
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
primary_key=True)
|
|
threshold_id = sqlalchemy.Column(sqlalchemy.String(36),
|
|
nullable=False,
|
|
unique=True)
|
|
level = sqlalchemy.Column(sqlalchemy.Numeric(20, 8),
|
|
nullable=True)
|
|
cost = sqlalchemy.Column(sqlalchemy.Numeric(20, 8),
|
|
nullable=False)
|
|
map_type = sqlalchemy.Column(sqlalchemy.Enum('flat',
|
|
'rate',
|
|
name='enum_map_type'),
|
|
nullable=False)
|
|
service_id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
sqlalchemy.ForeignKey('hashmap_services.id',
|
|
ondelete='CASCADE'),
|
|
nullable=True)
|
|
field_id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
sqlalchemy.ForeignKey('hashmap_fields.id',
|
|
ondelete='CASCADE'),
|
|
nullable=True)
|
|
group_id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
sqlalchemy.ForeignKey('hashmap_groups.id',
|
|
ondelete='SET NULL'),
|
|
nullable=True)
|
|
|
|
def __repr__(self):
|
|
return ('<HashMapThreshold[{uuid}]: '
|
|
'type={map_type} {level}={cost}>').format(
|
|
uuid=self.threshold_id,
|
|
map_type=self.map_type,
|
|
level=self.level,
|
|
cost=self.cost)
|