# Copyright (c) 2013 Mirantis, Inc. # # 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. """ SQLAlchemy models for murano data """ from oslo_db.sqlalchemy import models from oslo_utils import timeutils import sqlalchemy as sa from sqlalchemy.ext import declarative from sqlalchemy import orm as sa_orm from murano.common import uuidutils from murano.db.sqla import types as st class TimestampMixin(object): __protected_attributes__ = set(["created", "updated"]) created = sa.Column(sa.DateTime, default=timeutils.utcnow, nullable=False) updated = sa.Column(sa.DateTime, default=timeutils.utcnow, nullable=False, onupdate=timeutils.utcnow) def update(self, values): """dict.update() behaviour.""" self.updated = timeutils.utcnow() super(TimestampMixin, self).update(values) def __setitem__(self, key, value): self.updated = timeutils.utcnow() super(TimestampMixin, self).__setitem__(key, value) class _MuranoBase(models.ModelBase): def to_dict(self): dictionary = self.__dict__.copy() return dict((k, v) for k, v in dictionary.iteritems() if k != '_sa_instance_state') Base = declarative.declarative_base(cls=_MuranoBase) class Environment(Base, TimestampMixin): """Represents a Environment in the metadata-store.""" __tablename__ = 'environment' __table_args__ = (sa.Index( 'ix_name_tenant_id', 'name', 'tenant_id', unique=True),) id = sa.Column(sa.String(255), primary_key=True, default=uuidutils.generate_uuid) name = sa.Column(sa.String(255), nullable=False) tenant_id = sa.Column(sa.String(36), nullable=False) version = sa.Column(sa.BigInteger, nullable=False, default=0) description = sa.Column(st.JsonBlob(), nullable=False, default={}) sessions = sa_orm.relationship("Session", backref='environment', cascade='save-update, merge, delete') tasks = sa_orm.relationship('Task', backref='environment', cascade='save-update, merge, delete') cf_spaces = sa_orm.relationship("CFSpace", backref='environment', cascade='save-update, merge, delete') cf_serv_inst = sa_orm.relationship("CFServiceInstance", backref='environment', cascade='save-update, merge, delete') def to_dict(self): dictionary = super(Environment, self).to_dict() del dictionary['description'] return dictionary class EnvironmentTemplate(Base, TimestampMixin): """Represents a Environment emplate in the metadata-store.""" __tablename__ = 'environment-template' id = sa.Column(sa.String(36), primary_key=True, default=uuidutils.generate_uuid) name = sa.Column(sa.String(255), nullable=False) tenant_id = sa.Column(sa.String(36), nullable=False) version = sa.Column(sa.BigInteger, nullable=False, default=0) description = sa.Column(st.JsonBlob(), nullable=False, default={}) def to_dict(self): dictionary = super(EnvironmentTemplate, self).to_dict() if 'description' in dictionary: del dictionary['description'] return dictionary class Session(Base, TimestampMixin): __tablename__ = 'session' id = sa.Column(sa.String(36), primary_key=True, default=uuidutils.generate_uuid) environment_id = sa.Column(sa.String(255), sa.ForeignKey('environment.id')) user_id = sa.Column(sa.String(36), nullable=False) state = sa.Column(sa.String(36), nullable=False) description = sa.Column(st.JsonBlob(), nullable=False) version = sa.Column(sa.BigInteger, nullable=False, default=0) def to_dict(self): dictionary = super(Session, self).to_dict() del dictionary['description'] # object relations may be not loaded yet if 'environment' in dictionary: del dictionary['environment'] return dictionary class Task(Base, TimestampMixin): __tablename__ = 'task' id = sa.Column(sa.String(36), primary_key=True, default=uuidutils.generate_uuid) started = sa.Column(sa.DateTime, default=timeutils.utcnow, nullable=False) finished = sa.Column(sa.DateTime, default=None, nullable=True) description = sa.Column(st.JsonBlob(), nullable=False) environment_id = sa.Column(sa.String(255), sa.ForeignKey('environment.id')) action = sa.Column(st.JsonBlob()) statuses = sa_orm.relationship("Status", backref='task', cascade='save-update, merge, delete') result = sa.Column(st.JsonBlob(), nullable=True, default={}) def to_dict(self): dictionary = super(Task, self).to_dict() if 'statuses' in dictionary: del dictionary['statuses'] if 'environment' in dictionary: del dictionary['environment'] return dictionary class Status(Base, TimestampMixin): __tablename__ = 'status' id = sa.Column(sa.String(36), primary_key=True, default=uuidutils.generate_uuid) entity_id = sa.Column(sa.String(255), nullable=True) entity = sa.Column(sa.String(10), nullable=True) task_id = sa.Column(sa.String(32), sa.ForeignKey('task.id')) text = sa.Column(sa.Text(), nullable=False) level = sa.Column(sa.String(32), nullable=False) details = sa.Column(sa.Text(), nullable=True) def to_dict(self): dictionary = super(Status, self).to_dict() # object relations may be not loaded yet if 'deployment' in dictionary: del dictionary['deployment'] return dictionary class ApiStats(Base, TimestampMixin): __tablename__ = 'apistats' id = sa.Column(sa.Integer(), primary_key=True) host = sa.Column(sa.String(80)) request_count = sa.Column(sa.BigInteger()) error_count = sa.Column(sa.BigInteger()) average_response_time = sa.Column(sa.Float()) requests_per_tenant = sa.Column(sa.Text()) requests_per_second = sa.Column(sa.Float()) errors_per_second = sa.Column(sa.Float()) cpu_count = sa.Column(sa.Integer()) cpu_percent = sa.Column(sa.Float()) package_to_category = sa.Table('package_to_category', Base.metadata, sa.Column('package_id', sa.String(36), sa.ForeignKey('package.id')), sa.Column('category_id', sa.String(36), sa.ForeignKey('category.id', ondelete="RESTRICT"))) package_to_tag = sa.Table('package_to_tag', Base.metadata, sa.Column('package_id', sa.String(36), sa.ForeignKey('package.id')), sa.Column('tag_id', sa.String(36), sa.ForeignKey('tag.id', ondelete="CASCADE"))) class Instance(Base): __tablename__ = 'instance_stats' environment_id = sa.Column( sa.String(255), primary_key=True, nullable=False) instance_id = sa.Column( sa.String(255), primary_key=True, nullable=False) instance_type = sa.Column(sa.Integer, default=0, nullable=False) created = sa.Column(sa.Integer, nullable=False) destroyed = sa.Column(sa.Integer, nullable=True) type_name = sa.Column('type_name', sa.String(512), nullable=False) type_title = sa.Column('type_title', sa.String(512)) unit_count = sa.Column('unit_count', sa.Integer()) tenant_id = sa.Column('tenant_id', sa.String(36), nullable=False) class Package(Base, TimestampMixin): """Represents a meta information about application package.""" __tablename__ = 'package' __table_args__ = (sa.Index('ix_package_fqn_and_owner', 'fully_qualified_name', 'owner_id', unique=True),) id = sa.Column(sa.String(36), primary_key=True, default=uuidutils.generate_uuid) archive = sa.Column(st.LargeBinary()) fully_qualified_name = sa.Column(sa.String(128), nullable=False) type = sa.Column(sa.String(20), nullable=False, default='class') author = sa.Column(sa.String(80), default='Openstack') supplier = sa.Column(st.JsonBlob(), nullable=True, default={}) name = sa.Column(sa.String(80), nullable=False) enabled = sa.Column(sa.Boolean, default=True) description = sa.Column(sa.Text(), nullable=False, default='The description is not provided') is_public = sa.Column(sa.Boolean, default=False) tags = sa_orm.relationship("Tag", secondary=package_to_tag, cascade='save-update, merge', lazy='joined') logo = sa.Column(st.LargeBinary(), nullable=True) owner_id = sa.Column(sa.String(36), nullable=False) ui_definition = sa.Column(sa.Text) supplier_logo = sa.Column(sa.LargeBinary, nullable=True) categories = sa_orm.relationship("Category", secondary=package_to_category, cascade='save-update, merge', lazy='joined') class_definitions = sa_orm.relationship( "Class", cascade='save-update, merge, delete', lazy='joined', backref='package') def to_dict(self): d = self.__dict__.copy() not_serializable = ['_sa_instance_state', 'archive', 'logo', 'ui_definition', 'supplier_logo'] nested_objects = ['categories', 'tags', 'class_definitions'] for key in not_serializable: if key in d.keys(): del d[key] for key in nested_objects: d[key] = [a.name for a in d.get(key, [])] return d class Category(Base, TimestampMixin): """Represents an application categories in the datastore.""" __tablename__ = 'category' id = sa.Column(sa.String(36), primary_key=True, default=uuidutils.generate_uuid) name = sa.Column(sa.String(80), nullable=False, index=True, unique=True) package_count = sa_orm.column_property( sa.select([sa.func.count(package_to_category.c.package_id)]). where(package_to_category.c.category_id == id). correlate_except(package_to_category) ) def to_dict(self): d = super(Category, self).to_dict() d['package_count'] = self.package_count return d class Tag(Base, TimestampMixin): """Represents tags in the datastore.""" __tablename__ = 'tag' id = sa.Column(sa.String(36), primary_key=True, default=uuidutils.generate_uuid) name = sa.Column(sa.String(80), nullable=False, unique=True) class Class(Base, TimestampMixin): """Represents a class definition in the datastore.""" __tablename__ = 'class_definition' id = sa.Column(sa.String(36), primary_key=True, default=uuidutils.generate_uuid) name = sa.Column(sa.String(128), nullable=False, index=True) package_id = sa.Column(sa.String(36), sa.ForeignKey('package.id')) class Lock(Base): __tablename__ = 'locks' id = sa.Column(sa.String(50), primary_key=True) ts = sa.Column(sa.DateTime, nullable=False) class CFOrganization(Base): __tablename__ = "cf_orgs" id = sa.Column(sa.String(255), primary_key=True) tenant = sa.Column(sa.String(255), nullable=False) class CFSpace(Base): __tablename__ = "cf_spaces" id = sa.Column(sa.String(255), primary_key=True) environment_id = sa.Column(sa.String(255), sa.ForeignKey('environment.id'), nullable=False) def to_dict(self): dictionary = super(CFSpace, self).to_dict() if 'environment' in dictionary: del dictionary['environment'] return dictionary class CFServiceInstance(Base): __tablename__ = 'cf_serv_inst' id = sa.Column(sa.String(255), primary_key=True) service_id = sa.Column(sa.String(255), nullable=False) environment_id = sa.Column(sa.String(255), sa.ForeignKey('environment.id'), nullable=False) tenant = sa.Column(sa.String(255), nullable=False) def to_dict(self): dictionary = super(CFSpace, self).to_dict() if 'environment' in dictionary: del dictionary['environment'] return dictionary def register_models(engine): """Creates database tables for all models with the given engine.""" models = (Environment, Status, Session, Task, ApiStats, Package, Category, Class, Instance, Lock, CFSpace, CFOrganization) for model in models: model.metadata.create_all(engine) def unregister_models(engine): """Drops database tables for all models with the given engine.""" models = (Environment, Status, Session, Task, ApiStats, Package, Category, Class, Lock, CFOrganization, CFSpace) for model in models: model.metadata.drop_all(engine)