Add the sqlalchemy implementation of the alarms collection.

blueprint alarm-api
Change-Id: Id8f00b1cb7519ca59f277170fcb03e0976a4fb1a
This commit is contained in:
Angus Salkeld 2013-05-08 10:47:34 +10:00
parent 0eefaf3c88
commit 2c84007b34
4 changed files with 163 additions and 7 deletions

View File

@ -19,9 +19,10 @@
from __future__ import absolute_import
import copy
import os
import uuid
from sqlalchemy import func
from sqlalchemy.orm import exc
from ceilometer.openstack.common import log
from ceilometer.openstack.common import timeutils
@ -29,7 +30,7 @@ from ceilometer.storage import base
from ceilometer.storage import models as api_models
from ceilometer.storage.sqlalchemy import migration
from ceilometer.storage.sqlalchemy.models import Meter, Project, Resource
from ceilometer.storage.sqlalchemy.models import Source, User, Base
from ceilometer.storage.sqlalchemy.models import Source, User, Base, Alarm
import ceilometer.storage.sqlalchemy.session as sqlalchemy_session
LOG = log.getLogger(__name__)
@ -412,18 +413,78 @@ class Connection(base.Connection):
period_end=period_end,
)
def _row_to_alarm_model(self, row):
return api_models.Alarm(alarm_id=row.id,
enabled=row.enabled,
name=row.name,
description=row.description,
timestamp=row.timestamp,
counter_name=row.counter_name,
user_id=row.user_id,
project_id=row.project_id,
comparison_operator=row.comparison_operator,
threshold=row.threshold,
statistic=row.statistic,
evaluation_periods=row.evaluation_periods,
period=row.period,
state=row.state,
state_timestamp=row.state_timestamp,
ok_actions=row.ok_actions,
alarm_actions=row.alarm_actions,
insufficient_data_actions=
row.insufficient_data_actions,
matching_metadata=row.matching_metadata)
def _alarm_model_to_row(self, alarm, row=None):
if row is None:
row = Alarm(id=str(uuid.uuid1()))
row.update(alarm.as_dict())
return row
def get_alarms(self, name=None, user=None,
project=None, enabled=True, alarm_id=None):
"""Yields a lists of alarms that match filters
:param user: Optional ID for user that owns the resource.
:param project: Optional ID for project that owns the resource.
:param enabled: Optional boolean to list disable alarm.
:param alarm_id: Optional alarm_id to return one alarm.
"""
raise NotImplementedError('Alarms not implemented')
query = self.session.query(Alarm)
if name is not None:
query = query.filter(Alarm.name == name)
if enabled is not None:
query = query.filter(Alarm.enabled == enabled)
if user is not None:
query = query.filter(Alarm.user_id == user)
if project is not None:
query = query.filter(Alarm.project_id == project)
if alarm_id is not None:
query = query.filter(Alarm.id == alarm_id)
return (self._row_to_alarm_model(x) for x in query.all())
def update_alarm(self, alarm):
"""update alarm
:param alarm: the new Alarm to update
"""
raise NotImplementedError('Alarms not implemented')
if alarm.alarm_id:
alarm_row = self.session.merge(Alarm(id=alarm.alarm_id))
self._alarm_model_to_row(alarm, alarm_row)
else:
self.session.merge(User(id=alarm.user_id))
self.session.merge(Project(id=alarm.project_id))
alarm_row = self._alarm_model_to_row(alarm)
self.session.add(alarm_row)
self.session.flush()
return self._row_to_alarm_model(alarm_row)
def delete_alarm(self, alarm_id):
"""Delete a alarm
:param alarm_id: ID of the alarm to delete
"""
raise NotImplementedError('Alarms not implemented')
self.session.query(Alarm).filter(Alarm.id == alarm_id).delete()
self.session.flush()

View File

@ -0,0 +1,56 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 eNovance <licensing@enovance.com>
# Copyright © 2013 Red Hat, Inc.
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
# Angus Salkeld <asalkeld@redhat.com>
#
# 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 sqlalchemy import MetaData, Table, Column, Text
from sqlalchemy import Boolean, Integer, String, DateTime, Float
meta = MetaData()
alarm = Table(
'alarm', meta,
Column('id', String(255), primary_key=True, index=True),
Column('enabled', Boolean),
Column('name', Text()),
Column('description', Text()),
Column('timestamp', DateTime(timezone=False)),
Column('counter_name', String(255), index=True),
Column('user_id', String(255), index=True),
Column('project_id', String(255), index=True),
Column('comparison_operator', String(2)),
Column('threshold', Float),
Column('statistic', String(255)),
Column('evaluation_periods', Integer),
Column('period', Integer),
Column('state', String(255)),
Column('state_timestamp', DateTime(timezone=False)),
Column('ok_actions', Text()),
Column('alarm_actions', Text()),
Column('insufficient_data_actions', Text()),
Column('matching_metadata', Text()))
def upgrade(migrate_engine):
meta.bind = migrate_engine
alarm.create()
def downgrade(migrate_engine):
meta.bind = migrate_engine
alarm.drop()

View File

@ -22,8 +22,8 @@ import json
import urlparse
from oslo.config import cfg
from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime, \
Float
from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime
from sqlalchemy import Float, Boolean, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.types import TypeDecorator, VARCHAR
@ -74,6 +74,12 @@ class CeilometerBase(object):
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)
@ -139,3 +145,32 @@ class Resource(Base):
user_id = Column(String(255), ForeignKey('user.id'))
project_id = Column(String(255), ForeignKey('project.id'))
meters = relationship("Meter", backref='resource')
class Alarm(Base):
"""Alarm data"""
__tablename__ = 'alarm'
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(Text)
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)

View File

@ -61,6 +61,10 @@ class CounterDataTypeTest(base.CounterDataTypeTest, SQLAlchemyEngineTestBase):
pass
class AlarmTest(base.AlarmTest, SQLAlchemyEngineTestBase):
pass
def test_model_table_args():
cfg.CONF.database_connection = 'mysql://localhost'
assert table_args()