Notifications for metadefinition resources

Metadefinition resources - namespaces, objects, properties, tags and
resource types - don't provide any notification events when certain
operations are performed on them. This patch includes following events
that will be triggered when necessary:

* metadef_namespace.create - namespace has been created
* metadef_namespace.update - namespace has been updated
* metadef_namespace.delete - namespace has been deleted
* metadef_namespace.delete_properties - all properties have been removed
from namespace
* metadef_namespace.delete_objects - all objects have been removed from
namespace
* metadef_namespace.delete_tags - all tags have been removed from
namespace

* metadef_object.create - object has been created
* metadef_object.update - object has been updated
* metadef_object.delete - object has been deleted

* metadef_property.create - property has been created
* metadef_property.update - property has been updated
* metadef_property.delete - property has been deleted

* metadef_tag.create - tag has been created
* metadef_tag.update - tag has been updated
* metadef_tag.delete - tag has been deleted

* metadef_resource_type.create - resource type has been added to
namespace
* metadef_resource_type.delete - resource type has been removed from
namespace

Additionally new configuration option has been added to allow for
disabling either individual or group of notifications.

DocImpact
UpgradeImpact
Depends-On: Iaa771ead0114e3941667b1e07ff32472d2f77afd

Change-Id: Ie1635793d80188f8f7a07aea91b9f0842900ffa6
Implements: blueprint metadefs-notifications
This commit is contained in:
Kamil Rykowski 2015-01-20 14:16:00 +01:00
parent 030250ed64
commit fd547e3717
14 changed files with 1213 additions and 177 deletions

View File

@ -1303,6 +1303,18 @@ Sets the notification driver used by oslo.messaging. Options include
For more information see :doc:`Glance notifications <notifications>` and For more information see :doc:`Glance notifications <notifications>` and
`oslo.messaging <http://docs.openstack.org/developer/oslo.messaging/>`_. `oslo.messaging <http://docs.openstack.org/developer/oslo.messaging/>`_.
* ``disabled_notifications``
Optional. Default: ``[]``
List of disabled notifications. A notification can be given either as a
notification type to disable a single event, or as a notification group prefix
to disable all events within a group.
Example: if this config option is set to ["image.create", "metadef_namespace"],
then "image.create" notification will not be sent after image is created and
none of the notifications for metadefinition namespaces will be sent.
Configuring Glance Property Protections Configuring Glance Property Protections
--------------------------------------- ---------------------------------------

View File

@ -220,6 +220,15 @@ registry_client_protocol = http
# Default publisher_id for outgoing notifications. # Default publisher_id for outgoing notifications.
# default_publisher_id = image.localhost # default_publisher_id = image.localhost
# List of disabled notifications. A notification can be given either as a
# notification type to disable a single event, or as a notification group
# prefix to disable all events within a group.
# Example: if this config option is set to
# ["image.create", "metadef_namespace"], then "image.create" notification will
# not be sent after image is created and none of the notifications for
# metadefinition namespaces will be sent.
# disabled_notifications = []
# Messaging driver used for 'messaging' notifications driver # Messaging driver used for 'messaging' notifications driver
# rpc_backend = 'rabbit' # rpc_backend = 'rabbit'

View File

@ -48,10 +48,12 @@ CONF = cfg.CONF
class NamespaceController(object): class NamespaceController(object):
def __init__(self, db_api=None, policy_enforcer=None): def __init__(self, db_api=None, policy_enforcer=None, notifier=None):
self.db_api = db_api or glance.db.get_api() self.db_api = db_api or glance.db.get_api()
self.policy = policy_enforcer or policy.Enforcer() self.policy = policy_enforcer or policy.Enforcer()
self.notifier = notifier or glance.notifier.Notifier()
self.gateway = glance.gateway.Gateway(db_api=self.db_api, self.gateway = glance.gateway.Gateway(db_api=self.db_api,
notifier=self.notifier,
policy_enforcer=self.policy) policy_enforcer=self.policy)
self.ns_schema_link = '/v2/schemas/metadefs/namespace' self.ns_schema_link = '/v2/schemas/metadefs/namespace'
self.obj_schema_link = '/v2/schemas/metadefs/object' self.obj_schema_link = '/v2/schemas/metadefs/object'
@ -269,6 +271,7 @@ class NamespaceController(object):
namespace_repo = self.gateway.get_metadef_namespace_repo(req.context) namespace_repo = self.gateway.get_metadef_namespace_repo(req.context)
try: try:
ns_obj = namespace_repo.get(namespace) ns_obj = namespace_repo.get(namespace)
ns_obj._old_namespace = ns_obj.namespace
ns_obj.namespace = wsme_utils._get_value(user_ns.namespace) ns_obj.namespace = wsme_utils._get_value(user_ns.namespace)
ns_obj.display_name = wsme_utils._get_value(user_ns.display_name) ns_obj.display_name = wsme_utils._get_value(user_ns.display_name)
ns_obj.description = wsme_utils._get_value(user_ns.description) ns_obj.description = wsme_utils._get_value(user_ns.description)

View File

@ -42,10 +42,12 @@ CONF = cfg.CONF
class MetadefObjectsController(object): class MetadefObjectsController(object):
def __init__(self, db_api=None, policy_enforcer=None): def __init__(self, db_api=None, policy_enforcer=None, notifier=None):
self.db_api = db_api or glance.db.get_api() self.db_api = db_api or glance.db.get_api()
self.policy = policy_enforcer or policy.Enforcer() self.policy = policy_enforcer or policy.Enforcer()
self.notifier = notifier or glance.notifier.Notifier()
self.gateway = glance.gateway.Gateway(db_api=self.db_api, self.gateway = glance.gateway.Gateway(db_api=self.db_api,
notifier=self.notifier,
policy_enforcer=self.policy) policy_enforcer=self.policy)
self.obj_schema_link = '/v2/schemas/metadefs/object' self.obj_schema_link = '/v2/schemas/metadefs/object'
@ -117,6 +119,7 @@ class MetadefObjectsController(object):
meta_repo = self.gateway.get_metadef_object_repo(req.context) meta_repo = self.gateway.get_metadef_object_repo(req.context)
try: try:
metadef_object = meta_repo.get(namespace, object_name) metadef_object = meta_repo.get(namespace, object_name)
metadef_object._old_name = metadef_object.name
metadef_object.name = wsme_utils._get_value( metadef_object.name = wsme_utils._get_value(
metadata_object.name) metadata_object.name)
metadef_object.description = wsme_utils._get_value( metadef_object.description = wsme_utils._get_value(

View File

@ -40,10 +40,12 @@ _LI = i18n._LI
class NamespacePropertiesController(object): class NamespacePropertiesController(object):
def __init__(self, db_api=None, policy_enforcer=None): def __init__(self, db_api=None, policy_enforcer=None, notifier=None):
self.db_api = db_api or glance.db.get_api() self.db_api = db_api or glance.db.get_api()
self.policy = policy_enforcer or policy.Enforcer() self.policy = policy_enforcer or policy.Enforcer()
self.notifier = notifier or glance.notifier.Notifier()
self.gateway = glance.gateway.Gateway(db_api=self.db_api, self.gateway = glance.gateway.Gateway(db_api=self.db_api,
notifier=self.notifier,
policy_enforcer=self.policy) policy_enforcer=self.policy)
def _to_dict(self, model_property_type): def _to_dict(self, model_property_type):
@ -131,6 +133,7 @@ class NamespacePropertiesController(object):
prop_repo = self.gateway.get_metadef_property_repo(req.context) prop_repo = self.gateway.get_metadef_property_repo(req.context)
try: try:
db_property_type = prop_repo.get(namespace, property_name) db_property_type = prop_repo.get(namespace, property_name)
db_property_type._old_name = db_property_type.name
db_property_type.name = property_type.name db_property_type.name = property_type.name
db_property_type.schema = (self._to_dict(property_type))['schema'] db_property_type.schema = (self._to_dict(property_type))['schema']
updated_property_type = prop_repo.save(db_property_type) updated_property_type = prop_repo.save(db_property_type)

View File

@ -40,10 +40,12 @@ _LI = i18n._LI
class ResourceTypeController(object): class ResourceTypeController(object):
def __init__(self, db_api=None, policy_enforcer=None): def __init__(self, db_api=None, policy_enforcer=None, notifier=None):
self.db_api = db_api or glance.db.get_api() self.db_api = db_api or glance.db.get_api()
self.policy = policy_enforcer or policy.Enforcer() self.policy = policy_enforcer or policy.Enforcer()
self.notifier = notifier or glance.notifier.Notifier()
self.gateway = glance.gateway.Gateway(db_api=self.db_api, self.gateway = glance.gateway.Gateway(db_api=self.db_api,
notifier=self.notifier,
policy_enforcer=self.policy) policy_enforcer=self.policy)
def index(self, req): def index(self, req):

View File

@ -41,10 +41,12 @@ CONF = cfg.CONF
class TagsController(object): class TagsController(object):
def __init__(self, db_api=None, policy_enforcer=None): def __init__(self, db_api=None, policy_enforcer=None, notifier=None):
self.db_api = db_api or glance.db.get_api() self.db_api = db_api or glance.db.get_api()
self.policy = policy_enforcer or policy.Enforcer() self.policy = policy_enforcer or policy.Enforcer()
self.notifier = notifier or glance.notifier.Notifier()
self.gateway = glance.gateway.Gateway(db_api=self.db_api, self.gateway = glance.gateway.Gateway(db_api=self.db_api,
notifier=self.notifier,
policy_enforcer=self.policy) policy_enforcer=self.policy)
self.tag_schema_link = '/v2/schemas/metadefs/tag' self.tag_schema_link = '/v2/schemas/metadefs/tag'
@ -141,6 +143,7 @@ class TagsController(object):
meta_repo = self.gateway.get_metadef_tag_repo(req.context) meta_repo = self.gateway.get_metadef_tag_repo(req.context)
try: try:
metadef_tag = meta_repo.get(namespace, tag_name) metadef_tag = meta_repo.get(namespace, tag_name)
metadef_tag._old_name = metadef_tag.name
metadef_tag.name = wsme_utils._get_value( metadef_tag.name = wsme_utils._get_value(
metadata_tag.name) metadata_tag.name)
updated_metadata_tag = meta_repo.save(metadef_tag) updated_metadata_tag = meta_repo.save(metadef_tag)

View File

@ -1060,6 +1060,15 @@ def _task_info_get(task_id):
return task_info return task_info
def _metadef_delete_namespace_content(get_func, key, context, namespace_name):
global DATA
metadefs = get_func(context, namespace_name)
data = DATA[key]
for metadef in metadefs:
data.remove(metadef)
return metadefs
@log_call @log_call
def metadef_namespace_create(context, values): def metadef_namespace_create(context, values):
"""Create a namespace object""" """Create a namespace object"""
@ -1374,6 +1383,13 @@ def metadef_object_delete(context, namespace_name, object_name):
return object return object
def metadef_object_delete_namespace_content(context, namespace_name,
session=None):
"""Delete an object or raise if namespace or object doesn't exist."""
return _metadef_delete_namespace_content(
metadef_object_get_all, 'metadef_objects', context, namespace_name)
@log_call @log_call
def metadef_object_count(context, namespace_name): def metadef_object_count(context, namespace_name):
"""Get metadef object count in a namespace""" """Get metadef object count in a namespace"""
@ -1550,6 +1566,14 @@ def metadef_property_delete(context, namespace_name, property_name):
return property return property
def metadef_property_delete_namespace_content(context, namespace_name,
session=None):
"""Delete a property or raise if it or namespace doesn't exist."""
return _metadef_delete_namespace_content(
metadef_property_get_all, 'metadef_properties', context,
namespace_name)
@log_call @log_call
def metadef_resource_type_create(context, values): def metadef_resource_type_create(context, values):
"""Create a metadef resource type""" """Create a metadef resource type"""
@ -1863,6 +1887,13 @@ def metadef_tag_delete(context, namespace_name, name):
return tags return tags
def metadef_tag_delete_namespace_content(context, namespace_name,
session=None):
"""Delete an tag or raise if namespace or tag doesn't exist."""
return _metadef_delete_namespace_content(
metadef_tag_get_all, 'metadef_tags', context, namespace_name)
@log_call @log_call
def metadef_tag_count(context, namespace_name): def metadef_tag_count(context, namespace_name):
"""Get metadef tag count in a namespace""" """Get metadef tag count in a namespace"""

View File

@ -131,77 +131,103 @@ class Gateway(object):
ns_factory = glance.domain.MetadefNamespaceFactory() ns_factory = glance.domain.MetadefNamespaceFactory()
policy_ns_factory = policy.MetadefNamespaceFactoryProxy( policy_ns_factory = policy.MetadefNamespaceFactoryProxy(
ns_factory, context, self.policy) ns_factory, context, self.policy)
notifier_ns_factory = glance.notifier.MetadefNamespaceFactoryProxy(
policy_ns_factory, context, self.notifier)
authorized_ns_factory = authorization.MetadefNamespaceFactoryProxy( authorized_ns_factory = authorization.MetadefNamespaceFactoryProxy(
policy_ns_factory, context) notifier_ns_factory, context)
return authorized_ns_factory return authorized_ns_factory
def get_metadef_namespace_repo(self, context): def get_metadef_namespace_repo(self, context):
ns_repo = glance.db.MetadefNamespaceRepo(context, self.db_api) ns_repo = glance.db.MetadefNamespaceRepo(context, self.db_api)
policy_ns_repo = policy.MetadefNamespaceRepoProxy( policy_ns_repo = policy.MetadefNamespaceRepoProxy(
ns_repo, context, self.policy) ns_repo, context, self.policy)
notifier_ns_repo = glance.notifier.MetadefNamespaceRepoProxy(
policy_ns_repo, context, self.notifier)
authorized_ns_repo = authorization.MetadefNamespaceRepoProxy( authorized_ns_repo = authorization.MetadefNamespaceRepoProxy(
policy_ns_repo, context) notifier_ns_repo, context)
return authorized_ns_repo return authorized_ns_repo
def get_metadef_object_factory(self, context): def get_metadef_object_factory(self, context):
object_factory = glance.domain.MetadefObjectFactory() object_factory = glance.domain.MetadefObjectFactory()
policy_object_factory = policy.MetadefObjectFactoryProxy( policy_object_factory = policy.MetadefObjectFactoryProxy(
object_factory, context, self.policy) object_factory, context, self.policy)
notifier_object_factory = glance.notifier.MetadefObjectFactoryProxy(
policy_object_factory, context, self.notifier)
authorized_object_factory = authorization.MetadefObjectFactoryProxy( authorized_object_factory = authorization.MetadefObjectFactoryProxy(
policy_object_factory, context) notifier_object_factory, context)
return authorized_object_factory return authorized_object_factory
def get_metadef_object_repo(self, context): def get_metadef_object_repo(self, context):
object_repo = glance.db.MetadefObjectRepo(context, self.db_api) object_repo = glance.db.MetadefObjectRepo(context, self.db_api)
policy_object_repo = policy.MetadefObjectRepoProxy( policy_object_repo = policy.MetadefObjectRepoProxy(
object_repo, context, self.policy) object_repo, context, self.policy)
notifier_object_repo = glance.notifier.MetadefObjectRepoProxy(
policy_object_repo, context, self.notifier)
authorized_object_repo = authorization.MetadefObjectRepoProxy( authorized_object_repo = authorization.MetadefObjectRepoProxy(
policy_object_repo, context) notifier_object_repo, context)
return authorized_object_repo return authorized_object_repo
def get_metadef_resource_type_factory(self, context): def get_metadef_resource_type_factory(self, context):
resource_type_factory = glance.domain.MetadefResourceTypeFactory() resource_type_factory = glance.domain.MetadefResourceTypeFactory()
policy_resource_type_factory = policy.MetadefResourceTypeFactoryProxy( policy_resource_type_factory = policy.MetadefResourceTypeFactoryProxy(
resource_type_factory, context, self.policy) resource_type_factory, context, self.policy)
return authorization.MetadefResourceTypeFactoryProxy( notifier_resource_type_factory = (
policy_resource_type_factory, context) glance.notifier.MetadefResourceTypeFactoryProxy(
policy_resource_type_factory, context, self.notifier)
)
authorized_resource_type_factory = (
authorization.MetadefResourceTypeFactoryProxy(
notifier_resource_type_factory, context)
)
return authorized_resource_type_factory
def get_metadef_resource_type_repo(self, context): def get_metadef_resource_type_repo(self, context):
resource_type_repo = glance.db.MetadefResourceTypeRepo( resource_type_repo = glance.db.MetadefResourceTypeRepo(
context, self.db_api) context, self.db_api)
policy_object_repo = policy.MetadefResourceTypeRepoProxy( policy_object_repo = policy.MetadefResourceTypeRepoProxy(
resource_type_repo, context, self.policy) resource_type_repo, context, self.policy)
return authorization.MetadefResourceTypeRepoProxy(policy_object_repo, notifier_object_repo = glance.notifier.MetadefResourceTypeRepoProxy(
context) policy_object_repo, context, self.notifier)
authorized_object_repo = authorization.MetadefResourceTypeRepoProxy(
notifier_object_repo, context)
return authorized_object_repo
def get_metadef_property_factory(self, context): def get_metadef_property_factory(self, context):
prop_factory = glance.domain.MetadefPropertyFactory() prop_factory = glance.domain.MetadefPropertyFactory()
policy_prop_factory = policy.MetadefPropertyFactoryProxy( policy_prop_factory = policy.MetadefPropertyFactoryProxy(
prop_factory, context, self.policy) prop_factory, context, self.policy)
notifier_prop_factory = glance.notifier.MetadefPropertyFactoryProxy(
policy_prop_factory, context, self.notifier)
authorized_prop_factory = authorization.MetadefPropertyFactoryProxy( authorized_prop_factory = authorization.MetadefPropertyFactoryProxy(
policy_prop_factory, context) notifier_prop_factory, context)
return authorized_prop_factory return authorized_prop_factory
def get_metadef_property_repo(self, context): def get_metadef_property_repo(self, context):
prop_repo = glance.db.MetadefPropertyRepo(context, self.db_api) prop_repo = glance.db.MetadefPropertyRepo(context, self.db_api)
policy_prop_repo = policy.MetadefPropertyRepoProxy( policy_prop_repo = policy.MetadefPropertyRepoProxy(
prop_repo, context, self.policy) prop_repo, context, self.policy)
notifier_prop_repo = glance.notifier.MetadefPropertyRepoProxy(
policy_prop_repo, context, self.notifier)
authorized_prop_repo = authorization.MetadefPropertyRepoProxy( authorized_prop_repo = authorization.MetadefPropertyRepoProxy(
policy_prop_repo, context) notifier_prop_repo, context)
return authorized_prop_repo return authorized_prop_repo
def get_metadef_tag_factory(self, context): def get_metadef_tag_factory(self, context):
tag_factory = glance.domain.MetadefTagFactory() tag_factory = glance.domain.MetadefTagFactory()
policy_tag_factory = policy.MetadefTagFactoryProxy( policy_tag_factory = policy.MetadefTagFactoryProxy(
tag_factory, context, self.policy) tag_factory, context, self.policy)
notifier_tag_factory = glance.notifier.MetadefTagFactoryProxy(
policy_tag_factory, context, self.notifier)
authorized_tag_factory = authorization.MetadefTagFactoryProxy( authorized_tag_factory = authorization.MetadefTagFactoryProxy(
policy_tag_factory, context) notifier_tag_factory, context)
return authorized_tag_factory return authorized_tag_factory
def get_metadef_tag_repo(self, context): def get_metadef_tag_repo(self, context):
tag_repo = glance.db.MetadefTagRepo(context, self.db_api) tag_repo = glance.db.MetadefTagRepo(context, self.db_api)
policy_tag_repo = policy.MetadefTagRepoProxy( policy_tag_repo = policy.MetadefTagRepoProxy(
tag_repo, context, self.policy) tag_repo, context, self.policy)
notifier_tag_repo = glance.notifier.MetadefTagRepoProxy(
policy_tag_repo, context, self.notifier)
authorized_tag_repo = authorization.MetadefTagRepoProxy( authorized_tag_repo = authorization.MetadefTagRepoProxy(
policy_tag_repo, context) notifier_tag_repo, context)
return authorized_tag_repo return authorized_tag_repo

View File

@ -14,16 +14,19 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import abc
import glance_store import glance_store
from oslo import messaging from oslo import messaging
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import timeutils from oslo_utils import timeutils
import six
import webob import webob
from glance.common import exception from glance.common import exception
from glance.common import utils from glance.common import utils
import glance.domain.proxy from glance.domain import proxy as domain_proxy
from glance import i18n from glance import i18n
import glance.openstack.common.log as logging import glance.openstack.common.log as logging
@ -32,6 +35,15 @@ _ = i18n._
notifier_opts = [ notifier_opts = [
cfg.StrOpt('default_publisher_id', default="image.localhost", cfg.StrOpt('default_publisher_id', default="image.localhost",
help='Default publisher_id for outgoing notifications.'), help='Default publisher_id for outgoing notifications.'),
cfg.ListOpt('disabled_notifications', default=[],
help='List of disabled notifications. A notification can be '
'given either as a notification type to disable a single '
'event, or as a notification group prefix to disable all '
'events within a group. Example: if this config option '
'is set to ["image.create", "metadef_namespace"], then '
'"image.create" notification will not be sent after '
'image is created and none of the notifications for '
'metadefinition namespaces will be sent.'),
] ]
CONF = cfg.CONF CONF = cfg.CONF
@ -69,6 +81,27 @@ class Notifier(object):
self._notifier.error({}, event_type, payload) self._notifier.error({}, event_type, payload)
def _get_notification_group(notification):
return notification.split('.', 1)[0]
def _is_notification_enabled(notification):
disabled_notifications = CONF.disabled_notifications
notification_group = _get_notification_group(notification)
notifications = (notification, notification_group)
for disabled_notification in disabled_notifications:
if disabled_notification in notifications:
return False
return True
def _send_notification(notify, notification_type, payload):
if _is_notification_enabled(notification_type):
notify(notification_type, payload)
def format_image_notification(image): def format_image_notification(image):
""" """
Given a glance.domain.Image object, return a dictionary of relevant Given a glance.domain.Image object, return a dictionary of relevant
@ -100,70 +133,211 @@ def format_image_notification(image):
def format_task_notification(task): def format_task_notification(task):
# NOTE(nikhil): input is not passed to the notifier payload as it may # NOTE(nikhil): input is not passed to the notifier payload as it may
# contain sensitive info. # contain sensitive info.
return {'id': task.task_id, return {
'type': task.type, 'id': task.task_id,
'status': task.status, 'type': task.type,
'result': None, 'status': task.status,
'owner': task.owner, 'result': None,
'message': None, 'owner': task.owner,
'expires_at': timeutils.isotime(task.expires_at), 'message': None,
'created_at': timeutils.isotime(task.created_at), 'expires_at': timeutils.isotime(task.expires_at),
'updated_at': timeutils.isotime(task.updated_at), 'created_at': timeutils.isotime(task.created_at),
'deleted': False, 'updated_at': timeutils.isotime(task.updated_at),
'deleted_at': None, 'deleted': False,
} 'deleted_at': None,
}
class ImageRepoProxy(glance.domain.proxy.Repo): def format_metadef_namespace_notification(metadef_namespace):
return {
'namespace': metadef_namespace.namespace,
'namespace_old': metadef_namespace.namespace,
'display_name': metadef_namespace.display_name,
'protected': metadef_namespace.protected,
'visibility': metadef_namespace.visibility,
'owner': metadef_namespace.owner,
'description': metadef_namespace.description,
'created_at': timeutils.isotime(metadef_namespace.created_at),
'updated_at': timeutils.isotime(metadef_namespace.updated_at),
'deleted': False,
'deleted_at': None,
}
def __init__(self, image_repo, context, notifier):
self.image_repo = image_repo def format_metadef_object_notification(metadef_object):
properties = []
for name, prop in six.iteritems(metadef_object.properties):
object_property = _format_metadef_object_property(name, prop)
properties.append(object_property)
return {
'namespace': metadef_object.namespace,
'name': metadef_object.name,
'name_old': metadef_object.name,
'properties': properties,
'required': metadef_object.required,
'description': metadef_object.description,
'created_at': timeutils.isotime(metadef_object.created_at),
'updated_at': timeutils.isotime(metadef_object.updated_at),
'deleted': False,
'deleted_at': None,
}
def _format_metadef_object_property(name, metadef_property):
return {
'name': name,
'type': metadef_property.type or None,
'title': metadef_property.title or None,
'description': metadef_property.description or None,
'default': metadef_property.default or None,
'minimum': metadef_property.minimum or None,
'maximum': metadef_property.maximum or None,
'enum': metadef_property.enum or None,
'pattern': metadef_property.pattern or None,
'minLength': metadef_property.minLength or None,
'maxLength': metadef_property.maxLength or None,
'confidential': metadef_property.confidential or None,
'items': metadef_property.items or None,
'uniqueItems': metadef_property.uniqueItems or None,
'minItems': metadef_property.minItems or None,
'maxItems': metadef_property.maxItems or None,
'additionalItems': metadef_property.additionalItems or None,
}
def format_metadef_property_notification(metadef_property):
schema = metadef_property.schema
return {
'namespace': metadef_property.namespace,
'name': metadef_property.name,
'name_old': metadef_property.name,
'type': schema.get('type'),
'title': schema.get('title'),
'description': schema.get('description'),
'default': schema.get('default'),
'minimum': schema.get('minimum'),
'maximum': schema.get('maximum'),
'enum': schema.get('enum'),
'pattern': schema.get('pattern'),
'minLength': schema.get('minLength'),
'maxLength': schema.get('maxLength'),
'confidential': schema.get('confidential'),
'items': schema.get('items'),
'uniqueItems': schema.get('uniqueItems'),
'minItems': schema.get('minItems'),
'maxItems': schema.get('maxItems'),
'additionalItems': schema.get('additionalItems'),
'deleted': False,
'deleted_at': None,
}
def format_metadef_resource_type_notification(metadef_resource_type):
return {
'namespace': metadef_resource_type.namespace,
'name': metadef_resource_type.name,
'name_old': metadef_resource_type.name,
'prefix': metadef_resource_type.prefix,
'properties_target': metadef_resource_type.properties_target,
'created_at': timeutils.isotime(metadef_resource_type.created_at),
'updated_at': timeutils.isotime(metadef_resource_type.updated_at),
'deleted': False,
'deleted_at': None,
}
def format_metadef_tag_notification(metadef_tag):
return {
'namespace': metadef_tag.namespace,
'name': metadef_tag.name,
'name_old': metadef_tag.name,
'created_at': timeutils.isotime(metadef_tag.created_at),
'updated_at': timeutils.isotime(metadef_tag.updated_at),
'deleted': False,
'deleted_at': None,
}
class NotificationBase(object):
def get_payload(self, obj):
return {}
def send_notification(self, notification_id, obj, extra_payload=None):
payload = self.get_payload(obj)
if extra_payload is not None:
payload.update(extra_payload)
_send_notification(self.notifier.info, notification_id, payload)
@six.add_metaclass(abc.ABCMeta)
class NotificationProxy(NotificationBase):
def __init__(self, repo, context, notifier):
self.repo = repo
self.context = context
self.notifier = notifier
super_class = self.get_super_class()
super_class.__init__(self, repo)
@abc.abstractmethod
def get_super_class(self):
pass
@six.add_metaclass(abc.ABCMeta)
class NotificationRepoProxy(NotificationBase):
def __init__(self, repo, context, notifier):
self.repo = repo
self.context = context self.context = context
self.notifier = notifier self.notifier = notifier
proxy_kwargs = {'context': self.context, 'notifier': self.notifier} proxy_kwargs = {'context': self.context, 'notifier': self.notifier}
super(ImageRepoProxy, self).__init__(image_repo,
item_proxy_class=ImageProxy,
item_proxy_kwargs=proxy_kwargs)
def save(self, image, from_state=None): proxy_class = self.get_proxy_class()
super(ImageRepoProxy, self).save(image, from_state=from_state) super_class = self.get_super_class()
self.notifier.info('image.update', super_class.__init__(self, repo, proxy_class, proxy_kwargs)
format_image_notification(image))
def add(self, image): @abc.abstractmethod
super(ImageRepoProxy, self).add(image) def get_super_class(self):
self.notifier.info('image.create', pass
format_image_notification(image))
def remove(self, image): @abc.abstractmethod
super(ImageRepoProxy, self).remove(image) def get_proxy_class(self):
payload = format_image_notification(image) pass
payload['deleted'] = True
payload['deleted_at'] = timeutils.isotime()
self.notifier.info('image.delete', payload)
class ImageFactoryProxy(glance.domain.proxy.ImageFactory): @six.add_metaclass(abc.ABCMeta)
class NotificationFactoryProxy(object):
def __init__(self, factory, context, notifier): def __init__(self, factory, context, notifier):
kwargs = {'context': context, 'notifier': notifier} kwargs = {'context': context, 'notifier': notifier}
super(ImageFactoryProxy, self).__init__(factory,
proxy_class=ImageProxy, proxy_class = self.get_proxy_class()
proxy_kwargs=kwargs) super_class = self.get_super_class()
super_class.__init__(self, factory, proxy_class, kwargs)
@abc.abstractmethod
def get_super_class(self):
pass
@abc.abstractmethod
def get_proxy_class(self):
pass
class ImageProxy(glance.domain.proxy.Image): class ImageProxy(NotificationProxy, domain_proxy.Image):
def get_super_class(self):
return domain_proxy.Image
def __init__(self, image, context, notifier): def get_payload(self, obj):
self.image = image return format_image_notification(obj)
self.context = context
self.notifier = notifier
super(ImageProxy, self).__init__(image)
def _format_image_send(self, bytes_sent): def _format_image_send(self, bytes_sent):
return { return {
'bytes_sent': bytes_sent, 'bytes_sent': bytes_sent,
'image_id': self.image.image_id, 'image_id': self.repo.image_id,
'owner_id': self.image.owner, 'owner_id': self.repo.owner,
'receiver_tenant_id': self.context.tenant, 'receiver_tenant_id': self.context.tenant,
'receiver_user_id': self.context.user, 'receiver_user_id': self.context.user,
} }
@ -174,14 +348,14 @@ class ImageProxy(glance.domain.proxy.Image):
yield chunk yield chunk
sent += len(chunk) sent += len(chunk)
if sent != (chunk_size or self.image.size): if sent != (chunk_size or self.repo.size):
notify = self.notifier.error notify = self.notifier.error
else: else:
notify = self.notifier.info notify = self.notifier.info
try: try:
notify('image.send', _send_notification(notify, 'image.send',
self._format_image_send(sent)) self._format_image_send(sent))
except Exception as err: except Exception as err:
msg = (_("An error occurred during image.send" msg = (_("An error occurred during image.send"
" notification: %(err)s") % {'err': err}) " notification: %(err)s") % {'err': err})
@ -191,152 +365,454 @@ class ImageProxy(glance.domain.proxy.Image):
# Due to the need of evaluating subsequent proxies, this one # Due to the need of evaluating subsequent proxies, this one
# should return a generator, the call should be done before # should return a generator, the call should be done before
# generator creation # generator creation
data = self.image.get_data(offset=offset, chunk_size=chunk_size) data = self.repo.get_data(offset=offset, chunk_size=chunk_size)
return self._get_chunk_data_iterator(data, chunk_size=chunk_size) return self._get_chunk_data_iterator(data, chunk_size=chunk_size)
def set_data(self, data, size=None): def set_data(self, data, size=None):
payload = format_image_notification(self.image) self.send_notification('image.prepare', self.repo)
self.notifier.info('image.prepare', payload)
notify_error = self.notifier.error
try: try:
self.image.set_data(data, size) self.repo.set_data(data, size)
except glance_store.StorageFull as e: except glance_store.StorageFull as e:
msg = (_("Image storage media is full: %s") % msg = (_("Image storage media is full: %s") %
utils.exception_to_str(e)) utils.exception_to_str(e))
self.notifier.error('image.upload', msg) _send_notification(notify_error, 'image.upload', msg)
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg) raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
except glance_store.StorageWriteDenied as e: except glance_store.StorageWriteDenied as e:
msg = (_("Insufficient permissions on image storage media: %s") msg = (_("Insufficient permissions on image storage media: %s")
% utils.exception_to_str(e)) % utils.exception_to_str(e))
self.notifier.error('image.upload', msg) _send_notification(notify_error, 'image.upload', msg)
raise webob.exc.HTTPServiceUnavailable(explanation=msg) raise webob.exc.HTTPServiceUnavailable(explanation=msg)
except ValueError as e: except ValueError as e:
msg = (_("Cannot save data for image %(image_id)s: %(error)s") % msg = (_("Cannot save data for image %(image_id)s: %(error)s") %
{'image_id': self.image.image_id, {'image_id': self.repo.image_id,
'error': utils.exception_to_str(e)}) 'error': utils.exception_to_str(e)})
self.notifier.error('image.upload', msg) _send_notification(notify_error, 'image.upload', msg)
raise webob.exc.HTTPBadRequest( raise webob.exc.HTTPBadRequest(
explanation=utils.exception_to_str(e)) explanation=utils.exception_to_str(e))
except exception.Duplicate as e: except exception.Duplicate as e:
msg = (_("Unable to upload duplicate image data for image" msg = (_("Unable to upload duplicate image data for image"
"%(image_id)s: %(error)s") % "%(image_id)s: %(error)s") %
{'image_id': self.image.image_id, {'image_id': self.repo.image_id,
'error': utils.exception_to_str(e)}) 'error': utils.exception_to_str(e)})
self.notifier.error('image.upload', msg) _send_notification(notify_error, 'image.upload', msg)
raise webob.exc.HTTPConflict(explanation=msg) raise webob.exc.HTTPConflict(explanation=msg)
except exception.Forbidden as e: except exception.Forbidden as e:
msg = (_("Not allowed to upload image data for image %(image_id)s:" msg = (_("Not allowed to upload image data for image %(image_id)s:"
" %(error)s") % {'image_id': self.image.image_id, " %(error)s") % {'image_id': self.repo.image_id,
'error': utils.exception_to_str(e)}) 'error': utils.exception_to_str(e)})
self.notifier.error('image.upload', msg) _send_notification(notify_error, 'image.upload', msg)
raise webob.exc.HTTPForbidden(explanation=msg) raise webob.exc.HTTPForbidden(explanation=msg)
except exception.NotFound as e: except exception.NotFound as e:
msg = (_("Image %(image_id)s could not be found after upload." msg = (_("Image %(image_id)s could not be found after upload."
" The image may have been deleted during the upload:" " The image may have been deleted during the upload:"
" %(error)s") % {'image_id': self.image.image_id, " %(error)s") % {'image_id': self.repo.image_id,
'error': utils.exception_to_str(e)}) 'error': utils.exception_to_str(e)})
self.notifier.error('image.upload', msg) _send_notification(notify_error, 'image.upload', msg)
raise webob.exc.HTTPNotFound(explanation=utils.exception_to_str(e)) raise webob.exc.HTTPNotFound(explanation=utils.exception_to_str(e))
except webob.exc.HTTPError as e: except webob.exc.HTTPError as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
msg = (_("Failed to upload image data for image %(image_id)s" msg = (_("Failed to upload image data for image %(image_id)s"
" due to HTTP error: %(error)s") % " due to HTTP error: %(error)s") %
{'image_id': self.image.image_id, {'image_id': self.repo.image_id,
'error': utils.exception_to_str(e)}) 'error': utils.exception_to_str(e)})
self.notifier.error('image.upload', msg) _send_notification(notify_error, 'image.upload', msg)
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
msg = (_("Failed to upload image data for image %(image_id)s " msg = (_("Failed to upload image data for image %(image_id)s "
"due to internal error: %(error)s") % "due to internal error: %(error)s") %
{'image_id': self.image.image_id, {'image_id': self.repo.image_id,
'error': utils.exception_to_str(e)}) 'error': utils.exception_to_str(e)})
self.notifier.error('image.upload', msg) _send_notification(notify_error, 'image.upload', msg)
else: else:
payload = format_image_notification(self.image) self.send_notification('image.upload', self.repo)
self.notifier.info('image.upload', payload) self.send_notification('image.activate', self.repo)
self.notifier.info('image.activate', payload)
class TaskRepoProxy(glance.domain.proxy.TaskRepo): class ImageFactoryProxy(NotificationFactoryProxy, domain_proxy.ImageFactory):
def get_super_class(self):
return domain_proxy.ImageFactory
def __init__(self, task_repo, context, notifier): def get_proxy_class(self):
self.task_repo = task_repo return ImageProxy
self.context = context
self.notifier = notifier
proxy_kwargs = {'context': self.context, 'notifier': self.notifier}
super(TaskRepoProxy, self).__init__(task_repo,
task_proxy_class=TaskProxy,
task_proxy_kwargs=proxy_kwargs)
def add(self, task):
self.notifier.info('task.create',
format_task_notification(task))
super(TaskRepoProxy, self).add(task)
def remove(self, task):
payload = format_task_notification(task)
payload['deleted'] = True
payload['deleted_at'] = timeutils.isotime()
self.notifier.info('task.delete', payload)
super(TaskRepoProxy, self).remove(task)
class TaskStubRepoProxy(glance.domain.proxy.TaskStubRepo): class ImageRepoProxy(NotificationRepoProxy, domain_proxy.Repo):
def get_super_class(self):
return domain_proxy.Repo
def __init__(self, task_stub_repo, context, notifier): def get_proxy_class(self):
self.task_stub_repo = task_stub_repo return ImageProxy
self.context = context
self.notifier = notifier def get_payload(self, obj):
proxy_kwargs = {'context': self.context, 'notifier': self.notifier} return format_image_notification(obj)
super(TaskStubRepoProxy, self).__init__(
task_stub_repo, def save(self, image, from_state=None):
task_stub_proxy_class=TaskStubProxy, super(ImageRepoProxy, self).save(image, from_state=from_state)
task_stub_proxy_kwargs=proxy_kwargs) self.send_notification('image.update', image)
def add(self, image):
super(ImageRepoProxy, self).add(image)
self.send_notification('image.create', image)
def remove(self, image):
super(ImageRepoProxy, self).remove(image)
self.send_notification('image.delete', image, extra_payload={
'deleted': True, 'deleted_at': timeutils.isotime()
})
class TaskFactoryProxy(glance.domain.proxy.TaskFactory): class TaskProxy(NotificationProxy, domain_proxy.Task):
def __init__(self, task_factory, context, notifier): def get_super_class(self):
kwargs = {'context': context, 'notifier': notifier} return domain_proxy.Task
super(TaskFactoryProxy, self).__init__(
task_factory,
task_proxy_class=TaskProxy,
task_proxy_kwargs=kwargs)
def get_payload(self, obj):
class TaskProxy(glance.domain.proxy.Task): return format_task_notification(obj)
def __init__(self, task, context, notifier):
self.task = task
self.context = context
self.notifier = notifier
super(TaskProxy, self).__init__(task)
def begin_processing(self): def begin_processing(self):
self.notifier.info( super(TaskProxy, self).begin_processing()
'task.processing', self.send_notification('task.processing', self.repo)
format_task_notification(self.task)
)
return super(TaskProxy, self).begin_processing()
def succeed(self, result): def succeed(self, result):
self.notifier.info('task.success', super(TaskProxy, self).succeed(result)
format_task_notification(self.task)) self.send_notification('task.success', self.repo)
return super(TaskProxy, self).succeed(result)
def fail(self, message): def fail(self, message):
self.notifier.info('task.failure', super(TaskProxy, self).fail(message)
format_task_notification(self.task)) self.send_notification('task.failure', self.repo)
return super(TaskProxy, self).fail(message)
def run(self, executor): def run(self, executor):
self.notifier.info('task.run', super(TaskProxy, self).run(executor)
format_task_notification(self.task)) self.send_notification('task.run', self.repo)
return super(TaskProxy, self).run(executor)
class TaskStubProxy(glance.domain.proxy.TaskStub): class TaskFactoryProxy(NotificationFactoryProxy, domain_proxy.TaskFactory):
def get_super_class(self):
return domain_proxy.TaskFactory
def __init__(self, task, context, notifier): def get_proxy_class(self):
self.task = task return TaskProxy
self.context = context
self.notifier = notifier
super(TaskStubProxy, self).__init__(task) class TaskRepoProxy(NotificationRepoProxy, domain_proxy.TaskRepo):
def get_super_class(self):
return domain_proxy.TaskRepo
def get_proxy_class(self):
return TaskProxy
def get_payload(self, obj):
return format_task_notification(obj)
def add(self, task):
result = super(TaskRepoProxy, self).add(task)
self.send_notification('task.create', task)
return result
def remove(self, task):
result = super(TaskRepoProxy, self).remove(task)
self.send_notification('task.delete', task, extra_payload={
'deleted': True, 'deleted_at': timeutils.isotime()
})
return result
class TaskStubProxy(NotificationProxy, domain_proxy.TaskStub):
def get_super_class(self):
return domain_proxy.TaskStub
class TaskStubRepoProxy(NotificationRepoProxy, domain_proxy.TaskStubRepo):
def get_super_class(self):
return domain_proxy.TaskStubRepo
def get_proxy_class(self):
return TaskStubProxy
class MetadefNamespaceProxy(NotificationProxy, domain_proxy.MetadefNamespace):
def get_super_class(self):
return domain_proxy.MetadefNamespace
class MetadefNamespaceFactoryProxy(NotificationFactoryProxy,
domain_proxy.MetadefNamespaceFactory):
def get_super_class(self):
return domain_proxy.MetadefNamespaceFactory
def get_proxy_class(self):
return MetadefNamespaceProxy
class MetadefNamespaceRepoProxy(NotificationRepoProxy,
domain_proxy.MetadefNamespaceRepo):
def get_super_class(self):
return domain_proxy.MetadefNamespaceRepo
def get_proxy_class(self):
return MetadefNamespaceProxy
def get_payload(self, obj):
return format_metadef_namespace_notification(obj)
def save(self, metadef_namespace):
name = getattr(metadef_namespace, '_old_namespace',
metadef_namespace.namespace)
result = super(MetadefNamespaceRepoProxy, self).save(metadef_namespace)
self.send_notification(
'metadef_namespace.update', metadef_namespace,
extra_payload={
'namespace_old': name,
})
return result
def add(self, metadef_namespace):
result = super(MetadefNamespaceRepoProxy, self).add(metadef_namespace)
self.send_notification('metadef_namespace.create', metadef_namespace)
return result
def remove(self, metadef_namespace):
result = super(MetadefNamespaceRepoProxy, self).remove(
metadef_namespace)
self.send_notification(
'metadef_namespace.delete', metadef_namespace,
extra_payload={'deleted': True, 'deleted_at': timeutils.isotime()}
)
return result
def remove_objects(self, metadef_namespace):
result = super(MetadefNamespaceRepoProxy, self).remove_objects(
metadef_namespace)
self.send_notification('metadef_namespace.delete_objects',
metadef_namespace)
return result
def remove_properties(self, metadef_namespace):
result = super(MetadefNamespaceRepoProxy, self).remove_properties(
metadef_namespace)
self.send_notification('metadef_namespace.delete_properties',
metadef_namespace)
return result
def remove_tags(self, metadef_namespace):
result = super(MetadefNamespaceRepoProxy, self).remove_tags(
metadef_namespace)
self.send_notification('metadef_namespace.delete_tags',
metadef_namespace)
return result
class MetadefObjectProxy(NotificationProxy, domain_proxy.MetadefObject):
def get_super_class(self):
return domain_proxy.MetadefObject
class MetadefObjectFactoryProxy(NotificationFactoryProxy,
domain_proxy.MetadefObjectFactory):
def get_super_class(self):
return domain_proxy.MetadefObjectFactory
def get_proxy_class(self):
return MetadefObjectProxy
class MetadefObjectRepoProxy(NotificationRepoProxy,
domain_proxy.MetadefObjectRepo):
def get_super_class(self):
return domain_proxy.MetadefObjectRepo
def get_proxy_class(self):
return MetadefObjectProxy
def get_payload(self, obj):
return format_metadef_object_notification(obj)
def save(self, metadef_object):
name = getattr(metadef_object, '_old_name', metadef_object.name)
result = super(MetadefObjectRepoProxy, self).save(metadef_object)
self.send_notification(
'metadef_object.update', metadef_object,
extra_payload={
'namespace': metadef_object.namespace.namespace,
'name_old': name,
})
return result
def add(self, metadef_object):
result = super(MetadefObjectRepoProxy, self).add(metadef_object)
self.send_notification('metadef_object.create', metadef_object)
return result
def remove(self, metadef_object):
result = super(MetadefObjectRepoProxy, self).remove(metadef_object)
self.send_notification(
'metadef_object.delete', metadef_object,
extra_payload={
'deleted': True,
'deleted_at': timeutils.isotime(),
'namespace': metadef_object.namespace.namespace
}
)
return result
class MetadefPropertyProxy(NotificationProxy, domain_proxy.MetadefProperty):
def get_super_class(self):
return domain_proxy.MetadefProperty
class MetadefPropertyFactoryProxy(NotificationFactoryProxy,
domain_proxy.MetadefPropertyFactory):
def get_super_class(self):
return domain_proxy.MetadefPropertyFactory
def get_proxy_class(self):
return MetadefPropertyProxy
class MetadefPropertyRepoProxy(NotificationRepoProxy,
domain_proxy.MetadefPropertyRepo):
def get_super_class(self):
return domain_proxy.MetadefPropertyRepo
def get_proxy_class(self):
return MetadefPropertyProxy
def get_payload(self, obj):
return format_metadef_property_notification(obj)
def save(self, metadef_property):
name = getattr(metadef_property, '_old_name', metadef_property.name)
result = super(MetadefPropertyRepoProxy, self).save(metadef_property)
self.send_notification(
'metadef_property.update', metadef_property,
extra_payload={
'namespace': metadef_property.namespace.namespace,
'name_old': name,
})
return result
def add(self, metadef_property):
result = super(MetadefPropertyRepoProxy, self).add(metadef_property)
self.send_notification('metadef_property.create', metadef_property)
return result
def remove(self, metadef_property):
result = super(MetadefPropertyRepoProxy, self).remove(metadef_property)
self.send_notification(
'metadef_property.delete', metadef_property,
extra_payload={
'deleted': True,
'deleted_at': timeutils.isotime(),
'namespace': metadef_property.namespace.namespace
}
)
return result
class MetadefResourceTypeProxy(NotificationProxy,
domain_proxy.MetadefResourceType):
def get_super_class(self):
return domain_proxy.MetadefResourceType
class MetadefResourceTypeFactoryProxy(NotificationFactoryProxy,
domain_proxy.MetadefResourceTypeFactory):
def get_super_class(self):
return domain_proxy.MetadefResourceTypeFactory
def get_proxy_class(self):
return MetadefResourceTypeProxy
class MetadefResourceTypeRepoProxy(NotificationRepoProxy,
domain_proxy.MetadefResourceTypeRepo):
def get_super_class(self):
return domain_proxy.MetadefResourceTypeRepo
def get_proxy_class(self):
return MetadefResourceTypeProxy
def get_payload(self, obj):
return format_metadef_resource_type_notification(obj)
def add(self, md_resource_type):
result = super(MetadefResourceTypeRepoProxy, self).add(
md_resource_type)
self.send_notification('metadef_resource_type.create',
md_resource_type)
return result
def remove(self, md_resource_type):
result = super(MetadefResourceTypeRepoProxy, self).remove(
md_resource_type)
self.send_notification(
'metadef_resource_type.delete', md_resource_type,
extra_payload={
'deleted': True,
'deleted_at': timeutils.isotime(),
'namespace': md_resource_type.namespace.namespace
}
)
return result
class MetadefTagProxy(NotificationProxy, domain_proxy.MetadefTag):
def get_super_class(self):
return domain_proxy.MetadefTag
class MetadefTagFactoryProxy(NotificationFactoryProxy,
domain_proxy.MetadefTagFactory):
def get_super_class(self):
return domain_proxy.MetadefTagFactory
def get_proxy_class(self):
return MetadefTagProxy
class MetadefTagRepoProxy(NotificationRepoProxy, domain_proxy.MetadefTagRepo):
def get_super_class(self):
return domain_proxy.MetadefTagRepo
def get_proxy_class(self):
return MetadefTagProxy
def get_payload(self, obj):
return format_metadef_tag_notification(obj)
def save(self, metadef_tag):
name = getattr(metadef_tag, '_old_name', metadef_tag.name)
result = super(MetadefTagRepoProxy, self).save(metadef_tag)
self.send_notification(
'metadef_tag.update', metadef_tag,
extra_payload={
'namespace': metadef_tag.namespace.namespace,
'name_old': name,
})
return result
def add(self, metadef_tag):
result = super(MetadefTagRepoProxy, self).add(metadef_tag)
self.send_notification('metadef_tag.create', metadef_tag)
return result
def add_tags(self, metadef_tags):
result = super(MetadefTagRepoProxy, self).add_tags(metadef_tags)
for metadef_tag in metadef_tags:
self.send_notification('metadef_tag.create', metadef_tag)
return result
def remove(self, metadef_tag):
result = super(MetadefTagRepoProxy, self).remove(metadef_tag)
self.send_notification(
'metadef_tag.delete', metadef_tag,
extra_payload={
'deleted': True,
'deleted_at': timeutils.isotime(),
'namespace': metadef_tag.namespace.namespace
}
)
return result

View File

@ -173,12 +173,12 @@ class TestImageNotifications(utils.BaseTestCase):
def test_image_get(self): def test_image_get(self):
image = self.image_repo_proxy.get(UUID1) image = self.image_repo_proxy.get(UUID1)
self.assertIsInstance(image, glance.notifier.ImageProxy) self.assertIsInstance(image, glance.notifier.ImageProxy)
self.assertEqual('image_from_get', image.image) self.assertEqual('image_from_get', image.repo)
def test_image_list(self): def test_image_list(self):
images = self.image_repo_proxy.list() images = self.image_repo_proxy.list()
self.assertIsInstance(images[0], glance.notifier.ImageProxy) self.assertIsInstance(images[0], glance.notifier.ImageProxy)
self.assertEqual('images_from_list', images[0].image) self.assertEqual('images_from_list', images[0].repo)
def test_image_get_data_should_call_next_image_get_data(self): def test_image_get_data_should_call_next_image_get_data(self):
with mock.patch.object(self.image, 'get_data') as get_data_mock: with mock.patch.object(self.image, 'get_data') as get_data_mock:

View File

@ -146,6 +146,7 @@ class OptsTestCase(utils.BaseTestCase):
'public_endpoint', 'public_endpoint',
'digest_algorithm', 'digest_algorithm',
'http_keepalive', 'http_keepalive',
'disabled_notifications',
] ]
self._check_opt_groups(opt_list, expected_opt_groups) self._check_opt_groups(opt_list, expected_opt_groups)

View File

@ -600,6 +600,20 @@ class TestImagesController(base.IsolatedUnitTest):
self.assertEqual('image.create', output_log['event_type']) self.assertEqual('image.create', output_log['event_type'])
self.assertEqual('image-1', output_log['payload']['name']) self.assertEqual('image-1', output_log['payload']['name'])
def test_create_disabled_notification(self):
self.config(disabled_notifications=["image.create"])
request = unit_test_utils.get_fake_request()
image = {'name': 'image-1'}
output = self.controller.create(request, image=image,
extra_properties={},
tags=[])
self.assertEqual('image-1', output.name)
self.assertEqual({}, output.extra_properties)
self.assertEqual(set([]), output.tags)
self.assertEqual('private', output.visibility)
output_logs = self.notifier.get_logs()
self.assertEqual(0, len(output_logs))
def test_create_with_properties(self): def test_create_with_properties(self):
request = unit_test_utils.get_fake_request() request = unit_test_utils.get_fake_request()
image_properties = {'foo': 'bar'} image_properties = {'foo': 'bar'}
@ -1781,6 +1795,17 @@ class TestImagesController(base.IsolatedUnitTest):
self.assertEqual('image.update', output_log['event_type']) self.assertEqual('image.update', output_log['event_type'])
self.assertEqual(UUID1, output_log['payload']['id']) self.assertEqual(UUID1, output_log['payload']['id'])
def test_update_disabled_notification(self):
self.config(disabled_notifications=["image.update"])
request = unit_test_utils.get_fake_request()
changes = [
{'op': 'replace', 'path': ['name'], 'value': 'Ping Pong'},
]
output = self.controller.update(request, UUID1, changes)
self.assertEqual('Ping Pong', output.name)
output_logs = self.notifier.get_logs()
self.assertEqual(0, len(output_logs))
def test_delete(self): def test_delete(self):
request = unit_test_utils.get_fake_request() request = unit_test_utils.get_fake_request()
self.assertIn('%s/%s' % (BASE_URI, UUID1), self.store.data) self.assertIn('%s/%s' % (BASE_URI, UUID1), self.store.data)
@ -1800,6 +1825,23 @@ class TestImagesController(base.IsolatedUnitTest):
self.assertEqual('deleted', deleted_img['status']) self.assertEqual('deleted', deleted_img['status'])
self.assertNotIn('%s/%s' % (BASE_URI, UUID1), self.store.data) self.assertNotIn('%s/%s' % (BASE_URI, UUID1), self.store.data)
def test_delete_disabled_notification(self):
self.config(disabled_notifications=["image.delete"])
request = unit_test_utils.get_fake_request()
self.assertIn('%s/%s' % (BASE_URI, UUID1), self.store.data)
try:
self.controller.delete(request, UUID1)
output_logs = self.notifier.get_logs()
self.assertEqual(0, len(output_logs))
except Exception as e:
self.fail("Delete raised exception: %s" % e)
deleted_img = self.db.image_get(request.context, UUID1,
force_show_deleted=True)
self.assertTrue(deleted_img['deleted'])
self.assertEqual('deleted', deleted_img['status'])
self.assertNotIn('%s/%s' % (BASE_URI, UUID1), self.store.data)
def test_delete_queued_updates_status(self): def test_delete_queued_updates_status(self):
"""Ensure status of queued image is updated (LP bug #1048851)""" """Ensure status of queued image is updated (LP bug #1048851)"""
request = unit_test_utils.get_fake_request(is_admin=True) request = unit_test_utils.get_fake_request(is_admin=True)

File diff suppressed because it is too large Load Diff