# -*- 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.
#
from oslo_db.sqlalchemy import models
import sqlalchemy
from sqlalchemy.ext import declarative
from sqlalchemy import orm
from sqlalchemy import schema

from cloudkitty.common.db import models as ck_models

Base = ck_models.get_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):
    """A hashmap service.

    Used to describe a CloudKitty service such as compute or volume.
    """
    __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):
    """A hashmap field.

    Used to describe a service metadata such as flavor_id or image_id for
    compute.
    """
    __tablename__ = 'hashmap_fields'
    fk_to_resolve = {
        'service_id': 'service.service_id'}

    @declarative.declared_attr
    def __table_args__(cls):
        args = (
            schema.UniqueConstraint(
                'service_id',
                'name',
                name='uniq_field_per_service'),
            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.

    Used to group multiple mappings or thresholds into a single calculation.
    """
    __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 or service, a value and a type.

    Used to model final equation.
    """
    __tablename__ = 'hashmap_mappings'
    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',
                'tenant_id',
                name='uniq_field_mapping'),
            schema.UniqueConstraint(
                'value',
                'service_id',
                'tenant_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(40, 28),
        nullable=False)
    map_type = sqlalchemy.Column(
        sqlalchemy.Enum(
            'flat',
            'rate',
            name='enum_map_type', create_constraint=True),
        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)
    tenant_id = sqlalchemy.Column(
        sqlalchemy.String(255),
        nullable=True)

    def __repr__(self):
        return ('<HashMapMapping[{uuid}]: '
                'type={map_type} {value}={cost}, '
                'tenant={tenant}>').format(
                    uuid=self.mapping_id,
                    map_type=self.map_type,
                    value=self.value,
                    cost=self.cost,
                    tenant=self.tenant_id)


class HashMapThreshold(Base, HashMapBase):
    """A threshold matching a service or a field with a level and a type.

    Used to model final equation.
    """
    __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',
                'tenant_id',
                name='uniq_field_threshold'),
            schema.UniqueConstraint(
                'level',
                'service_id',
                'tenant_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(40, 28),
        nullable=False)
    map_type = sqlalchemy.Column(
        sqlalchemy.Enum(
            'flat',
            'rate',
            name='enum_hashmap_type', create_constraint=True),
        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)
    tenant_id = sqlalchemy.Column(
        sqlalchemy.String(255),
        nullable=True)

    def __repr__(self):
        return ('<HashMapThreshold[{uuid}]: '
                'type={map_type} {level}={cost}, '
                'tenant={tenant}>').format(
                    uuid=self.threshold_id,
                    map_type=self.map_type,
                    level=self.level,
                    cost=self.cost,
                    tenant=self.tenant_id)

cb3a39e8527efe9d2edbfb48e5861b481b4e4e28
<mask token>


class HashMapService(Base, HashMapBase):
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>

    def __repr__(self):
        return '<HashMapService[{uuid}]: service={service}>'.format(uuid=
            self.service_id, service=self.name)


class HashMapField(Base, HashMapBase):
    """A hashmap field.

    Used to describe a service metadata such as flavor_id or image_id for
    compute.
    """
    __tablename__ = 'hashmap_fields'
    fk_to_resolve = {'service_id': 'service.service_id'}

    @declarative.declared_attr
    def __table_args__(cls):
        args = schema.UniqueConstraint('service_id', 'name', name=
            'uniq_field_per_service'), 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.

    Used to group multiple mappings or thresholds into a single calculation.
    """
    __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 or service, a value and a type.

    Used to model final equation.
    """
    __tablename__ = 'hashmap_mappings'
    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', 'tenant_id',
            name='uniq_field_mapping'), schema.UniqueConstraint('value',
            'service_id', 'tenant_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(40, 28), nullable=False)
    map_type = sqlalchemy.Column(sqlalchemy.Enum('flat', 'rate', name=
        'enum_map_type', create_constraint=True), 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)
    tenant_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)

    def __repr__(self):
        return (
            '<HashMapMapping[{uuid}]: type={map_type} {value}={cost}, tenant={tenant}>'
            .format(uuid=self.mapping_id, map_type=self.map_type, value=
            self.value, cost=self.cost, tenant=self.tenant_id))


class HashMapThreshold(Base, HashMapBase):
    """A threshold matching a service or a field with a level and a type.

    Used to model final equation.
    """
    __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', 'tenant_id',
            name='uniq_field_threshold'), schema.UniqueConstraint('level',
            'service_id', 'tenant_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(40, 28), nullable=False)
    map_type = sqlalchemy.Column(sqlalchemy.Enum('flat', 'rate', name=
        'enum_hashmap_type', create_constraint=True), 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)
    tenant_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)

    def __repr__(self):
        return (
            '<HashMapThreshold[{uuid}]: type={map_type} {level}={cost}, tenant={tenant}>'
            .format(uuid=self.threshold_id, map_type=self.map_type, level=
            self.level, cost=self.cost, tenant=self.tenant_id))

<mask token>


class HashMapBase(models.ModelBase):
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>


class HashMapService(Base, HashMapBase):
    """A hashmap service.

    Used to describe a CloudKitty service such as compute or volume.
    """
    __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):
    """A hashmap field.

    Used to describe a service metadata such as flavor_id or image_id for
    compute.
    """
    __tablename__ = 'hashmap_fields'
    fk_to_resolve = {'service_id': 'service.service_id'}

    @declarative.declared_attr
    def __table_args__(cls):
        args = schema.UniqueConstraint('service_id', 'name', name=
            'uniq_field_per_service'), 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.

    Used to group multiple mappings or thresholds into a single calculation.
    """
    __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 or service, a value and a type.

    Used to model final equation.
    """
    __tablename__ = 'hashmap_mappings'
    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', 'tenant_id',
            name='uniq_field_mapping'), schema.UniqueConstraint('value',
            'service_id', 'tenant_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(40, 28), nullable=False)
    map_type = sqlalchemy.Column(sqlalchemy.Enum('flat', 'rate', name=
        'enum_map_type', create_constraint=True), 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)
    tenant_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)

    def __repr__(self):
        return (
            '<HashMapMapping[{uuid}]: type={map_type} {value}={cost}, tenant={tenant}>'
            .format(uuid=self.mapping_id, map_type=self.map_type, value=
            self.value, cost=self.cost, tenant=self.tenant_id))


class HashMapThreshold(Base, HashMapBase):
    """A threshold matching a service or a field with a level and a type.

    Used to model final equation.
    """
    __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', 'tenant_id',
            name='uniq_field_threshold'), schema.UniqueConstraint('level',
            'service_id', 'tenant_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(40, 28), nullable=False)
    map_type = sqlalchemy.Column(sqlalchemy.Enum('flat', 'rate', name=
        'enum_hashmap_type', create_constraint=True), 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)
    tenant_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)

    def __repr__(self):
        return (
            '<HashMapThreshold[{uuid}]: type={map_type} {level}={cost}, tenant={tenant}>'
            .format(uuid=self.threshold_id, map_type=self.map_type, level=
            self.level, cost=self.cost, tenant=self.tenant_id))

<mask token>


class HashMapBase(models.ModelBase):
    <mask token>
    <mask token>
    <mask token>

    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
    <mask token>


class HashMapService(Base, HashMapBase):
    """A hashmap service.

    Used to describe a CloudKitty service such as compute or volume.
    """
    __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):
    """A hashmap field.

    Used to describe a service metadata such as flavor_id or image_id for
    compute.
    """
    __tablename__ = 'hashmap_fields'
    fk_to_resolve = {'service_id': 'service.service_id'}

    @declarative.declared_attr
    def __table_args__(cls):
        args = schema.UniqueConstraint('service_id', 'name', name=
            'uniq_field_per_service'), 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.

    Used to group multiple mappings or thresholds into a single calculation.
    """
    __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 or service, a value and a type.

    Used to model final equation.
    """
    __tablename__ = 'hashmap_mappings'
    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', 'tenant_id',
            name='uniq_field_mapping'), schema.UniqueConstraint('value',
            'service_id', 'tenant_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(40, 28), nullable=False)
    map_type = sqlalchemy.Column(sqlalchemy.Enum('flat', 'rate', name=
        'enum_map_type', create_constraint=True), 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)
    tenant_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)

    def __repr__(self):
        return (
            '<HashMapMapping[{uuid}]: type={map_type} {value}={cost}, tenant={tenant}>'
            .format(uuid=self.mapping_id, map_type=self.map_type, value=
            self.value, cost=self.cost, tenant=self.tenant_id))


class HashMapThreshold(Base, HashMapBase):
    """A threshold matching a service or a field with a level and a type.

    Used to model final equation.
    """
    __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', 'tenant_id',
            name='uniq_field_threshold'), schema.UniqueConstraint('level',
            'service_id', 'tenant_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(40, 28), nullable=False)
    map_type = sqlalchemy.Column(sqlalchemy.Enum('flat', 'rate', name=
        'enum_hashmap_type', create_constraint=True), 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)
    tenant_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)

    def __repr__(self):
        return (
            '<HashMapThreshold[{uuid}]: type={map_type} {level}={cost}, tenant={tenant}>'
            .format(uuid=self.threshold_id, map_type=self.map_type, level=
            self.level, cost=self.cost, tenant=self.tenant_id))

<mask token>


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):
    """A hashmap service.

    Used to describe a CloudKitty service such as compute or volume.
    """
    __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):
    """A hashmap field.

    Used to describe a service metadata such as flavor_id or image_id for
    compute.
    """
    __tablename__ = 'hashmap_fields'
    fk_to_resolve = {'service_id': 'service.service_id'}

    @declarative.declared_attr
    def __table_args__(cls):
        args = schema.UniqueConstraint('service_id', 'name', name=
            'uniq_field_per_service'), 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.

    Used to group multiple mappings or thresholds into a single calculation.
    """
    __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 or service, a value and a type.

    Used to model final equation.
    """
    __tablename__ = 'hashmap_mappings'
    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', 'tenant_id',
            name='uniq_field_mapping'), schema.UniqueConstraint('value',
            'service_id', 'tenant_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(40, 28), nullable=False)
    map_type = sqlalchemy.Column(sqlalchemy.Enum('flat', 'rate', name=
        'enum_map_type', create_constraint=True), 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)
    tenant_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)

    def __repr__(self):
        return (
            '<HashMapMapping[{uuid}]: type={map_type} {value}={cost}, tenant={tenant}>'
            .format(uuid=self.mapping_id, map_type=self.map_type, value=
            self.value, cost=self.cost, tenant=self.tenant_id))


class HashMapThreshold(Base, HashMapBase):
    """A threshold matching a service or a field with a level and a type.

    Used to model final equation.
    """
    __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', 'tenant_id',
            name='uniq_field_threshold'), schema.UniqueConstraint('level',
            'service_id', 'tenant_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(40, 28), nullable=False)
    map_type = sqlalchemy.Column(sqlalchemy.Enum('flat', 'rate', name=
        'enum_hashmap_type', create_constraint=True), 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)
    tenant_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)

    def __repr__(self):
        return (
            '<HashMapThreshold[{uuid}]: type={map_type} {level}={cost}, tenant={tenant}>'
            .format(uuid=self.threshold_id, map_type=self.map_type, level=
            self.level, cost=self.cost, tenant=self.tenant_id))

# -*- 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.
#
from oslo_db.sqlalchemy import models
import sqlalchemy
from sqlalchemy.ext import declarative
from sqlalchemy import orm
from sqlalchemy import schema

from cloudkitty.common.db import models as ck_models

Base = ck_models.get_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):
    """A hashmap service.

    Used to describe a CloudKitty service such as compute or volume.
    """
    __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):
    """A hashmap field.

    Used to describe a service metadata such as flavor_id or image_id for
    compute.
    """
    __tablename__ = 'hashmap_fields'
    fk_to_resolve = {
        'service_id': 'service.service_id'}

    @declarative.declared_attr
    def __table_args__(cls):
        args = (
            schema.UniqueConstraint(
                'service_id',
                'name',
                name='uniq_field_per_service'),
            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.

    Used to group multiple mappings or thresholds into a single calculation.
    """
    __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 or service, a value and a type.

    Used to model final equation.
    """
    __tablename__ = 'hashmap_mappings'
    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',
                'tenant_id',
                name='uniq_field_mapping'),
            schema.UniqueConstraint(
                'value',
                'service_id',
                'tenant_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(40, 28),
        nullable=False)
    map_type = sqlalchemy.Column(
        sqlalchemy.Enum(
            'flat',
            'rate',
            name='enum_map_type', create_constraint=True),
        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)
    tenant_id = sqlalchemy.Column(
        sqlalchemy.String(255),
        nullable=True)

    def __repr__(self):
        return ('<HashMapMapping[{uuid}]: '
                'type={map_type} {value}={cost}, '
                'tenant={tenant}>').format(
                    uuid=self.mapping_id,
                    map_type=self.map_type,
                    value=self.value,
                    cost=self.cost,
                    tenant=self.tenant_id)


class HashMapThreshold(Base, HashMapBase):
    """A threshold matching a service or a field with a level and a type.

    Used to model final equation.
    """
    __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',
                'tenant_id',
                name='uniq_field_threshold'),
            schema.UniqueConstraint(
                'level',
                'service_id',
                'tenant_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(40, 28),
        nullable=False)
    map_type = sqlalchemy.Column(
        sqlalchemy.Enum(
            'flat',
            'rate',
            name='enum_hashmap_type', create_constraint=True),
        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)
    tenant_id = sqlalchemy.Column(
        sqlalchemy.String(255),
        nullable=True)

    def __repr__(self):
        return ('<HashMapThreshold[{uuid}]: '
                'type={map_type} {level}={cost}, '
                'tenant={tenant}>').format(
                    uuid=self.threshold_id,
                    map_type=self.map_type,
                    level=self.level,
                    cost=self.cost,
                    tenant=self.tenant_id)
