update datamodel by nova notifications
Change-Id: Ib2676d6e69eb07644beae66bde22d308bbb836f1 Implements: blueprint update-datamodel-by-nova-notifications
This commit is contained in:
@@ -159,11 +159,7 @@ class NovaClusterDataModelCollector(base.BaseClusterDataModelCollector):
|
||||
:rtype: List of :py:class:`~.EventsNotificationEndpoint` instances
|
||||
"""
|
||||
return [
|
||||
nova.ServiceUpdated(self),
|
||||
|
||||
nova.InstanceCreated(self),
|
||||
nova.InstanceUpdated(self),
|
||||
nova.InstanceDeletedEnd(self),
|
||||
nova.VersionedNotification(self),
|
||||
]
|
||||
|
||||
def get_audit_scope_handler(self, audit_scope):
|
||||
@@ -354,7 +350,8 @@ class ModelBuilder(object):
|
||||
"vcpus": flavor["vcpus"],
|
||||
"state": getattr(instance, "OS-EXT-STS:vm_state"),
|
||||
"metadata": instance.metadata,
|
||||
"project_id": instance.tenant_id}
|
||||
"project_id": instance.tenant_id,
|
||||
"locked": instance.locked}
|
||||
|
||||
# node_attributes = dict()
|
||||
# node_attributes["layer"] = "virtual"
|
||||
|
||||
@@ -30,6 +30,7 @@ class InstanceState(enum.Enum):
|
||||
RESCUED = 'rescued' # A rescue image is running with the original image
|
||||
# attached.
|
||||
RESIZED = 'resized' # an Instance with the new size is active.
|
||||
SHELVED = 'shelved'
|
||||
|
||||
SOFT_DELETED = 'soft-delete'
|
||||
# still available to restore.
|
||||
@@ -53,6 +54,7 @@ class Instance(compute_resource.ComputeResource):
|
||||
"vcpus": wfields.NonNegativeIntegerField(),
|
||||
"metadata": wfields.JsonField(),
|
||||
"project_id": wfields.UUIDField(),
|
||||
"locked": wfields.BooleanField(default=False),
|
||||
}
|
||||
|
||||
def accept(self, visitor):
|
||||
|
||||
@@ -59,6 +59,7 @@ class NovaNotification(base.NotificationEndpoint):
|
||||
return instance
|
||||
|
||||
def update_instance(self, instance, data):
|
||||
n_version = float(data['nova_object.version'])
|
||||
instance_data = data['nova_object.data']
|
||||
instance_flavor_data = instance_data['flavor']['nova_object.data']
|
||||
|
||||
@@ -78,6 +79,9 @@ class NovaNotification(base.NotificationEndpoint):
|
||||
'metadata': instance_metadata,
|
||||
'project_id': instance_data['tenant_id']
|
||||
})
|
||||
# locked was added in nova notification payload version 1.1
|
||||
if n_version > 1.0:
|
||||
instance.update({'locked': instance_data['locked']})
|
||||
|
||||
try:
|
||||
node = self.get_or_create_node(instance_data['host'])
|
||||
@@ -181,30 +185,17 @@ class NovaNotification(base.NotificationEndpoint):
|
||||
except Exception:
|
||||
LOG.info("Instance %s already deleted", instance.uuid)
|
||||
|
||||
|
||||
class VersionedNotificationEndpoint(NovaNotification):
|
||||
publisher_id_regex = r'^nova-compute.*'
|
||||
def delete_node(self, node):
|
||||
try:
|
||||
self.cluster_data_model.remove_node(node)
|
||||
except Exception:
|
||||
LOG.info("Node %s already deleted", node.uuid)
|
||||
|
||||
|
||||
class ServiceUpdated(VersionedNotificationEndpoint):
|
||||
class VersionedNotification(NovaNotification):
|
||||
publisher_id_regex = r'^nova-.*'
|
||||
|
||||
@property
|
||||
def filter_rule(self):
|
||||
"""Nova service.update notification filter"""
|
||||
return filtering.NotificationFilter(
|
||||
publisher_id=self.publisher_id_regex,
|
||||
event_type='service.update',
|
||||
)
|
||||
|
||||
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
||||
ctxt.request_id = metadata['message_id']
|
||||
ctxt.project_domain = event_type
|
||||
LOG.info("Event '%(event)s' received from %(publisher)s "
|
||||
"with metadata %(metadata)s",
|
||||
dict(event=event_type,
|
||||
publisher=publisher_id,
|
||||
metadata=metadata))
|
||||
LOG.debug(payload)
|
||||
def service_updated(self, payload):
|
||||
node_data = payload['nova_object.data']
|
||||
node_uuid = node_data['host']
|
||||
try:
|
||||
@@ -213,44 +204,16 @@ class ServiceUpdated(VersionedNotificationEndpoint):
|
||||
except exception.ComputeNodeNotFound as exc:
|
||||
LOG.exception(exc)
|
||||
|
||||
def service_deleted(self, payload):
|
||||
node_data = payload['nova_object.data']
|
||||
node_uuid = node_data['host']
|
||||
try:
|
||||
node = self.get_or_create_node(node_uuid)
|
||||
self.delete_node(node)
|
||||
except exception.ComputeNodeNotFound as exc:
|
||||
LOG.exception(exc)
|
||||
|
||||
class InstanceCreated(VersionedNotificationEndpoint):
|
||||
|
||||
@property
|
||||
def filter_rule(self):
|
||||
"""Nova instance.update notification filter"""
|
||||
return filtering.NotificationFilter(
|
||||
publisher_id=self.publisher_id_regex,
|
||||
event_type='instance.update',
|
||||
# To be "fully" created, an instance transitions
|
||||
# from the 'building' state to the 'active' one.
|
||||
# See https://docs.openstack.org/nova/latest/reference/
|
||||
# vm-states.html
|
||||
|
||||
payload={
|
||||
'nova_object.data': {
|
||||
'state': element.InstanceState.ACTIVE.value,
|
||||
'state_update': {
|
||||
'nova_object.data': {
|
||||
'old_state': element.InstanceState.BUILDING.value,
|
||||
'state': element.InstanceState.ACTIVE.value,
|
||||
},
|
||||
'nova_object.name': 'InstanceStateUpdatePayload',
|
||||
'nova_object.namespace': 'nova',
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
||||
ctxt.request_id = metadata['message_id']
|
||||
ctxt.project_domain = event_type
|
||||
LOG.info("Event '%(event)s' received from %(publisher)s "
|
||||
"with metadata %(metadata)s",
|
||||
dict(event=event_type,
|
||||
publisher=publisher_id,
|
||||
metadata=metadata))
|
||||
LOG.debug(payload)
|
||||
def instance_updated(self, payload):
|
||||
instance_data = payload['nova_object.data']
|
||||
instance_uuid = instance_data['uuid']
|
||||
node_uuid = instance_data.get('host')
|
||||
@@ -258,62 +221,7 @@ class InstanceCreated(VersionedNotificationEndpoint):
|
||||
|
||||
self.update_instance(instance, payload)
|
||||
|
||||
|
||||
class InstanceUpdated(VersionedNotificationEndpoint):
|
||||
|
||||
@staticmethod
|
||||
def _match_not_new_instance_state(data):
|
||||
is_new_instance = (
|
||||
data['old_state'] == element.InstanceState.BUILDING.value and
|
||||
data['state'] == element.InstanceState.ACTIVE.value)
|
||||
|
||||
return not is_new_instance
|
||||
|
||||
@property
|
||||
def filter_rule(self):
|
||||
"""Nova instance.update notification filter"""
|
||||
return filtering.NotificationFilter(
|
||||
publisher_id=self.publisher_id_regex,
|
||||
event_type='instance.update',
|
||||
)
|
||||
|
||||
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
||||
ctxt.request_id = metadata['message_id']
|
||||
ctxt.project_domain = event_type
|
||||
LOG.info("Event '%(event)s' received from %(publisher)s "
|
||||
"with metadata %(metadata)s",
|
||||
dict(event=event_type,
|
||||
publisher=publisher_id,
|
||||
metadata=metadata))
|
||||
LOG.debug(payload)
|
||||
instance_data = payload['nova_object.data']
|
||||
instance_uuid = instance_data['uuid']
|
||||
node_uuid = instance_data.get('host')
|
||||
instance = self.get_or_create_instance(instance_uuid, node_uuid)
|
||||
|
||||
self.update_instance(instance, payload)
|
||||
|
||||
|
||||
class InstanceDeletedEnd(VersionedNotificationEndpoint):
|
||||
|
||||
@property
|
||||
def filter_rule(self):
|
||||
"""Nova service.update notification filter"""
|
||||
return filtering.NotificationFilter(
|
||||
publisher_id=self.publisher_id_regex,
|
||||
event_type='instance.delete.end',
|
||||
)
|
||||
|
||||
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
||||
ctxt.request_id = metadata['message_id']
|
||||
ctxt.project_domain = event_type
|
||||
LOG.info("Event '%(event)s' received from %(publisher)s "
|
||||
"with metadata %(metadata)s",
|
||||
dict(event=event_type,
|
||||
publisher=publisher_id,
|
||||
metadata=metadata))
|
||||
LOG.debug(payload)
|
||||
|
||||
def instance_deleted(self, payload):
|
||||
instance_data = payload['nova_object.data']
|
||||
instance_uuid = instance_data['uuid']
|
||||
node_uuid = instance_data.get('host')
|
||||
@@ -327,3 +235,49 @@ class InstanceDeletedEnd(VersionedNotificationEndpoint):
|
||||
node = None
|
||||
|
||||
self.delete_instance(instance, node)
|
||||
|
||||
notification_mapping = {
|
||||
'instance.create.end': instance_updated,
|
||||
'instance.lock': instance_updated,
|
||||
'instance.unlock': instance_updated,
|
||||
'instance.pause.end': instance_updated,
|
||||
'instance.power_off.end': instance_updated,
|
||||
'instance.power_on.end': instance_updated,
|
||||
'instance.resize_confirm.end': instance_updated,
|
||||
'instance.restore.end': instance_updated,
|
||||
'instance.resume.end': instance_updated,
|
||||
'instance.shelve.end': instance_updated,
|
||||
'instance.shutdown.end': instance_updated,
|
||||
'instance.suspend.end': instance_updated,
|
||||
'instance.unpause.end': instance_updated,
|
||||
'instance.unrescue.end': instance_updated,
|
||||
'instance.unshelve.end': instance_updated,
|
||||
'instance.rebuild.end': instance_updated,
|
||||
'instance.rescue.end': instance_updated,
|
||||
'instance.update': instance_updated,
|
||||
'instance.live_migration_force_complete.end': instance_updated,
|
||||
'instance.live_migration_post_dest.end': instance_updated,
|
||||
'instance.delete.end': instance_deleted,
|
||||
'instance.soft_delete.end': instance_deleted,
|
||||
'service.create': service_updated,
|
||||
'service.delete': service_deleted,
|
||||
'service.update': service_updated,
|
||||
}
|
||||
|
||||
@property
|
||||
def filter_rule(self):
|
||||
"""Nova notification filter"""
|
||||
return filtering.NotificationFilter(
|
||||
publisher_id=self.publisher_id_regex,
|
||||
)
|
||||
|
||||
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
||||
LOG.info("Event '%(event)s' received from %(publisher)s "
|
||||
"with metadata %(metadata)s",
|
||||
dict(event=event_type,
|
||||
publisher=publisher_id,
|
||||
metadata=metadata))
|
||||
func = self.notification_mapping.get(event_type)
|
||||
if func:
|
||||
LOG.debug(payload)
|
||||
func(self, payload)
|
||||
|
||||
Reference in New Issue
Block a user