Skip to content

Commit

Permalink
Merge pull request #144 from tzumainn/event-history
Browse files Browse the repository at this point in the history
Added event object
  • Loading branch information
tzumainn authored Jun 29, 2023
2 parents a5480d4 + dffbc5d commit 21490a8
Show file tree
Hide file tree
Showing 15 changed files with 586 additions and 6 deletions.
113 changes: 113 additions & 0 deletions esi_leap/api/controllers/v1/event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# 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
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan

from esi_leap.api.controllers import base
from esi_leap.api.controllers import types
from esi_leap.api.controllers.v1 import utils
from esi_leap.common import exception
from esi_leap.common import keystone
import esi_leap.conf
from esi_leap.objects import event as event_obj
from esi_leap.resource_objects import get_resource_object

CONF = esi_leap.conf.CONF


class Event(base.ESILEAPBase):

id = wsme.wsattr(int, readonly=True)
event_type = wsme.wsattr(wtypes.text, readonly=True)
event_time = wsme.wsattr(datetime.datetime, readonly=True)
object_type = wsme.wsattr(wtypes.text, readonly=True)
object_uuid = wsme.wsattr(wtypes.text, readonly=True)
resource_type = wsme.wsattr(wtypes.text, readonly=True)
resource_uuid = wsme.wsattr(wtypes.text, readonly=True)
lessee_id = wsme.wsattr(wtypes.text, readonly=True)
owner_id = wsme.wsattr(wtypes.text, readonly=True)

def __init__(self, **kwargs):

self.fields = event_obj.Event.fields
for field in self.fields:
setattr(self, field, kwargs.get(field, wtypes.Unset))


class EventCollection(types.Collection):
events = [Event]

def __init__(self, **kwargs):
self._type = 'events'


class EventsController(rest.RestController):

@wsme_pecan.wsexpose(EventCollection, int, wtypes.text,
datetime.datetime, wtypes.text, wtypes.text,
wtypes.text, wtypes.text)
def get_all(self, last_event_id=None, lessee_or_owner_id=None,
last_event_time=None, event_type=None,
resource_type=None, resource_uuid=None):
request = pecan.request.context
cdict = request.to_policy_values()

try:
utils.policy_authorize('esi_leap:offer:offer_admin', cdict, cdict)
except exception.HTTPForbidden:
lessee_or_owner_id = cdict['project_id']

if lessee_or_owner_id is not None:
lessee_or_owner_id = keystone.get_project_uuid_from_ident(
lessee_or_owner_id)

if resource_uuid is not None:
if resource_type is None:
resource_type = CONF.api.default_resource_type
resource = get_resource_object(resource_type, resource_uuid)
resource_uuid = resource.get_uuid()

filters = {
'last_event_id': last_event_id,
'last_event_time': last_event_time,
'lessee_or_owner_id': lessee_or_owner_id,
'event_type': event_type,
'resource_type': resource_type,
'resource_uuid': resource_uuid,
}

# unpack iterator to tuple so we can use 'del'
for k, v in tuple(filters.items()):
if v is None:
del filters[k]

events = event_obj.Event.get_all(filters, request)
event_collection = EventCollection()
event_collection.events = []
for event in events:
e = Event(id=event.id,
event_type=event.event_type,
event_time=event.event_time,
object_type=event.object_type,
object_uuid=event.object_uuid,
resource_type=event.resource_type,
resource_uuid=event.resource_uuid,
lessee_id=event.lessee_id,
owner_id=event.owner_id)
event_collection.events.append(e)

return event_collection
2 changes: 2 additions & 0 deletions esi_leap/api/controllers/v1/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import pecan
from pecan import rest

from esi_leap.api.controllers.v1 import event
from esi_leap.api.controllers.v1 import lease
from esi_leap.api.controllers.v1 import node
from esi_leap.api.controllers.v1 import offer
Expand All @@ -24,6 +25,7 @@ class Controller(rest.RestController):
leases = lease.LeasesController()
offers = offer.OffersController()
nodes = node.NodesController()
events = event.EventsController()

@pecan.expose(content_type='application/json')
def index(self):
Expand Down
5 changes: 3 additions & 2 deletions esi_leap/common/notification_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def _emit_notification(context, obj, action, level, status,
"%(notification_method)s, payload method "
"%(payload_method)s, error %(error)s"))
payload = payload_method(obj, **extra_args)
event_type = "esi_leap.%s.%s.%s" % (resource, action, status)
notification_method(
publisher=notification.NotificationPublisher(
service='esi-leap-manager', host=CONF.host),
Expand All @@ -78,10 +79,10 @@ def _emit_notification(context, obj, action, level, status,
level=level,
payload=payload).emit(context)
LOG.info("Emit esi_leap notification: host is %s "
"event is esi_leap.%s.%s.%s ,"
"event is %s ,"
"level is %s ,"
"notification method is %s",
CONF.host, resource, action, status,
CONF.host, event_type,
level, notification_method)
except (exception.NotificationSchemaObjectError,
exception.NotificationSchemaKeyError,
Expand Down
10 changes: 10 additions & 0 deletions esi_leap/db/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,13 @@ def resource_check_admin(resource_type, resource_uuid,
return IMPL.resource_check_admin(
resource_type, resource_uuid, start_time, end_time,
default_admin_project_id, project_id)


# Event
@to_dict
def event_get_all():
return IMPL.event_get_all()


def event_create(values):
return IMPL.event_create(values)
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 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.

"""create events table
Revision ID: a1ea63fec697
Revises:
Create Date: 2023-06-26 14:22:34.822066
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'a1ea63fec697'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
op.create_table(
'events',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('event_type', sa.String(length=255), nullable=False),
sa.Column('event_time', sa.DateTime(), nullable=False),
sa.Column('object_type', sa.String(length=255), nullable=True),
sa.Column('object_uuid', sa.String(length=36), nullable=True),
sa.Column('resource_type', sa.String(length=255), nullable=True),
sa.Column('resource_uuid', sa.String(length=36), nullable=True),
sa.Column('lessee_id', sa.String(length=36), nullable=True),
sa.Column('owner_id', sa.String(length=36), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
)

op.create_index('event_type_idx', 'events', ['event_type'],
unique=False)
op.create_index('event_lessee_id_idx', 'events', ['lessee_id'],
unique=False)
op.create_index('event_owner_id_idx', 'events', ['owner_id'],
unique=False)
op.create_index('event_resource_idx', 'events',
['resource_type', 'resource_uuid'],
unique=False)


def downgrade():
pass
34 changes: 34 additions & 0 deletions esi_leap/db/sqlalchemy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,3 +446,37 @@ def resource_verify_availability(r_type, r_uuid, start, end):
raise exception.ResourceTimeConflict(
resource_uuid=r_uuid,
resource_type=r_type)


# Events

def event_get_all(filters):
query = model_query(models.Event)

last_event_time = filters.pop('last_event_time', None)
last_event_id = filters.pop('last_event_id', None)
lessee_or_owner_id = filters.pop('lessee_or_owner_id', None)

query = query.filter_by(**filters)

if last_event_time:
query = query.filter(
last_event_time < models.Event.event_time)
if last_event_id:
query = query.filter(last_event_id < models.Event.id)
if lessee_or_owner_id:
query = query.filter(
(lessee_or_owner_id == models.Event.lessee_id) |
(lessee_or_owner_id == models.Event.owner_id))

return query


def event_create(values):
event_ref = models.Event()
event_ref.update(values)

with _session_for_write() as session:
session.add(event_ref)
session.flush()
return event_ref
23 changes: 23 additions & 0 deletions esi_leap/db/sqlalchemy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,26 @@ class Lease(Base):
'Lease',
backref=orm.backref('child_leases', remote_side=uuid),
)


class Event(Base):
"""Represents an event."""

__tablename__ = 'events'
__table_args__ = (
Index('event_type_idx', 'event_type'),
Index('event_lessee_id_idx', 'lessee_id'),
Index('event_owner_id_idx', 'owner_id'),
Index('event_resource_idx', 'resource_type', 'resource_uuid'),
Index('event_time_idx', 'event_time'),
)

id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
event_type = Column(String(36), nullable=False)
event_time = Column(DateTime, nullable=False)
object_type = Column(String(36), nullable=True)
object_uuid = Column(String(36), nullable=True)
resource_type = Column(String(36), nullable=True)
resource_uuid = Column(String(36), nullable=True)
lessee_id = Column(String(255), nullable=True)
owner_id = Column(String(255), nullable=True)
1 change: 1 addition & 0 deletions esi_leap/objects/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
def register_all():
__import__('esi_leap.objects.event')
__import__('esi_leap.objects.lease')
__import__('esi_leap.objects.offer')
51 changes: 51 additions & 0 deletions esi_leap/objects/event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 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 esi_leap.db import api as dbapi
from esi_leap.objects import base
from esi_leap.objects import fields

from oslo_config import cfg
from oslo_log import log as logging
from oslo_versionedobjects import base as versioned_objects_base

CONF = cfg.CONF
LOG = logging.getLogger(__name__)


@versioned_objects_base.VersionedObjectRegistry.register
class Event(base.ESILEAPObject):
dbapi = dbapi.get_instance()

fields = {
'id': fields.IntegerField(),
'event_type': fields.StringField(),
'event_time': fields.DateTimeField(),
'object_type': fields.StringField(nullable=True),
'object_uuid': fields.StringField(nullable=True),
'resource_type': fields.StringField(nullable=True),
'resource_uuid': fields.StringField(nullable=True),
'lessee_id': fields.StringField(nullable=True),
'owner_id': fields.StringField(nullable=True),
}

@classmethod
def get_all(cls, filters, context=None):
db_events = cls.dbapi.event_get_all(filters)
return cls._from_db_object_list(context, db_events)

def create(self, context=None):
updates = self.obj_get_changes()

LOG.info('Creating event')
db_event = self.dbapi.event_create(updates)
self._from_db_object(context, self, db_event)
12 changes: 12 additions & 0 deletions esi_leap/objects/lease.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ def __init__(self, lease, node):

self.populate_schema(lease=lease, node=node)

def get_event_dict(self, event_type):
event_dict = super().get_event_dict(event_type)
event_dict.update({
'object_type': 'lease',
'object_uuid': self.uuid,
'resource_type': self.resource_type,
'resource_uuid': self.resource_uuid,
'lessee_id': self.project_id,
'owner_id': self.owner_id,
})
return event_dict


CRUD_NOTIFY_OBJ = {
'lease': (LeaseCRUDNotification, LeaseCRUDPayload),
Expand Down
Loading

0 comments on commit 21490a8

Please sign in to comment.