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 from __future__ import absolute_import
import copy
import os import os
import uuid
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy.orm import exc
from ceilometer.openstack.common import log from ceilometer.openstack.common import log
from ceilometer.openstack.common import timeutils 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 import models as api_models
from ceilometer.storage.sqlalchemy import migration from ceilometer.storage.sqlalchemy import migration
from ceilometer.storage.sqlalchemy.models import Meter, Project, Resource 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 import ceilometer.storage.sqlalchemy.session as sqlalchemy_session
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -412,18 +413,78 @@ class Connection(base.Connection):
period_end=period_end, 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, def get_alarms(self, name=None, user=None,
project=None, enabled=True, alarm_id=None): project=None, enabled=True, alarm_id=None):
"""Yields a lists of alarms that match filters """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): def update_alarm(self, alarm):
"""update 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): def delete_alarm(self, alarm_id):
"""Delete a alarm """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 import urlparse
from oslo.config import cfg from oslo.config import cfg
from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime, \ from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime
Float from sqlalchemy import Float, Boolean, Text
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.types import TypeDecorator, VARCHAR from sqlalchemy.types import TypeDecorator, VARCHAR
@ -74,6 +74,12 @@ class CeilometerBase(object):
def __getitem__(self, key): def __getitem__(self, key):
return getattr(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) Base = declarative_base(cls=CeilometerBase)
@ -139,3 +145,32 @@ class Resource(Base):
user_id = Column(String(255), ForeignKey('user.id')) user_id = Column(String(255), ForeignKey('user.id'))
project_id = Column(String(255), ForeignKey('project.id')) project_id = Column(String(255), ForeignKey('project.id'))
meters = relationship("Meter", backref='resource') 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 pass
class AlarmTest(base.AlarmTest, SQLAlchemyEngineTestBase):
pass
def test_model_table_args(): def test_model_table_args():
cfg.CONF.database_connection = 'mysql://localhost' cfg.CONF.database_connection = 'mysql://localhost'
assert table_args() assert table_args()