f15a814107
This commit adds v2/event_types, v2/events and v2/events/*/traits. Currently, the event api is limited to admin users, but is configurable in the policy.json file. All users that are able to access the event api can view all events in the system, it is not restricted per project id. Operators other than 'eq' have not yet been implemented. This change re-names the parameters in the event_filter to class so they are more end-user friendly. implements bp specify-event-api Change-Id: I0ccd4ef81cac5f8f57a450dc980ea85ff8fe89ec
389 lines
14 KiB
Python
389 lines
14 KiB
Python
# -*- encoding: utf-8 -*-
|
|
#
|
|
# Copyright © 2013 New Dream Network, LLC (DreamHost)
|
|
#
|
|
# Author: Doug Hellmann <doug.hellmann@dreamhost.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.
|
|
"""Model classes for use in the storage API.
|
|
"""
|
|
|
|
from ceilometer.openstack.common import timeutils
|
|
|
|
|
|
class Model(object):
|
|
"""Base class for storage API models.
|
|
"""
|
|
|
|
def __init__(self, **kwds):
|
|
self.fields = list(kwds)
|
|
for k, v in kwds.iteritems():
|
|
setattr(self, k, v)
|
|
|
|
def as_dict(self):
|
|
d = {}
|
|
for f in self.fields:
|
|
v = getattr(self, f)
|
|
if isinstance(v, Model):
|
|
v = v.as_dict()
|
|
elif isinstance(v, list) and v and isinstance(v[0], Model):
|
|
v = [sub.as_dict() for sub in v]
|
|
d[f] = v
|
|
return d
|
|
|
|
def __eq__(self, other):
|
|
return self.as_dict() == other.as_dict()
|
|
|
|
|
|
class Event(Model):
|
|
"""A raw event from the source system. Events have Traits.
|
|
|
|
Metrics will be derived from one or more Events.
|
|
"""
|
|
|
|
DUPLICATE = 1
|
|
UNKNOWN_PROBLEM = 2
|
|
|
|
def __init__(self, message_id, event_type, generated, traits):
|
|
"""Create a new event.
|
|
|
|
:param message_id: Unique ID for the message this event
|
|
stemmed from. This is different than
|
|
the Event ID, which comes from the
|
|
underlying storage system.
|
|
:param event_type: The type of the event.
|
|
:param generated: UTC time for when the event occured.
|
|
:param traits: list of Traits on this Event.
|
|
"""
|
|
Model.__init__(self, message_id=message_id, event_type=event_type,
|
|
generated=generated, traits=traits)
|
|
|
|
def append_trait(self, trait_model):
|
|
self.traits.append(trait_model)
|
|
|
|
def __repr__(self):
|
|
trait_list = []
|
|
if self.traits:
|
|
trait_list = [str(trait) for trait in self.traits]
|
|
return "<Event: %s, %s, %s, %s>" % \
|
|
(self.message_id, self.event_type, self.generated,
|
|
" ".join(trait_list))
|
|
|
|
|
|
class Trait(Model):
|
|
"""A Trait is a key/value pair of data on an Event. The value is variant
|
|
record of basic data types (int, date, float, etc).
|
|
"""
|
|
|
|
NONE_TYPE = 0
|
|
TEXT_TYPE = 1
|
|
INT_TYPE = 2
|
|
FLOAT_TYPE = 3
|
|
DATETIME_TYPE = 4
|
|
|
|
type_names = {
|
|
NONE_TYPE: "none",
|
|
TEXT_TYPE: "string",
|
|
INT_TYPE: "integer",
|
|
FLOAT_TYPE: "float",
|
|
DATETIME_TYPE: "datetime"
|
|
}
|
|
|
|
def __init__(self, name, dtype, value):
|
|
if not dtype:
|
|
dtype = Trait.NONE_TYPE
|
|
Model.__init__(self, name=name, dtype=dtype, value=value)
|
|
|
|
def __repr__(self):
|
|
return "<Trait: %s %d %s>" % (self.name, self.dtype, self.value)
|
|
|
|
def get_type_name(self):
|
|
return self.get_name_by_type(self.dtype)
|
|
|
|
@classmethod
|
|
def get_type_by_name(cls, type_name):
|
|
return getattr(cls, '%s_TYPE' % type_name.upper(), None)
|
|
|
|
@classmethod
|
|
def get_type_names(cls):
|
|
return cls.type_names.values()
|
|
|
|
@classmethod
|
|
def get_name_by_type(cls, type_id):
|
|
return cls.type_names.get(type_id, "none")
|
|
|
|
@classmethod
|
|
def convert_value(cls, trait_type, value):
|
|
if trait_type is cls.INT_TYPE:
|
|
return int(value)
|
|
if trait_type is cls.FLOAT_TYPE:
|
|
return float(value)
|
|
if trait_type is cls.DATETIME_TYPE:
|
|
return timeutils.normalize_time(timeutils.parse_isotime(value))
|
|
return str(value)
|
|
|
|
|
|
class Resource(Model):
|
|
"""Something for which sample data has been collected.
|
|
"""
|
|
|
|
def __init__(self, resource_id, project_id,
|
|
first_sample_timestamp,
|
|
last_sample_timestamp,
|
|
source, user_id, metadata,
|
|
meter):
|
|
"""Create a new resource.
|
|
|
|
:param resource_id: UUID of the resource
|
|
:param project_id: UUID of project owning the resource
|
|
:param first_sample_timestamp: first sample timestamp captured
|
|
:param last_sample_timestamp: last sample timestamp captured
|
|
:param source: the identifier for the user/project id definition
|
|
:param user_id: UUID of user owning the resource
|
|
:param metadata: most current metadata for the resource (a dict)
|
|
:param meter: list of the meters reporting data for the resource,
|
|
"""
|
|
Model.__init__(self,
|
|
resource_id=resource_id,
|
|
first_sample_timestamp=first_sample_timestamp,
|
|
last_sample_timestamp=last_sample_timestamp,
|
|
project_id=project_id,
|
|
source=source,
|
|
user_id=user_id,
|
|
metadata=metadata,
|
|
meter=meter,
|
|
)
|
|
|
|
|
|
class ResourceMeter(Model):
|
|
"""The definitions of the meters for which data has been collected
|
|
for a resource.
|
|
|
|
See Resource.meter field.
|
|
"""
|
|
|
|
def __init__(self, counter_name, counter_type, counter_unit):
|
|
"""Create a new resource meter.
|
|
|
|
:param counter_name: the name of the counter updating the resource
|
|
:param counter_type: one of gauge, delta, cumulative
|
|
:param counter_unit: official units name for the sample data
|
|
"""
|
|
Model.__init__(self,
|
|
counter_name=counter_name,
|
|
counter_type=counter_type,
|
|
counter_unit=counter_unit,
|
|
)
|
|
|
|
|
|
class Meter(Model):
|
|
"""Definition of a meter for which sample data has been collected.
|
|
"""
|
|
|
|
def __init__(self, name, type, unit, resource_id, project_id, source,
|
|
user_id):
|
|
"""Create a new meter.
|
|
|
|
:param name: name of the meter
|
|
:param type: type of the meter (guage, counter)
|
|
:param unit: unit of the meter
|
|
:param resource_id: UUID of the resource
|
|
:param project_id: UUID of project owning the resource
|
|
:param source: the identifier for the user/project id definition
|
|
:param user_id: UUID of user owning the resource
|
|
"""
|
|
Model.__init__(self,
|
|
name=name,
|
|
type=type,
|
|
unit=unit,
|
|
resource_id=resource_id,
|
|
project_id=project_id,
|
|
source=source,
|
|
user_id=user_id,
|
|
)
|
|
|
|
|
|
class Sample(Model):
|
|
"""One collected data point.
|
|
"""
|
|
def __init__(self,
|
|
source,
|
|
counter_name, counter_type, counter_unit, counter_volume,
|
|
user_id, project_id, resource_id,
|
|
timestamp, resource_metadata,
|
|
message_id,
|
|
message_signature,
|
|
):
|
|
"""Create a new sample.
|
|
|
|
:param source: the identifier for the user/project id definition
|
|
:param counter_name: the name of the measurement being taken
|
|
:param counter_type: the type of the measurement
|
|
:param counter_unit: the units for the measurement
|
|
:param counter_volume: the measured value
|
|
:param user_id: the user that triggered the measurement
|
|
:param project_id: the project that owns the resource
|
|
:param resource_id: the thing on which the measurement was taken
|
|
:param timestamp: the time of the measurement
|
|
:param resource_metadata: extra details about the resource
|
|
:param message_id: a message identifier
|
|
:param message_signature: a hash created from the rest of the
|
|
message data
|
|
"""
|
|
Model.__init__(self,
|
|
source=source,
|
|
counter_name=counter_name,
|
|
counter_type=counter_type,
|
|
counter_unit=counter_unit,
|
|
counter_volume=counter_volume,
|
|
user_id=user_id,
|
|
project_id=project_id,
|
|
resource_id=resource_id,
|
|
timestamp=timestamp,
|
|
resource_metadata=resource_metadata,
|
|
message_id=message_id,
|
|
message_signature=message_signature)
|
|
|
|
|
|
class Statistics(Model):
|
|
"""Computed statistics based on a set of sample data.
|
|
"""
|
|
def __init__(self, unit,
|
|
min, max, avg, sum, count,
|
|
period, period_start, period_end,
|
|
duration, duration_start, duration_end,
|
|
groupby):
|
|
"""Create a new statistics object.
|
|
|
|
:param unit: The unit type of the data set
|
|
:param min: The smallest volume found
|
|
:param max: The largest volume found
|
|
:param avg: The average of all volumes found
|
|
:param sum: The total of all volumes found
|
|
:param count: The number of samples found
|
|
:param period: The length of the time range covered by these stats
|
|
:param period_start: The timestamp for the start of the period
|
|
:param period_end: The timestamp for the end of the period
|
|
:param duration: The total time for the matching samples
|
|
:param duration_start: The earliest time for the matching samples
|
|
:param duration_end: The latest time for the matching samples
|
|
:param groupby: The fields used to group the samples.
|
|
"""
|
|
Model.__init__(self, unit=unit,
|
|
min=min, max=max, avg=avg, sum=sum, count=count,
|
|
period=period, period_start=period_start,
|
|
period_end=period_end, duration=duration,
|
|
duration_start=duration_start,
|
|
duration_end=duration_end,
|
|
groupby=groupby)
|
|
|
|
|
|
class Alarm(Model):
|
|
ALARM_INSUFFICIENT_DATA = 'insufficient data'
|
|
ALARM_OK = 'ok'
|
|
ALARM_ALARM = 'alarm'
|
|
|
|
ALARM_ACTIONS_MAP = {
|
|
ALARM_INSUFFICIENT_DATA: 'insufficient_data_actions',
|
|
ALARM_OK: 'ok_actions',
|
|
ALARM_ALARM: 'alarm_actions',
|
|
}
|
|
|
|
"""
|
|
An alarm to monitor.
|
|
|
|
:param alarm_id: UUID of the alarm
|
|
:param type: type of the alarm
|
|
:param name: The Alarm name
|
|
:param description: User friendly description of the alarm
|
|
:param enabled: Is the alarm enabled
|
|
:param state: Alarm state (ok/alarm/insufficient data)
|
|
:param rule: A rule that defines when the alarm fires
|
|
:param user_id: the owner/creator of the alarm
|
|
:param project_id: the project_id of the creator
|
|
:param evaluation_periods: the number of periods
|
|
:param period: the time period in seconds
|
|
:param timestamp: the timestamp when the alarm was last updated
|
|
:param state_timestamp: the timestamp of the last state change
|
|
:param ok_actions: the list of webhooks to call when entering the ok state
|
|
:param alarm_actions: the list of webhooks to call when entering the
|
|
alarm state
|
|
:param insufficient_data_actions: the list of webhooks to call when
|
|
entering the insufficient data state
|
|
:param repeat_actions: Is the actions should be triggered on each
|
|
alarm evaluation.
|
|
"""
|
|
def __init__(self, alarm_id, type, enabled, name, description,
|
|
timestamp, user_id, project_id, state, state_timestamp,
|
|
ok_actions, alarm_actions, insufficient_data_actions,
|
|
repeat_actions, rule):
|
|
Model.__init__(
|
|
self,
|
|
alarm_id=alarm_id,
|
|
type=type,
|
|
enabled=enabled,
|
|
name=name,
|
|
description=description,
|
|
timestamp=timestamp,
|
|
user_id=user_id,
|
|
project_id=project_id,
|
|
state=state,
|
|
state_timestamp=state_timestamp,
|
|
ok_actions=ok_actions,
|
|
alarm_actions=alarm_actions,
|
|
insufficient_data_actions=
|
|
insufficient_data_actions,
|
|
repeat_actions=repeat_actions,
|
|
rule=rule)
|
|
|
|
|
|
class AlarmChange(Model):
|
|
"""Record of an alarm change.
|
|
|
|
:param event_id: UUID of the change event
|
|
:param alarm_id: UUID of the alarm
|
|
:param type: The type of change
|
|
:param detail: JSON fragment describing change
|
|
:param user_id: the user ID of the initiating identity
|
|
:param project_id: the project ID of the initiating identity
|
|
:param on_behalf_of: the tenant on behalf of which the change
|
|
is being made
|
|
:param timestamp: the timestamp of the change
|
|
"""
|
|
|
|
CREATION = 'creation'
|
|
RULE_CHANGE = 'rule change'
|
|
STATE_TRANSITION = 'state transition'
|
|
DELETION = 'deletion'
|
|
|
|
def __init__(self,
|
|
event_id,
|
|
alarm_id,
|
|
type,
|
|
detail,
|
|
user_id,
|
|
project_id,
|
|
on_behalf_of,
|
|
timestamp=None
|
|
):
|
|
Model.__init__(
|
|
self,
|
|
event_id=event_id,
|
|
alarm_id=alarm_id,
|
|
type=type,
|
|
detail=detail,
|
|
user_id=user_id,
|
|
project_id=project_id,
|
|
on_behalf_of=on_behalf_of,
|
|
timestamp=timestamp)
|