panko.api.controllers.v2.events

Source code for panko.api.controllers.v2.events

#
# Copyright 2012 New Dream Network, LLC (DreamHost)
# Copyright 2013 IBM Corp.
# Copyright 2013 eNovance <licensing@enovance.com>
# Copyright Ericsson AB 2013. All rights reserved
# Copyright 2014 Hewlett-Packard Company
# Copyright 2015 Huawei Technologies Co., Ltd.
#
# 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.

import datetime

from oslo_log import log
from oslo_utils import strutils
import pecan
from pecan import rest
import six
import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan

from panko.api.controllers.v2 import base
from panko.api.controllers.v2 import utils as v2_utils
from panko.api import rbac
from panko.i18n import _
from panko import storage
from panko.storage import models as event_models

LOG = log.getLogger(__name__)


class TraitDescription(base.Base):
    """A description of a trait, with no associated value."""

    type = wtypes.text
    "the data type, defaults to string"

    name = wtypes.text
    "the name of the trait"

    @classmethod
    def sample(cls):
        return cls(name='service',
                   type='string'
                   )


class EventQuery(base.Query):
    """Query arguments for Event Queries."""

    _supported_types = ['integer', 'float', 'string', 'datetime']

    type = wsme.wsattr(wtypes.text, default='string')
    "the type of the trait filter, defaults to string"

    field = wsme.wsattr(wtypes.text)
    '''
    Name of the field to filter on. Can be either a trait name or field of an
    event.
    1) Use start_timestamp/end_timestamp to filter on `generated` field.
    2) Specify the 'all_tenants=True' query parameter to get all events for all
    projects, this is only allowed by admin users.
    '''

    def __repr__(self):
        # for logging calls
        return '<EventQuery %r %s %r %s>' % (self.field,
                                             self.op,
                                             self._get_value_as_type(),
                                             self.type)

    @classmethod
    def sample(cls):
        return cls(field="event_type",
                   type="string",
                   op="eq",
                   value="compute.instance.create.start")


class Trait(base.Base):
    """A Trait associated with an event."""

    name = wtypes.text
    "The name of the trait"

    value = wtypes.text
    "the value of the trait"

    type = wtypes.text
    "the type of the trait (string, integer, float or datetime)"

    @staticmethod
    def _convert_storage_trait(trait):
        """Helper method to convert a storage model into an API trait instance.

        If an API trait instance is passed in, just return it.
        """
        if isinstance(trait, Trait):
            return trait
        value = (six.text_type(trait.value)
                 if not trait.dtype == event_models.Trait.DATETIME_TYPE
                 else trait.value.isoformat())
        trait_type = event_models.Trait.get_name_by_type(trait.dtype)
        return Trait(name=trait.name, type=trait_type, value=value)

    @classmethod
    def sample(cls):
        return cls(name='service',
                   type='string',
                   value='compute.hostname'
                   )


class Event(base.Base):
    """A System event."""

    message_id = wtypes.text
    "The message ID for the notification"

    event_type = wtypes.text
    "The type of the event"

    _traits = None

    def get_traits(self):
        return self._traits

    def set_traits(self, traits):
        self._traits = map(Trait._convert_storage_trait, traits)

    traits = wsme.wsproperty(wtypes.ArrayType(Trait),
                             get_traits,
                             set_traits)
    "Event specific properties"

    generated = datetime.datetime
    "The time the event occurred"

    raw = base.JsonType()
    "The raw copy of notification"

    @classmethod
    def sample(cls):
        return cls(
            event_type='compute.instance.update',
            generated=datetime.datetime(2015, 1, 1, 12, 0, 0, 0),
            message_id='94834db1-8f1b-404d-b2ec-c35901f1b7f0',
            traits={
                Trait(name='request_id',
                      value='req-4e2d67b8-31a4-48af-bb2f-9df72a353a72'),
                Trait(name='service',
                      value='conductor.tem-devstack-01'),
                Trait(name='tenant_id',
                      value='7f13f2b17917463b9ee21aa92c4b36d6')
            },
            raw={'status': {'nested': 'started'}}
        )


def _build_rbac_query_filters():
    filters = {'t_filter': [], 'admin_proj': None}
    # Returns user_id, proj_id for non-admins
    user_id, proj_id = rbac.get_limited_to(pecan.request.headers)
    # If non-admin, filter events by user and project
    if user_id and proj_id:
        filters['t_filter'].append({"key": "project_id", "string": proj_id,
                                    "op": "eq"})
        filters['t_filter'].append({"key": "user_id", "string": user_id,
                                    "op": "eq"})
    elif not user_id and not proj_id:
        filters['admin_proj'] = pecan.request.headers.get('X-Project-Id')
    return filters


def _event_query_to_event_filter(q):
    evt_model_filter = {
        'event_type': None,
        'message_id': None,
        'start_timestamp': None,
        'end_timestamp': None
    }
    filters = _build_rbac_query_filters()
    traits_filter = filters['t_filter']
    admin_proj = filters['admin_proj']

    for i in q:
        if not i.op:
            i.op = 'eq'
        elif i.op not in base.operation_kind:
            error = (_('Operator %(operator)s is not supported. The supported'
                       ' operators are: %(supported)s') %
                     {'operator': i.op, 'supported': base.operation_kind})
            raise base.ClientSideError(error)
        if i.field in evt_model_filter:
            if i.op != 'eq' and i.field in ('event_type', 'message_id'):
                error = (_('Operator %(operator)s is not supported. Only'
                           ' `eq\' operator is available for field'
                           ' %(field)s') %
                         {'operator': i.op, 'field': i.field})
                raise base.ClientSideError(error)
            if i.op != 'ge' and i.field == 'start_timestamp':
                error = (_('Operator %(operator)s is not supported. Only'
                           ' `ge\' operator is available for field'
                           ' %(field)s') %
                         {'operator': i.op, 'field': i.field})
                raise base.ClientSideError(error)
            if i.op != 'le' and i.field == 'end_timestamp':
                error = (_('Operator %(operator)s is not supported. Only'
                           ' `le\' operator is available for field'
                           ' %(field)s') %
                         {'operator': i.op, 'field': i.field})
                raise base.ClientSideError(error)
            evt_model_filter[i.field] = i.value
        elif i.field == 'all_tenants' and admin_proj:
            all_tenants = strutils.bool_from_string(i.value)
            if all_tenants:
                admin_proj = None
        else:
            trait_type = i.type or 'string'
            traits_filter.append({"key": i.field,
                                  trait_type: i._get_value_as_type(),
                                  "op": i.op})
    return storage.EventFilter(traits_filter=traits_filter,
                               admin_proj=admin_proj, **evt_model_filter)


class TraitsController(rest.RestController):
    """Works on Event Traits."""

[docs] @v2_utils.requires_admin @wsme_pecan.wsexpose([Trait], wtypes.text, wtypes.text) def get_one(self, event_type, trait_name): """Return all instances of a trait for an event type. :param event_type: Event type to filter traits by :param trait_name: Trait to return values for """ LOG.debug("Getting traits for %s", event_type) return [Trait._convert_storage_trait(t) for t in pecan.request.conn.get_traits(event_type, trait_name)]
[docs] @v2_utils.requires_admin @wsme_pecan.wsexpose([TraitDescription], wtypes.text) def get_all(self, event_type): """Return all trait names for an event type. :param event_type: Event type to filter traits by """ get_trait_name = event_models.Trait.get_name_by_type return [TraitDescription(name=t['name'], type=get_trait_name(t['data_type'])) for t in pecan.request.conn.get_trait_types(event_type)]
class EventTypesController(rest.RestController): """Works on Event Types in the system.""" traits = TraitsController() @v2_utils.requires_admin @wsme_pecan.wsexpose(None, wtypes.text) def get_one(self, event_type): """Unused API, will always return 404. :param event_type: A event type """ pecan.abort(404) @v2_utils.requires_admin @wsme_pecan.wsexpose([six.text_type]) def get_all(self): """Get all event types.""" return list(pecan.request.conn.get_event_types()) class EventsController(rest.RestController): """Works on Events.""" @v2_utils.requires_context @wsme_pecan.wsexpose([Event], [EventQuery], int, [str], str) def get_all(self, q=None, limit=None, sort=None, marker=None): """Return all events matching the query filters. :param q: Filter arguments for which Events to return :param limit: Maximum number of samples to be returned. :param sort: A pair of sort key and sort direction combined with ":" :param marker: The pagination query marker, message id of the last item viewed """ rbac.enforce("events:index", pecan.request) q = q or [] event_filter = _event_query_to_event_filter(q) pagination = v2_utils.set_pagination_options( sort, limit, marker, event_models.Event) return [Event(message_id=event.message_id, event_type=event.event_type, generated=event.generated, traits=event.traits, raw=event.raw) for event in pecan.request.conn.get_events(event_filter, pagination)] @v2_utils.requires_context @wsme_pecan.wsexpose(Event, wtypes.text) def get_one(self, message_id): """Return a single event with the given message id. :param message_id: Message ID of the Event to be returned """ rbac.enforce("events:show", pecan.request) filters = _build_rbac_query_filters() t_filter = filters['t_filter'] admin_proj = filters['admin_proj'] event_filter = storage.EventFilter(traits_filter=t_filter, admin_proj=admin_proj, message_id=message_id) events = [event for event in pecan.request.conn.get_events(event_filter)] if not events: raise base.EntityNotFound(_("Event"), message_id) if len(events) > 1: LOG.error(("More than one event with " "id %s returned from storage driver"), message_id) event = events[0] return Event(message_id=event.message_id, event_type=event.event_type, generated=event.generated, traits=event.traits, raw=event.raw)
Creative Commons Attribution 3.0 License

Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.