Event Storage Layer
This change adds the storage layer interfaces necessary to implement the Event API. It also updates the get_events call to allow multiple trait filters. implements bp specify-event-api Change-Id: I7aef4a8fc9b88528479f6c0a9feab2c18945bae3
This commit is contained in:
parent
ec06be3c6a
commit
a5989ab7ad
@ -116,23 +116,28 @@ class SampleFilter(object):
|
|||||||
class EventFilter(object):
|
class EventFilter(object):
|
||||||
"""Properties for building an Event query.
|
"""Properties for building an Event query.
|
||||||
|
|
||||||
:param start: UTC start datetime (mandatory)
|
:param start_time: UTC start datetime (mandatory)
|
||||||
:param end: UTC end datetime (mandatory)
|
:param end_time: UTC end datetime (mandatory)
|
||||||
:param event_type: the name of the event. None for all.
|
:param event_type: the name of the event. None for all.
|
||||||
:param traits: the trait filter dict, all of which are optional
|
:param message_id: the message_id of the event. None for all.
|
||||||
|
:param traits_filter: the trait filter dicts, all of which are optional.
|
||||||
|
This parameter is a list of dictionaries that specify
|
||||||
|
trait values:
|
||||||
{'key': <key>,
|
{'key': <key>,
|
||||||
't_string': <value>,
|
't_string': <value>,
|
||||||
't_int': <value>,
|
't_int': <value>,
|
||||||
't_datetime': <value>
|
't_datetime': <value>
|
||||||
't_float': <value>}
|
't_float': <value>,
|
||||||
currently, only one trait dict is supported.
|
'op': <eq, lt, le, ne, gt or ge> }
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, start, end, event_type=None, traits={}):
|
def __init__(self, start_time=None, end_time=None, event_type=None,
|
||||||
self.start = utils.sanitize_timestamp(start)
|
message_id=None, traits_filter=[]):
|
||||||
self.end = utils.sanitize_timestamp(end)
|
self.start_time = utils.sanitize_timestamp(start_time)
|
||||||
|
self.end_time = utils.sanitize_timestamp(end_time)
|
||||||
|
self.message_id = message_id
|
||||||
self.event_type = event_type
|
self.event_type = event_type
|
||||||
self.traits = traits
|
self.traits_filter = traits_filter
|
||||||
|
|
||||||
|
|
||||||
def dbsync():
|
def dbsync():
|
||||||
|
@ -278,3 +278,26 @@ class Connection(object):
|
|||||||
def get_events(self, event_filter):
|
def get_events(self, event_filter):
|
||||||
"""Return an iterable of model.Event objects.
|
"""Return an iterable of model.Event objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_event_types(self):
|
||||||
|
"""Return all event types as an iterable of strings.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_trait_types(self, event_type):
|
||||||
|
"""Return a dictionary containing the name and data type of
|
||||||
|
the trait type. Only trait types for the provided event_type are
|
||||||
|
returned.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_traits(self, event_type, trait_type=None):
|
||||||
|
"""Return all trait instances associated with an event_type. If
|
||||||
|
trait_type is specified, only return instances of that trait type.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event to filter by
|
||||||
|
:param trait_type: the name of the Trait to filter by
|
||||||
|
"""
|
||||||
|
@ -748,12 +748,12 @@ class Connection(base.Connection):
|
|||||||
:param end_timestamp: Optional modified timestamp end range
|
:param end_timestamp: Optional modified timestamp end range
|
||||||
:param end_timestamp_op: Optional timestamp end range operation
|
:param end_timestamp_op: Optional timestamp end range operation
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarm history not implemented')
|
raise NotImplementedError(_('Alarm history not implemented'))
|
||||||
|
|
||||||
def record_alarm_change(self, alarm_change):
|
def record_alarm_change(self, alarm_change):
|
||||||
"""Record alarm change event.
|
"""Record alarm change event.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarm history not implemented')
|
raise NotImplementedError(_('Alarm history not implemented'))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def record_events(events):
|
def record_events(events):
|
||||||
@ -761,7 +761,7 @@ class Connection(base.Connection):
|
|||||||
|
|
||||||
:param events: a list of model.Event objects.
|
:param events: a list of model.Event objects.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Events not implemented.')
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_events(event_filter):
|
def get_events(event_filter):
|
||||||
@ -769,4 +769,32 @@ class Connection(base.Connection):
|
|||||||
|
|
||||||
:param event_filter: EventFilter instance
|
:param event_filter: EventFilter instance
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Events not implemented.')
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_event_types():
|
||||||
|
"""Return all event types as an iterable of strings.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_trait_types(event_type):
|
||||||
|
"""Return a dictionary containing the name and data type of
|
||||||
|
the trait type. Only trait types for the provided event_type are
|
||||||
|
returned.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_traits(event_type, trait_type=None):
|
||||||
|
"""Return all trait instances associated with an event_type. If
|
||||||
|
trait_type is specified, only return instances of that trait type.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event to filter by
|
||||||
|
:param trait_type: the name of the Trait to filter by
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
@ -597,17 +597,17 @@ class Connection(base.Connection):
|
|||||||
"""Yields a lists of alarms that match filters
|
"""Yields a lists of alarms that match filters
|
||||||
raise NotImplementedError('metaquery not implemented')
|
raise NotImplementedError('metaquery not implemented')
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarms not implemented')
|
raise NotImplementedError(_('Alarms not implemented'))
|
||||||
|
|
||||||
def create_alarm(self, alarm):
|
def create_alarm(self, alarm):
|
||||||
"""update alarm
|
"""update alarm
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarms not implemented')
|
raise NotImplementedError(_('Alarms not implemented'))
|
||||||
|
|
||||||
def update_alarm(self, alarm):
|
def update_alarm(self, alarm):
|
||||||
"""update alarm
|
"""update alarm
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarms not implemented')
|
raise NotImplementedError(_('Alarms not implemented'))
|
||||||
|
|
||||||
def get_alarm_changes(self, alarm_id, on_behalf_of,
|
def get_alarm_changes(self, alarm_id, on_behalf_of,
|
||||||
user=None, project=None, type=None,
|
user=None, project=None, type=None,
|
||||||
@ -636,31 +636,56 @@ class Connection(base.Connection):
|
|||||||
:param end_timestamp: Optional modified timestamp end range
|
:param end_timestamp: Optional modified timestamp end range
|
||||||
:param end_timestamp_op: Optional timestamp end range operation
|
:param end_timestamp_op: Optional timestamp end range operation
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarm history not implemented')
|
raise NotImplementedError(_('Alarm history not implemented'))
|
||||||
|
|
||||||
def record_alarm_change(self, alarm_change):
|
def record_alarm_change(self, alarm_change):
|
||||||
"""Record alarm change event.
|
"""Record alarm change event.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarm history not implemented')
|
raise NotImplementedError(_('Alarm history not implemented'))
|
||||||
|
|
||||||
def delete_alarm(self, alarm_id):
|
def delete_alarm(self, alarm_id):
|
||||||
"""Delete a alarm
|
"""Delete a alarm
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarms not implemented')
|
raise NotImplementedError(_('Alarms not implemented'))
|
||||||
|
|
||||||
def record_events(self, events):
|
def record_events(self, events):
|
||||||
"""Write the events.
|
"""Write the events.
|
||||||
|
|
||||||
:param events: a list of model.Event objects.
|
:param events: a list of model.Event objects.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Events not implemented.')
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
def get_events(self, event_filter):
|
def get_events(self, event_filter):
|
||||||
"""Return an iterable of model.Event objects.
|
"""Return an iterable of model.Event objects.
|
||||||
|
|
||||||
:param event_filter: EventFilter instance
|
:param event_filter: EventFilter instance
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Events not implemented.')
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
def get_event_types(self):
|
||||||
|
"""Return all event types as an iterable of strings.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
def get_trait_types(self, event_type):
|
||||||
|
"""Return a dictionary containing the name and data type of
|
||||||
|
the trait type. Only trait types for the provided event_type are
|
||||||
|
returned.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
def get_traits(self, event_type, trait_type=None):
|
||||||
|
"""Return all trait instances associated with an event_type. If
|
||||||
|
trait_type is specified, only return instances of that trait type.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event to filter by
|
||||||
|
:param trait_type: the name of the Trait to filter by
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"""Simple logging storage backend.
|
"""Simple logging storage backend.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from ceilometer.openstack.common.gettextutils import _ # noqa
|
||||||
from ceilometer.openstack.common import log
|
from ceilometer.openstack.common import log
|
||||||
from ceilometer.storage import base
|
from ceilometer.storage import base
|
||||||
|
|
||||||
@ -205,23 +206,48 @@ class Connection(base.Connection):
|
|||||||
:param end_timestamp: Optional modified timestamp end range
|
:param end_timestamp: Optional modified timestamp end range
|
||||||
:param end_timestamp_op: Optional timestamp end range operation
|
:param end_timestamp_op: Optional timestamp end range operation
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarm history not implemented')
|
raise NotImplementedError(_('Alarm history not implemented'))
|
||||||
|
|
||||||
def record_alarm_change(self, alarm_change):
|
def record_alarm_change(self, alarm_change):
|
||||||
"""Record alarm change event.
|
"""Record alarm change event.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Alarm history not implemented')
|
raise NotImplementedError(_('Alarm history not implemented'))
|
||||||
|
|
||||||
def record_events(self, events):
|
def record_events(self, events):
|
||||||
"""Write the events.
|
"""Write the events.
|
||||||
|
|
||||||
:param events: a list of model.Event objects.
|
:param events: a list of model.Event objects.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Events not implemented.')
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
def get_events(self, event_filter):
|
def get_events(self, event_filter):
|
||||||
"""Return an iterable of model.Event objects.
|
"""Return an iterable of model.Event objects.
|
||||||
|
|
||||||
:param event_filter: EventFilter instance
|
:param event_filter: EventFilter instance
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Events not implemented.')
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
def get_event_types(self):
|
||||||
|
"""Return all event types as an iterable of strings.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
def get_trait_types(self, event_type):
|
||||||
|
"""Return a dictionary containing the name and data type of
|
||||||
|
the trait type. Only trait types for the provided event_type are
|
||||||
|
returned.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
def get_traits(self, event_type, trait_type=None):
|
||||||
|
"""Return all trait instances associated with an event_type. If
|
||||||
|
trait_type is specified, only return instances of that trait type.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event to filter by
|
||||||
|
:param trait_type: the name of the Trait to filter by
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
@ -965,7 +965,7 @@ class Connection(base.Connection):
|
|||||||
|
|
||||||
:param events: a list of model.Event objects.
|
:param events: a list of model.Event objects.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Events not implemented.')
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_events(event_filter):
|
def get_events(event_filter):
|
||||||
@ -973,4 +973,32 @@ class Connection(base.Connection):
|
|||||||
|
|
||||||
:param event_filter: EventFilter instance
|
:param event_filter: EventFilter instance
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Events not implemented.')
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_event_types():
|
||||||
|
"""Return all event types as an iterable of strings.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(_('EventTypes not implemented.'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_trait_types(event_type):
|
||||||
|
"""Return a dictionary containing the name and data type of
|
||||||
|
the trait type. Only trait types for the provided event_type are
|
||||||
|
returned.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_traits(event_type, trait_type=None):
|
||||||
|
"""Return all trait instances associated with an event_type. If
|
||||||
|
trait_type is specified, only return instances of that trait type.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event to filter by
|
||||||
|
:param trait_type: the name of the Trait to filter by
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError(_('Events not implemented.'))
|
||||||
|
@ -918,90 +918,182 @@ class Connection(base.Connection):
|
|||||||
return problem_events
|
return problem_events
|
||||||
|
|
||||||
def get_events(self, event_filter):
|
def get_events(self, event_filter):
|
||||||
"""Return an iterable of model.Event objects. The event model objects
|
"""Return an iterable of model.Event objects.
|
||||||
have their Trait model objects available -- filtered by any traits
|
|
||||||
in the event_filter.
|
|
||||||
|
|
||||||
:param event_filter: EventFilter instance
|
:param event_filter: EventFilter instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
start = utils.dt_to_decimal(event_filter.start)
|
start = utils.dt_to_decimal(event_filter.start_time)
|
||||||
end = utils.dt_to_decimal(event_filter.end)
|
end = utils.dt_to_decimal(event_filter.end_time)
|
||||||
session = sqlalchemy_session.get_session()
|
session = sqlalchemy_session.get_session()
|
||||||
with session.begin():
|
with session.begin():
|
||||||
sub_query = session.query(models.Event.id)\
|
event_query = session.query(models.Event)
|
||||||
.join(models.EventType,
|
|
||||||
models.Event.event_type_id == models.EventType.id)\
|
# Build up the join conditions
|
||||||
.join(models.Trait,
|
event_join_conditions = [models.EventType.id ==
|
||||||
models.Trait.event_id == models.Event.id)\
|
models.Event.event_type_id]
|
||||||
.filter(models.Event.generated >= start,
|
|
||||||
models.Event.generated <= end)
|
|
||||||
|
|
||||||
if event_filter.event_type:
|
if event_filter.event_type:
|
||||||
event_type = event_filter.event_type
|
event_join_conditions\
|
||||||
sub_query = sub_query\
|
.append(models.EventType.desc == event_filter.event_type)
|
||||||
.filter(models.EventType.desc == event_type)
|
|
||||||
|
event_query = event_query.join(models.EventType,
|
||||||
|
and_(*event_join_conditions))
|
||||||
|
|
||||||
|
# Build up the where conditions
|
||||||
|
event_filter_conditions = []
|
||||||
|
if event_filter.message_id:
|
||||||
|
event_filter_conditions\
|
||||||
|
.append(models.Event.message_id == event_filter.message_id)
|
||||||
|
if start:
|
||||||
|
event_filter_conditions.append(models.Event.generated >= start)
|
||||||
|
if end:
|
||||||
|
event_filter_conditions.append(models.Event.generated <= end)
|
||||||
|
|
||||||
|
if event_filter_conditions:
|
||||||
|
event_query = event_query\
|
||||||
|
.filter(and_(*event_filter_conditions))
|
||||||
|
|
||||||
event_models_dict = {}
|
event_models_dict = {}
|
||||||
if event_filter.traits:
|
if event_filter.traits_filter:
|
||||||
sub_query = sub_query.join(models.TraitType,
|
for trait_filter in event_filter.traits_filter:
|
||||||
models.TraitType.id ==
|
|
||||||
models.Trait.trait_type_id)
|
# Build a sub query that joins Trait to TraitType
|
||||||
for key, value in event_filter.traits.iteritems():
|
# where the trait name matches
|
||||||
if key == 'key':
|
trait_name = trait_filter.pop('key')
|
||||||
sub_query = sub_query.filter(models.TraitType.desc ==
|
conditions = [models.Trait.trait_type_id ==
|
||||||
value)
|
models.TraitType.id,
|
||||||
elif key == 't_string':
|
models.TraitType.desc == trait_name]
|
||||||
sub_query = sub_query.filter(
|
|
||||||
models.Trait.t_string == value)
|
for key, value in trait_filter.iteritems():
|
||||||
|
if key == 't_string':
|
||||||
|
conditions.append(models.Trait.t_string == value)
|
||||||
elif key == 't_int':
|
elif key == 't_int':
|
||||||
sub_query = sub_query.filter(
|
conditions.append(models.Trait.t_int == value)
|
||||||
models.Trait.t_int == value)
|
|
||||||
elif key == 't_datetime':
|
elif key == 't_datetime':
|
||||||
dt = utils.dt_to_decimal(value)
|
dt = utils.dt_to_decimal(value)
|
||||||
sub_query = sub_query.filter(
|
conditions.append(models.Trait.t_datetime == dt)
|
||||||
models.Trait.t_datetime == dt)
|
|
||||||
elif key == 't_float':
|
elif key == 't_float':
|
||||||
sub_query = sub_query.filter(
|
conditions.append(models.Trait.t_float == value)
|
||||||
models.Trait.t_datetime == value)
|
|
||||||
|
trait_query = session.query(models.Trait.event_id)\
|
||||||
|
.join(models.TraitType, and_(*conditions)).subquery()
|
||||||
|
|
||||||
|
event_query = event_query\
|
||||||
|
.join(trait_query,
|
||||||
|
models.Event.id == trait_query.c.event_id)
|
||||||
else:
|
else:
|
||||||
# Pre-populate event_models_dict to cover Events without traits
|
# If there are no trait filters, grab the events from the db
|
||||||
events = session.query(models.Event)\
|
query = session.query(models.Event.id,
|
||||||
.filter(models.Event.generated >= start)\
|
models.Event.generated,
|
||||||
.filter(models.Event.generated <= end)
|
models.Event.message_id,
|
||||||
if event_filter.event_type:
|
models.EventType.desc)\
|
||||||
events = events\
|
|
||||||
.join(models.EventType,
|
.join(models.EventType,
|
||||||
models.EventType.id ==
|
and_(*event_join_conditions))
|
||||||
models.Event.event_type_id)\
|
if event_filter_conditions:
|
||||||
.filter(models.EventType.desc ==
|
query = query.filter(and_(*event_filter_conditions))
|
||||||
event_filter.event_type)
|
for (id, generated, message_id, desc) in query.all():
|
||||||
for db_event in events.all():
|
event_models_dict[id] = api_models.Event(message_id,
|
||||||
generated = utils.decimal_to_dt(db_event.generated)
|
desc,
|
||||||
api_event = api_models.Event(db_event.message_id,
|
generated,
|
||||||
db_event.event_type.desc,
|
[])
|
||||||
generated, [])
|
|
||||||
event_models_dict[db_event.id] = api_event
|
|
||||||
|
|
||||||
sub_query = sub_query.subquery()
|
# Build event models for the events
|
||||||
|
event_query = event_query.subquery()
|
||||||
all_data = session.query(models.Trait)\
|
query = session.query(models.Trait)\
|
||||||
.join(sub_query, models.Trait.event_id == sub_query.c.id)
|
.join(models.TraitType,
|
||||||
|
models.Trait.trait_type_id == models.TraitType.id)\
|
||||||
|
.join(event_query, models.Trait.event_id == event_query.c.id)
|
||||||
|
|
||||||
# Now convert the sqlalchemy objects back into Models ...
|
# Now convert the sqlalchemy objects back into Models ...
|
||||||
for trait in all_data.all():
|
for trait in query.all():
|
||||||
event = event_models_dict.get(trait.event_id)
|
event = event_models_dict.get(trait.event_id)
|
||||||
if not event:
|
if not event:
|
||||||
generated = utils.decimal_to_dt(trait.event.generated)
|
generated = utils.decimal_to_dt(trait.event.generated)
|
||||||
event = api_models.Event(trait.event.message_id,
|
event = api_models.Event(
|
||||||
|
trait.event.message_id,
|
||||||
trait.event.event_type.desc,
|
trait.event.event_type.desc,
|
||||||
generated, [])
|
generated, [])
|
||||||
event_models_dict[trait.event_id] = event
|
event_models_dict[trait.event_id] = event
|
||||||
value = trait.get_value()
|
|
||||||
trait_model = api_models.Trait(trait.trait_type.desc,
|
trait_model = api_models.Trait(trait.trait_type.desc,
|
||||||
trait.trait_type.data_type,
|
trait.trait_type.data_type,
|
||||||
value)
|
trait.get_value())
|
||||||
event.append_trait(trait_model)
|
event.append_trait(trait_model)
|
||||||
|
|
||||||
event_models = event_models_dict.values()
|
event_models = event_models_dict.values()
|
||||||
return sorted(event_models, key=operator.attrgetter('generated'))
|
return sorted(event_models, key=operator.attrgetter('generated'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_event_types():
|
||||||
|
"""Return all event types as an iterable of strings.
|
||||||
|
"""
|
||||||
|
|
||||||
|
session = sqlalchemy_session.get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = session.query(models.EventType.desc)\
|
||||||
|
.order_by(models.EventType.desc)
|
||||||
|
for name in query.all():
|
||||||
|
# The query returns a tuple with one element.
|
||||||
|
yield name[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_trait_types(event_type):
|
||||||
|
"""Return a dictionary containing the name and data type of
|
||||||
|
the trait type. Only trait types for the provided event_type are
|
||||||
|
returned.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event
|
||||||
|
"""
|
||||||
|
session = sqlalchemy_session.get_session()
|
||||||
|
|
||||||
|
with session.begin():
|
||||||
|
query = (session.query(models.TraitType.desc,
|
||||||
|
models.TraitType.data_type)
|
||||||
|
.join(models.Trait,
|
||||||
|
models.Trait.trait_type_id ==
|
||||||
|
models.TraitType.id)
|
||||||
|
.join(models.Event,
|
||||||
|
models.Event.id ==
|
||||||
|
models.Trait.event_id)
|
||||||
|
.join(models.EventType,
|
||||||
|
and_(models.EventType.id ==
|
||||||
|
models.Event.id,
|
||||||
|
models.EventType.desc ==
|
||||||
|
event_type))
|
||||||
|
.group_by(models.TraitType.desc,
|
||||||
|
models.TraitType.data_type)
|
||||||
|
.distinct())
|
||||||
|
|
||||||
|
for desc, type in query.all():
|
||||||
|
yield {'name': desc, 'data_type': type}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_traits(event_type, trait_type=None):
|
||||||
|
"""Return all trait instances associated with an event_type. If
|
||||||
|
trait_type is specified, only return instances of that trait type.
|
||||||
|
|
||||||
|
:param event_type: the type of the Event to filter by
|
||||||
|
:param trait_type: the name of the Trait to filter by
|
||||||
|
"""
|
||||||
|
|
||||||
|
session = sqlalchemy_session.get_session()
|
||||||
|
with session.begin():
|
||||||
|
trait_type_filters = [models.TraitType.id ==
|
||||||
|
models.Trait.trait_type_id]
|
||||||
|
if trait_type:
|
||||||
|
trait_type_filters.append(models.TraitType.desc == trait_type)
|
||||||
|
|
||||||
|
query = (session.query(models.Trait)
|
||||||
|
.join(models.TraitType, and_(*trait_type_filters))
|
||||||
|
.join(models.Event,
|
||||||
|
models.Event.id == models.Trait.event_id)
|
||||||
|
.join(models.EventType,
|
||||||
|
and_(models.EventType.id ==
|
||||||
|
models.Event.event_type_id,
|
||||||
|
models.EventType.desc == event_type)))
|
||||||
|
|
||||||
|
for trait in query.all():
|
||||||
|
type = trait.trait_type
|
||||||
|
yield api_models.Trait(name=type.desc,
|
||||||
|
dtype=type.data_type,
|
||||||
|
value=trait.get_value())
|
||||||
|
@ -2101,7 +2101,7 @@ class GetEventTest(EventTestBase):
|
|||||||
base = 0
|
base = 0
|
||||||
self.start = datetime.datetime(2013, 12, 31, 5, 0)
|
self.start = datetime.datetime(2013, 12, 31, 5, 0)
|
||||||
now = self.start
|
now = self.start
|
||||||
for event_type in ['Foo', 'Bar', 'Zoo']:
|
for event_type in ['Foo', 'Bar', 'Zoo', 'Foo', 'Bar', 'Zoo']:
|
||||||
trait_models = \
|
trait_models = \
|
||||||
[models.Trait(name, dtype, value)
|
[models.Trait(name, dtype, value)
|
||||||
for name, dtype, value in [
|
for name, dtype, value in [
|
||||||
@ -2113,7 +2113,7 @@ class GetEventTest(EventTestBase):
|
|||||||
float(base) + 0.123456),
|
float(base) + 0.123456),
|
||||||
('trait_D', models.Trait.DATETIME_TYPE, now)]]
|
('trait_D', models.Trait.DATETIME_TYPE, now)]]
|
||||||
event_models.append(
|
event_models.append(
|
||||||
models.Event("id_%s" % event_type,
|
models.Event("id_%s_%d" % (event_type, base),
|
||||||
event_type, now, trait_models))
|
event_type, now, trait_models))
|
||||||
base += 100
|
base += 100
|
||||||
now = now + datetime.timedelta(hours=1)
|
now = now + datetime.timedelta(hours=1)
|
||||||
@ -2124,10 +2124,10 @@ class GetEventTest(EventTestBase):
|
|||||||
def test_simple_get(self):
|
def test_simple_get(self):
|
||||||
event_filter = storage.EventFilter(self.start, self.end)
|
event_filter = storage.EventFilter(self.start, self.end)
|
||||||
events = self.conn.get_events(event_filter)
|
events = self.conn.get_events(event_filter)
|
||||||
self.assertEqual(3, len(events))
|
self.assertEqual(6, len(events))
|
||||||
start_time = None
|
start_time = None
|
||||||
for i, name in enumerate(["Foo", "Bar", "Zoo"]):
|
for i, type in enumerate(['Foo', 'Bar', 'Zoo']):
|
||||||
self.assertEqual(events[i].event_type, name)
|
self.assertEqual(events[i].event_type, type)
|
||||||
self.assertEqual(4, len(events[i].traits))
|
self.assertEqual(4, len(events[i].traits))
|
||||||
# Ensure sorted results ...
|
# Ensure sorted results ...
|
||||||
if start_time is not None:
|
if start_time is not None:
|
||||||
@ -2136,22 +2136,120 @@ class GetEventTest(EventTestBase):
|
|||||||
start_time = events[i].generated
|
start_time = events[i].generated
|
||||||
|
|
||||||
def test_simple_get_event_type(self):
|
def test_simple_get_event_type(self):
|
||||||
|
expected_trait_values = {
|
||||||
|
'id_Bar_100': {
|
||||||
|
'trait_A': 'my_Bar_text',
|
||||||
|
'trait_B': 101,
|
||||||
|
'trait_C': 100.123456,
|
||||||
|
'trait_D': self.start + datetime.timedelta(hours=1)
|
||||||
|
},
|
||||||
|
'id_Bar_400': {
|
||||||
|
'trait_A': 'my_Bar_text',
|
||||||
|
'trait_B': 401,
|
||||||
|
'trait_C': 400.123456,
|
||||||
|
'trait_D': self.start + datetime.timedelta(hours=4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
event_filter = storage.EventFilter(self.start, self.end, "Bar")
|
event_filter = storage.EventFilter(self.start, self.end, "Bar")
|
||||||
events = self.conn.get_events(event_filter)
|
events = self.conn.get_events(event_filter)
|
||||||
self.assertEqual(1, len(events))
|
self.assertEqual(2, len(events))
|
||||||
self.assertEqual(events[0].event_type, "Bar")
|
self.assertEqual(events[0].event_type, "Bar")
|
||||||
|
self.assertEqual(events[1].event_type, "Bar")
|
||||||
self.assertEqual(4, len(events[0].traits))
|
self.assertEqual(4, len(events[0].traits))
|
||||||
|
self.assertEqual(4, len(events[1].traits))
|
||||||
|
for event in events:
|
||||||
|
trait_values = expected_trait_values.get(event.message_id,
|
||||||
|
None)
|
||||||
|
if not trait_values:
|
||||||
|
self.fail("Unexpected event ID returned:" % event.message_id)
|
||||||
|
|
||||||
|
for trait in event.traits:
|
||||||
|
expected_val = trait_values.get(trait.name, None)
|
||||||
|
if not expected_val:
|
||||||
|
self.fail("Unexpected trait type: %s" % trait.dtype)
|
||||||
|
self.assertEqual(expected_val, trait.value)
|
||||||
|
|
||||||
def test_get_event_trait_filter(self):
|
def test_get_event_trait_filter(self):
|
||||||
trait_filters = {'key': 'trait_B', 't_int': 101}
|
trait_filters = [{'key': 'trait_B', 't_int': 101}]
|
||||||
event_filter = storage.EventFilter(self.start, self.end,
|
event_filter = storage.EventFilter(self.start, self.end,
|
||||||
traits=trait_filters)
|
traits_filter=trait_filters)
|
||||||
events = self.conn.get_events(event_filter)
|
events = self.conn.get_events(event_filter)
|
||||||
self.assertEqual(1, len(events))
|
self.assertEqual(1, len(events))
|
||||||
self.assertEqual(events[0].event_type, "Bar")
|
self.assertEqual(events[0].event_type, "Bar")
|
||||||
self.assertEqual(4, len(events[0].traits))
|
self.assertEqual(4, len(events[0].traits))
|
||||||
|
|
||||||
def test_simple_get_no_traits(self):
|
def test_get_event_multiple_trait_filter(self):
|
||||||
|
trait_filters = [{'key': 'trait_B', 't_int': 1},
|
||||||
|
{'key': 'trait_A', 't_string': 'my_Foo_text'}]
|
||||||
|
event_filter = storage.EventFilter(self.start, self.end,
|
||||||
|
traits_filter=trait_filters)
|
||||||
|
events = self.conn.get_events(event_filter)
|
||||||
|
self.assertEqual(1, len(events))
|
||||||
|
self.assertEqual(events[0].event_type, "Foo")
|
||||||
|
self.assertEqual(4, len(events[0].traits))
|
||||||
|
|
||||||
|
def test_get_event_multiple_trait_filter_expect_none(self):
|
||||||
|
trait_filters = [{'key': 'trait_B', 't_int': 1},
|
||||||
|
{'key': 'trait_A', 't_string': 'my_Zoo_text'}]
|
||||||
|
event_filter = storage.EventFilter(self.start, self.end,
|
||||||
|
traits_filter=trait_filters)
|
||||||
|
events = self.conn.get_events(event_filter)
|
||||||
|
self.assertEqual(0, len(events))
|
||||||
|
|
||||||
|
def test_get_event_types(self):
|
||||||
|
event_types = [e for e in
|
||||||
|
self.conn.get_event_types()]
|
||||||
|
|
||||||
|
self.assertEqual(3, len(event_types))
|
||||||
|
self.assertTrue("Bar" in event_types)
|
||||||
|
self.assertTrue("Foo" in event_types)
|
||||||
|
self.assertTrue("Zoo" in event_types)
|
||||||
|
|
||||||
|
def test_get_trait_types(self):
|
||||||
|
trait_types = [tt for tt in
|
||||||
|
self.conn.get_trait_types("Foo")]
|
||||||
|
self.assertEqual(4, len(trait_types))
|
||||||
|
trait_type_names = map(lambda x: x['name'], trait_types)
|
||||||
|
self.assertIn("trait_A", trait_type_names)
|
||||||
|
self.assertIn("trait_B", trait_type_names)
|
||||||
|
self.assertIn("trait_C", trait_type_names)
|
||||||
|
self.assertIn("trait_D", trait_type_names)
|
||||||
|
|
||||||
|
def test_get_trait_types_unknown_event(self):
|
||||||
|
trait_types = [tt for tt in
|
||||||
|
self.conn.get_trait_types("Moo")]
|
||||||
|
self.assertEqual(0, len(trait_types))
|
||||||
|
|
||||||
|
def test_get_traits(self):
|
||||||
|
traits = self.conn.get_traits("Bar")
|
||||||
|
#format results in a way that makes them easier to
|
||||||
|
#work with
|
||||||
|
trait_dict = {}
|
||||||
|
for trait in traits:
|
||||||
|
trait_dict[trait.name] = trait.dtype
|
||||||
|
|
||||||
|
self.assertTrue("trait_A" in trait_dict)
|
||||||
|
self.assertEqual(models.Trait.TEXT_TYPE, trait_dict["trait_A"])
|
||||||
|
self.assertTrue("trait_B" in trait_dict)
|
||||||
|
self.assertEqual(models.Trait.INT_TYPE, trait_dict["trait_B"])
|
||||||
|
self.assertTrue("trait_C" in trait_dict)
|
||||||
|
self.assertEqual(models.Trait.FLOAT_TYPE, trait_dict["trait_C"])
|
||||||
|
self.assertTrue("trait_D" in trait_dict)
|
||||||
|
self.assertEqual(models.Trait.DATETIME_TYPE,
|
||||||
|
trait_dict["trait_D"])
|
||||||
|
|
||||||
|
def test_get_all_traits(self):
|
||||||
|
traits = self.conn.\
|
||||||
|
get_traits("Foo")
|
||||||
|
traits = [t for t in traits]
|
||||||
|
self.assertEqual(8, len(traits))
|
||||||
|
|
||||||
|
trait = traits[0]
|
||||||
|
self.assertEqual("trait_A", trait.name)
|
||||||
|
self.assertEqual(models.Trait.TEXT_TYPE, trait.dtype)
|
||||||
|
|
||||||
|
def test_simple_get_event_no_traits(self):
|
||||||
new_events = [models.Event("id_notraits", "NoTraits", self.start, [])]
|
new_events = [models.Event("id_notraits", "NoTraits", self.start, [])]
|
||||||
bad_events = self.conn.record_events(new_events)
|
bad_events = self.conn.record_events(new_events)
|
||||||
event_filter = storage.EventFilter(self.start, self.end, "NoTraits")
|
event_filter = storage.EventFilter(self.start, self.end, "NoTraits")
|
||||||
@ -2162,6 +2260,25 @@ class GetEventTest(EventTestBase):
|
|||||||
self.assertEqual(events[0].event_type, "NoTraits")
|
self.assertEqual(events[0].event_type, "NoTraits")
|
||||||
self.assertEqual(0, len(events[0].traits))
|
self.assertEqual(0, len(events[0].traits))
|
||||||
|
|
||||||
|
def test_simple_get_no_filters(self):
|
||||||
|
event_filter = storage.EventFilter(None, None, None)
|
||||||
|
events = self.conn.get_events(event_filter)
|
||||||
|
self.assertEqual(6, len(events))
|
||||||
|
|
||||||
|
def test_get_by_message_id(self):
|
||||||
|
new_events = [models.Event("id_testid",
|
||||||
|
"MessageIDTest",
|
||||||
|
self.start,
|
||||||
|
[])]
|
||||||
|
|
||||||
|
bad_events = self.conn.record_events(new_events)
|
||||||
|
event_filter = storage.EventFilter(message_id="id_testid")
|
||||||
|
events = self.conn.get_events(event_filter)
|
||||||
|
self.assertEqual(0, len(bad_events))
|
||||||
|
self.assertEqual(1, len(events))
|
||||||
|
event = events[0]
|
||||||
|
self.assertEqual("id_testid", event.message_id)
|
||||||
|
|
||||||
|
|
||||||
class BigIntegerTest(tests_db.TestBase,
|
class BigIntegerTest(tests_db.TestBase,
|
||||||
tests_db.MixinTestsWithBackendScenarios):
|
tests_db.MixinTestsWithBackendScenarios):
|
||||||
|
@ -48,6 +48,9 @@ def dt_to_decimal(utc):
|
|||||||
Some databases don't store microseconds in datetime
|
Some databases don't store microseconds in datetime
|
||||||
so we always store as Decimal unixtime.
|
so we always store as Decimal unixtime.
|
||||||
"""
|
"""
|
||||||
|
if utc is None:
|
||||||
|
return None
|
||||||
|
|
||||||
decimal.getcontext().prec = 30
|
decimal.getcontext().prec = 30
|
||||||
return decimal.Decimal(str(calendar.timegm(utc.utctimetuple()))) + \
|
return decimal.Decimal(str(calendar.timegm(utc.utctimetuple()))) + \
|
||||||
(decimal.Decimal(str(utc.microsecond)) /
|
(decimal.Decimal(str(utc.microsecond)) /
|
||||||
@ -59,6 +62,7 @@ def decimal_to_dt(dec):
|
|||||||
"""
|
"""
|
||||||
if dec is None:
|
if dec is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
integer = int(dec)
|
integer = int(dec)
|
||||||
micro = (dec - decimal.Decimal(integer)) * decimal.Decimal(1000000)
|
micro = (dec - decimal.Decimal(integer)) * decimal.Decimal(1000000)
|
||||||
daittyme = datetime.datetime.utcfromtimestamp(integer)
|
daittyme = datetime.datetime.utcfromtimestamp(integer)
|
||||||
|
Loading…
Reference in New Issue
Block a user