The ``CallbacksManager`` class considers, by default, that the events starting with "before_" and "precommit_" can raise an Exception (``CallbackFailure``) in case that the callbacks associated to these methods exit with an error. However there are some other events (those started with "after_") that won't generate an exception in case of error. The error will be logged but the process will continue. This new functionality adds the possibility of adding any kind of event and mark is as "cancellable". The ``CallbacksManager`` instance will check the errors returned by the callback methods and if any of them is marked as "cancellable", the manager will raise a ``CallbackFailure`` exception, terminating the process. In case of being a Neutron worker, for example, the ``oslo_service.service.Services`` class will restart the process again. Related-Bug: #2036607 Change-Id: Ie1e7be6d70cca957c1b1b6c15b402e8bc6523865
165 lines
5.6 KiB
Python
165 lines
5.6 KiB
Python
# 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.
|
|
|
|
# String literals representing events associated to data store operations
|
|
BEFORE_CREATE = 'before_create'
|
|
BEFORE_READ = 'before_read'
|
|
BEFORE_UPDATE = 'before_update'
|
|
BEFORE_DELETE = 'before_delete'
|
|
|
|
PRECOMMIT_CREATE = 'precommit_create'
|
|
PRECOMMIT_UPDATE = 'precommit_update'
|
|
PRECOMMIT_DELETE = 'precommit_delete'
|
|
PRECOMMIT_ADD_ASSOCIATION = 'precommit_add_association'
|
|
PRECOMMIT_DELETE_ASSOCIATIONS = 'precommit_delete_associations'
|
|
|
|
AFTER_CREATE = 'after_create'
|
|
AFTER_READ = 'after_read'
|
|
AFTER_UPDATE = 'after_update'
|
|
AFTER_DELETE = 'after_delete'
|
|
AFTER_STATUS_UPDATE = 'after_status_update'
|
|
|
|
# String literals representing events associated to API operations
|
|
BEFORE_RESPONSE = 'before_response'
|
|
AFTER_REQUEST = 'after_request'
|
|
|
|
# String literals representing events associated to process operations
|
|
BEFORE_INIT = 'before_init'
|
|
BEFORE_SPAWN = 'before_spawn' # sent per process
|
|
AFTER_SPAWN = 'after_spawn' # sent per process
|
|
AFTER_INIT = 'after_init' # sent per worker
|
|
|
|
# String literals representing events associated to error conditions
|
|
ABORT_CREATE = 'abort_create'
|
|
ABORT_READ = 'abort_read'
|
|
ABORT_UPDATE = 'abort_update'
|
|
ABORT_DELETE = 'abort_delete'
|
|
|
|
ABORT = 'abort_'
|
|
BEFORE = 'before_'
|
|
PRECOMMIT = 'precommit_'
|
|
|
|
OVS_RESTARTED = 'ovs_restarted'
|
|
|
|
|
|
def is_cancellable_event(event):
|
|
"""Return if an event is cancellable by definition"""
|
|
return (event.startswith(BEFORE) or
|
|
event.startswith(PRECOMMIT))
|
|
|
|
|
|
class EventPayload(object):
|
|
"""Base event payload object.
|
|
|
|
This class is intended to be the super class for all event payloads. As
|
|
such, it defines common attributes many events are likely to use in their
|
|
payload. Note that event attributes are passed by reference; no copying
|
|
of states, metadata or request_body is performed and thus consumers should
|
|
not modify payload references.
|
|
|
|
For more information, see the callbacks dev-ref documentation for this
|
|
project.
|
|
"""
|
|
|
|
def __init__(self, context, metadata=None, request_body=None,
|
|
states=None, resource_id=None):
|
|
# the event context
|
|
self.context = context
|
|
|
|
# NOTE(boden): longer term we should consider removing metadata
|
|
# optional 'unstructured' (key,value) pairs for special needs
|
|
self.metadata = metadata if metadata else {}
|
|
|
|
# the request body associated to the resource
|
|
self.request_body = request_body
|
|
|
|
# an iterable of states for the resource from the newest to the oldest
|
|
# for example db states or api request/response
|
|
# the actual object type for states will vary depending on event caller
|
|
self.states = states if states else []
|
|
|
|
# a unique ID for the event resource; may be None if the resource
|
|
# isn't created yet
|
|
self.resource_id = resource_id
|
|
|
|
@property
|
|
def has_states(self):
|
|
"""Determines if this event payload has any states.
|
|
|
|
:returns: True if this event payload has states, otherwise False.
|
|
"""
|
|
return len(self.states) > 0
|
|
|
|
@property
|
|
def latest_state(self):
|
|
"""Returns the latest state for the event payload.
|
|
|
|
:returns: The last state of this event payload if has_state else None.
|
|
"""
|
|
return self.states[-1] if self.has_states else None
|
|
|
|
|
|
class DBEventPayload(EventPayload):
|
|
"""The payload for data store events payloads."""
|
|
|
|
def __init__(self, context, metadata=None, request_body=None,
|
|
states=None, resource_id=None, desired_state=None):
|
|
|
|
super().__init__(
|
|
context, metadata=metadata, request_body=request_body,
|
|
states=states, resource_id=resource_id)
|
|
|
|
# the model object to be persisted in pre create/commit payloads
|
|
self.desired_state = desired_state
|
|
|
|
@property
|
|
def is_persisted(self):
|
|
"""Determine if the resource for this event payload is persisted.
|
|
|
|
:returns: True if this payload's resource is persisted, otherwise
|
|
False.
|
|
"""
|
|
return self.resource_id is not None and self.has_states
|
|
|
|
@property
|
|
def is_to_be_committed(self):
|
|
""""Determine if the event payload resource is to be committed.
|
|
|
|
:returns: True if the desired state has been populated, else False.
|
|
"""
|
|
return self.desired_state is not None
|
|
|
|
@property
|
|
def latest_state(self):
|
|
"""Returns the latest state for the event payload resource.
|
|
|
|
:returns: If this payload has a desired_state its returned, otherwise
|
|
latest_state is returned.
|
|
"""
|
|
return (self.desired_state or super().latest_state)
|
|
|
|
|
|
class APIEventPayload(EventPayload):
|
|
"""The payload for API events."""
|
|
|
|
def __init__(self, context, method_name, action,
|
|
metadata=None, request_body=None, states=None,
|
|
resource_id=None, collection_name=None):
|
|
|
|
super().__init__(
|
|
context, metadata=metadata, request_body=request_body,
|
|
states=states, resource_id=resource_id)
|
|
|
|
self.method_name = method_name
|
|
self.action = action
|
|
self.collection_name = collection_name
|