249
260
' automatically') % (self.value)
252
if type == 'integer':
253
converted_value = int(self.value)
254
elif type == 'float':
255
converted_value = float(self.value)
256
elif type == 'boolean':
257
converted_value = strutils.bool_from_string(self.value)
258
elif type == 'string':
259
converted_value = self.value
261
# For now, this method only support integer, float,
262
# boolean and and string as the metadata type. A TypeError
263
# will be raised for any other type.
263
if type not in self._supported_types:
264
# Types must be explicitly declared so the
265
# correct type converter may be used. Subclasses
266
# of Query may define _supported_types and
267
# _type_converters to define their own types.
264
268
raise TypeError()
269
converted_value = self._type_converters[type](self.value)
265
270
except ValueError:
266
msg = _('Failed to convert the metadata value %(value)s'
271
msg = _('Failed to convert the value %(value)s'
267
272
' to the expected data type %(type)s.') % \
268
273
{'value': self.value, 'type': type}
269
274
raise ClientSideError(msg)
270
275
except TypeError:
271
msg = _('The data type %s is not supported. The supported'
272
' data type list is: integer, float, boolean and'
276
msg = _('The data type %(type)s is not supported. The supported'
277
' data type list is: %(supported)s') % \
278
{'type': type, 'supported': self._supported_types}
274
279
raise ClientSideError(msg)
275
280
except Exception:
276
281
msg = _('Unexpected exception converting %(value)s to'
884
891
for m in pecan.request.storage_conn.get_meters(**kwargs)]
895
"""One measurement."""
898
"The unique identifier for the sample."
901
"The meter name this sample is for."
903
type = wtypes.Enum(str, *sample.TYPES)
904
"The meter type (see :ref:`measurements`)"
907
"The unit of measure."
912
user_id = wtypes.text
913
"The user this sample was taken for."
915
project_id = wtypes.text
916
"The project this sample was taken for."
918
resource_id = wtypes.text
919
"The :class:`Resource` this sample was taken for."
922
"The source that identifies where the sample comes from."
924
timestamp = datetime.datetime
925
"When the sample has been generated."
927
metadata = {wtypes.text: wtypes.text}
928
"Arbitrary metadata associated with the sample."
931
def from_db_model(cls, m):
932
return cls(id=m.message_id,
933
meter=m.counter_name,
936
volume=m.counter_volume,
938
project_id=m.project_id,
939
resource_id=m.resource_id,
940
timestamp=m.timestamp,
941
metadata=_flatten_metadata(m.resource_metadata))
945
return cls(id=str(uuid.uuid1()),
949
resource_id='bd9431c1-8d69-4ad3-803a-8d4a6b89fd36',
950
project_id='35b17138-b364-4e6a-a131-8f3099c5be68',
951
user_id='efd87807-12d2-4b38-9c70-5f5c2ac427ff',
952
timestamp=timeutils.utcnow(),
954
metadata={'name1': 'value1',
959
class SamplesController(rest.RestController):
960
"""Controller managing the samples."""
962
@wsme_pecan.wsexpose([Sample], [Query], int)
963
def get_all(self, q=[], limit=None):
964
"""Return all known samples, based on the data recorded so far.
966
:param q: Filter rules for the samples to be returned.
967
:param limit: Maximum number of samples to be returned.
969
if limit and limit < 0:
970
raise ClientSideError(_("Limit must be positive"))
971
kwargs = _query_to_kwargs(q, storage.SampleFilter.__init__)
972
f = storage.SampleFilter(**kwargs)
973
return map(Sample.from_db_model,
974
pecan.request.storage_conn.get_samples(f, limit=limit))
976
@wsme_pecan.wsexpose(Sample, wtypes.text)
977
def get_one(self, sample_id):
980
:param sample_id: the id of the sample
982
f = storage.SampleFilter(message_id=sample_id)
984
samples = list(pecan.request.storage_conn.get_samples(f))
986
raise EntityNotFound(_('Sample'), sample_id)
988
return Sample.from_db_model(samples[0])
887
991
class Resource(_Base):
888
992
"""An externally defined object for which samples have been received.
1506
1613
for m in pecan.request.storage_conn.get_alarms(**kwargs)]
1616
class TraitDescription(_Base):
1617
"""A description of a trait, with no associated value."""
1620
"the data type, defaults to string"
1623
"the name of the trait"
1627
return cls(name='service',
1632
class EventQuery(Query):
1633
"""Query arguments for Event Queries."""
1635
_supported_types = ['integer', 'float', 'string', 'datetime']
1637
type = wsme.wsattr(wtypes.text, default='string')
1638
"the type of the trait filter, defaults to string"
1642
return '<EventQuery %r %s %r %s>' % (self.field,
1644
self._get_value_as_type(),
1649
"""A Trait associated with an event."""
1652
"The name of the trait"
1655
"the value of the trait"
1658
"the type of the trait (string, integer, float or datetime)"
1662
return cls(name='service',
1664
value='compute.hostname'
1669
"""A System event."""
1671
message_id = wtypes.text
1672
"The message ID for the notification"
1674
event_type = wtypes.text
1675
"The type of the event"
1679
def get_traits(self):
1683
def _convert_storage_trait(t):
1684
"""Helper method to convert a storage model into an API trait
1685
instance. If an API trait instance is passed in, just return it.
1687
if isinstance(t, Trait):
1689
value = (six.text_type(t.value)
1690
if not t.dtype == storage.models.Trait.DATETIME_TYPE
1691
else t.value.isoformat())
1692
type = storage.models.Trait.get_name_by_type(t.dtype)
1693
return Trait(name=t.name, type=type, value=value)
1695
def set_traits(self, traits):
1696
self._traits = map(self._convert_storage_trait, traits)
1698
traits = wsme.wsproperty(wtypes.ArrayType(Trait),
1701
"Event specific properties"
1703
generated = datetime.datetime
1704
"The time the event occurred"
1709
event_type='compute.instance.update',
1710
generated='2013-11-11T20:00:00',
1711
message_id='94834db1-8f1b-404d-b2ec-c35901f1b7f0',
1713
'request_id': 'req-4e2d67b8-31a4-48af-bb2f-9df72a353a72',
1714
'service': 'conductor.tem-devstack-01',
1715
'tenant_id': '7f13f2b17917463b9ee21aa92c4b36d6'
1720
def requires_admin(func):
1722
@functools.wraps(func)
1723
def wrapped(*args, **kwargs):
1724
usr_limit, proj_limit = acl.get_limited_to(pecan.request.headers)
1725
# If User and Project are None, you have full access.
1726
if usr_limit and proj_limit:
1727
raise ClientSideError(_("Not Authorized"), status_code=403)
1728
return func(*args, **kwargs)
1733
def _event_query_to_event_filter(q):
1734
evt_model_filter = {
1743
# FIXME(herndon): Support for operators other than
1744
# 'eq' will come later.
1746
error = _("operator %s not supported") % i.op
1747
raise ClientSideError(error)
1748
if i.field in evt_model_filter:
1749
evt_model_filter[i.field] = i.value
1751
traits_filter.append({"key": i.field,
1752
i.type: i._get_value_as_type()})
1753
return storage.EventFilter(traits_filter=traits_filter, **evt_model_filter)
1756
class TraitsController(rest.RestController):
1757
"""Works on Event Traits."""
1760
@wsme_pecan.wsexpose([Trait], wtypes.text, wtypes.text)
1761
def get_one(self, event_type, trait_name):
1762
"""Return all instances of a trait for an event type.
1764
:param event_type: Event type to filter traits by
1765
:param trait_name: Trait to return values for
1767
LOG.debug(_("Getting traits for %s") % event_type)
1768
return [Trait(name=t.name, type=t.get_type_name(), value=t.value)
1769
for t in pecan.request.storage_conn
1770
.get_traits(event_type, trait_name)]
1773
@wsme_pecan.wsexpose([TraitDescription], wtypes.text)
1774
def get_all(self, event_type):
1775
"""Return all trait names for an event type.
1777
:param event_type: Event type to filter traits by
1779
get_trait_name = storage.models.Trait.get_name_by_type
1780
return [TraitDescription(name=t['name'],
1781
type=get_trait_name(t['data_type']))
1782
for t in pecan.request.storage_conn
1783
.get_trait_types(event_type)]
1786
class EventTypesController(rest.RestController):
1787
"""Works on Event Types in the system."""
1789
traits = TraitsController()
1791
# FIXME(herndon): due to a bug in pecan, making this method
1792
# get_all instead of get will hide the traits subcontroller.
1793
# https://bugs.launchpad.net/pecan/+bug/1262277
1795
@wsme_pecan.wsexpose([unicode])
1797
"""Get all event types.
1799
return list(pecan.request.storage_conn.get_event_types())
1802
class EventsController(rest.RestController):
1803
"""Works on Events."""
1806
@wsme_pecan.wsexpose([Event], [EventQuery])
1807
def get_all(self, q=[]):
1808
"""Return all events matching the query filters.
1810
:param q: Filter arguments for which Events to return
1812
event_filter = _event_query_to_event_filter(q)
1813
return [Event(message_id=event.message_id,
1814
event_type=event.event_type,
1815
generated=event.generated,
1816
traits=event.traits)
1818
pecan.request.storage_conn.get_events(event_filter)]
1821
@wsme_pecan.wsexpose(Event, wtypes.text)
1822
def get_one(self, message_id):
1823
"""Return a single event with the given message id.
1825
:param message_id: Message ID of the Event to be returned
1827
event_filter = storage.EventFilter(message_id=message_id)
1828
events = pecan.request.storage_conn.get_events(event_filter)
1830
raise EntityNotFound(_("Event"), message_id)
1833
LOG.error(_("More than one event with "
1834
"id %s returned from storage driver") % message_id)
1838
return Event(message_id=event.message_id,
1839
event_type=event.event_type,
1840
generated=event.generated,
1841
traits=event.traits)
1509
1844
class V2Controller(object):
1510
1845
"""Version 2 API controller root."""
1512
1847
resources = ResourcesController()
1513
1848
meters = MetersController()
1849
samples = SamplesController()
1514
1850
alarms = AlarmsController()
1851
event_types = EventTypesController()
1852
events = EventsController()