Skip to content

Commit

Permalink
Add the sqlalchemy implementation of the alarms collection.
Browse files Browse the repository at this point in the history
blueprint alarm-api
Change-Id: Id8f00b1cb7519ca59f277170fcb03e0976a4fb1a
  • Loading branch information
asalkeld committed May 10, 2013
1 parent 0eefaf3 commit 2c84007
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 7 deletions.
71 changes: 66 additions & 5 deletions ceilometer/storage/impl_sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@

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
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__)
Expand Down Expand Up @@ -412,18 +413,78 @@ def get_meter_statistics(self, sample_filter, period=None):
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()
Original file line number Diff line number Diff line change
@@ -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()
39 changes: 37 additions & 2 deletions ceilometer/storage/sqlalchemy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
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
Expand Down Expand Up @@ -74,6 +74,12 @@ def __setitem__(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)

Expand Down Expand Up @@ -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)
4 changes: 4 additions & 0 deletions tests/storage/test_impl_sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

0 comments on commit 2c84007

Please sign in to comment.