# -*- encoding: utf-8 -*- # # Author: John Tran # # 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 Ceilometer data. """ import json import urlparse from oslo.config import cfg from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime, \ Index from sqlalchemy import Float, Boolean, Text from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import backref from sqlalchemy.orm import relationship from sqlalchemy.types import TypeDecorator, VARCHAR from ceilometer.openstack.common import timeutils from ceilometer.storage import models as api_models from ceilometer import utils sql_opts = [ cfg.StrOpt('mysql_engine', default='InnoDB', help='MySQL engine') ] cfg.CONF.register_opts(sql_opts) def table_args(): engine_name = urlparse.urlparse(cfg.CONF.database.connection).scheme if engine_name == 'mysql': return {'mysql_engine': cfg.CONF.mysql_engine, 'mysql_charset': "utf8"} return None class JSONEncodedDict(TypeDecorator): "Represents an immutable structure as a json-encoded string." impl = VARCHAR def process_bind_param(self, value, dialect): if value is not None: value = json.dumps(value) return value def process_result_value(self, value, dialect): if value is not None: value = json.loads(value) return value class CeilometerBase(object): """Base class for Ceilometer Models.""" __table_args__ = table_args() __table_initialized__ = False def __setitem__(self, key, value): setattr(self, key, value) def __getitem__(self, key): return getattr(self, key) def update(self, values): """Make the model object behave like a dict.""" for k, v in values.iteritems(): setattr(self, k, v) Base = declarative_base(cls=CeilometerBase) sourceassoc = Table('sourceassoc', Base.metadata, Column('meter_id', Integer, ForeignKey("meter.id")), Column('project_id', String(255), ForeignKey("project.id")), Column('resource_id', String(255), ForeignKey("resource.id")), Column('user_id', String(255), ForeignKey("user.id")), Column('source_id', String(255), ForeignKey("source.id"))) class Source(Base): __tablename__ = 'source' id = Column(String(255), primary_key=True) class Meter(Base): """Metering data.""" __tablename__ = 'meter' __table_args__ = ( Index('ix_meter_timestamp', 'timestamp'), Index('ix_meter_user_id', 'user_id'), Index('ix_meter_project_id', 'project_id'), Index('idx_meter_rid_cname', 'resource_id', 'counter_name'), ) id = Column(Integer, primary_key=True) counter_name = Column(String(255)) sources = relationship("Source", secondary=lambda: sourceassoc) user_id = Column(String(255), ForeignKey('user.id')) project_id = Column(String(255), ForeignKey('project.id')) resource_id = Column(String(255), ForeignKey('resource.id')) resource_metadata = Column(JSONEncodedDict) counter_type = Column(String(255)) counter_unit = Column(String(255)) counter_volume = Column(Float(53)) timestamp = Column(DateTime, default=timeutils.utcnow) message_signature = Column(String(1000)) message_id = Column(String(1000)) class User(Base): __tablename__ = 'user' id = Column(String(255), primary_key=True) sources = relationship("Source", secondary=lambda: sourceassoc) resources = relationship("Resource", backref='user') meters = relationship("Meter", backref='user') class Project(Base): __tablename__ = 'project' id = Column(String(255), primary_key=True) sources = relationship("Source", secondary=lambda: sourceassoc) resources = relationship("Resource", backref='project') meters = relationship("Meter", backref='project') class Resource(Base): __tablename__ = 'resource' __table_ards__ = ( Index('ix_resource_project_id', 'project_id'), Index('ix_resource_user_id', 'user_id'), ) id = Column(String(255), primary_key=True) sources = relationship("Source", secondary=lambda: sourceassoc) resource_metadata = Column(JSONEncodedDict) user_id = Column(String(255), ForeignKey('user.id')) project_id = Column(String(255), ForeignKey('project.id')) meters = relationship("Meter", backref='resource') class Alarm(Base): """Define Alarm data.""" __tablename__ = 'alarm' __table_ards__ = ( Index('ix_alarm_user_id', 'user_id'), Index('ix_alarm_project_id', 'project_id'), Index('ix_alarm_counter_name', 'counter_name'), ) id = Column(String(255), primary_key=True) enabled = Column(Boolean) name = Column(Text) description = Column(Text) timestamp = Column(DateTime, default=timeutils.utcnow) counter_name = Column(String(255)) user_id = Column(String(255), ForeignKey('user.id')) project_id = Column(String(255), ForeignKey('project.id')) comparison_operator = Column(String(2)) threshold = Column(Float) statistic = Column(String(255)) evaluation_periods = Column(Integer) period = Column(Integer) state = Column(String(255)) state_timestamp = Column(DateTime, default=timeutils.utcnow) ok_actions = Column(JSONEncodedDict) alarm_actions = Column(JSONEncodedDict) insufficient_data_actions = Column(JSONEncodedDict) matching_metadata = Column(JSONEncodedDict) class UniqueName(Base): """Key names should only be stored once. """ __tablename__ = 'unique_name' id = Column(Integer, primary_key=True) key = Column(String(255), index=True, unique=True) def __init__(self, key): self.key = key def __repr__(self): return "" % self.key class Event(Base): __tablename__ = 'event' __table_args__ = ( Index('unique_name_id', 'unique_name_id'), Index('ix_event_generated', 'generated'), ) id = Column(Integer, primary_key=True) generated = Column(Float(asdecimal=True)) unique_name_id = Column(Integer, ForeignKey('unique_name.id')) unique_name = relationship("UniqueName", backref=backref('unique_name', order_by=id)) def __init__(self, event, generated): self.unique_name = event self.generated = generated def __repr__(self): return "" % \ (self.id, self.unique_name, self.generated) class Trait(Base): __tablename__ = 'trait' id = Column(Integer, primary_key=True) name_id = Column(Integer, ForeignKey('unique_name.id')) name = relationship("UniqueName", backref=backref('name', order_by=id)) t_type = Column(Integer, index=True) t_string = Column(String(255), nullable=True, default=None, index=True) t_float = Column(Float, nullable=True, default=None, index=True) t_int = Column(Integer, nullable=True, default=None, index=True) t_datetime = Column(Float(asdecimal=True), nullable=True, default=None, index=True) event_id = Column(Integer, ForeignKey('event.id')) event = relationship("Event", backref=backref('event', order_by=id)) _value_map = {api_models.Trait.TEXT_TYPE: 't_string', api_models.Trait.FLOAT_TYPE: 't_float', api_models.Trait.INT_TYPE: 't_int', api_models.Trait.DATETIME_TYPE: 't_datetime'} def __init__(self, name, event, t_type, t_string=None, t_float=None, t_int=None, t_datetime=None): self.name = name self.t_type = t_type self.t_string = t_string self.t_float = t_float self.t_int = t_int self.t_datetime = t_datetime self.event = event def get_value(self): if self.t_type == api_models.Trait.INT_TYPE: return self.t_int if self.t_type == api_models.Trait.FLOAT_TYPE: return self.t_float if self.t_type == api_models.Trait.DATETIME_TYPE: return utils.decimal_to_dt(self.t_datetime) if self.t_type == api_models.Trait.TEXT_TYPE: return self.t_string return None def __repr__(self): return "" % (self.name, self.t_type, self.t_string, self.t_float, self.t_int, self.t_datetime, self.event)