Transform compute_task notifications
The following notifications have been transformed to the versioned notification framework. * compute_task.build_instances * compute_task.migrate_server * compute_task.rebuild_server Co-Authored-By: Takashi Natsume <natsume.takashi@lab.ntt.co.jp> Change-Id: Ibfb0a6db5920d921c4fc7cabf3f4d2838ea7f421 Implements: bp versioned-notification-transformation-stein
This commit is contained in:
parent
7217e38baf
commit
fe4e47d989
@ -207,6 +207,7 @@
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"architecture": "x86_64",
|
||||
"kernel_id": "nokernel",
|
||||
"ramdisk_id": "nokernel"
|
||||
},
|
||||
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"nova_object.version": "1.0",
|
||||
"nova_object.namespace": "nova",
|
||||
"nova_object.name": "ComputeTaskPayload",
|
||||
"nova_object.data": {
|
||||
"instance_uuid": "d5e6a7b7-80e5-4166-85a3-cd6115201082",
|
||||
"reason": {"$ref": "ExceptionPayload.json#"},
|
||||
"request_spec": {"$ref": "RequestSpecPayload.json#"},
|
||||
"state": "error"
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"nova_object.version": "1.1",
|
||||
"nova_object.namespace": "nova",
|
||||
"nova_object.name": "ExceptionPayload",
|
||||
"nova_object.data": {
|
||||
"function_name": "_schedule_instances",
|
||||
"module_name": "nova.conductor.manager",
|
||||
"exception": "NoValidHost",
|
||||
"exception_message": "No valid host was found. There are not enough hosts available.",
|
||||
"traceback": "Traceback (most recent call last):\n File \"nova/conductor/manager.py\", line ..."
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
{
|
||||
"nova_object.namespace": "nova",
|
||||
"nova_object.data": {
|
||||
"checksum": null,
|
||||
"container_format": "raw",
|
||||
"created_at": "2011-01-01T01:02:03Z",
|
||||
"direct_url": null,
|
||||
"disk_format": "raw",
|
||||
"id": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
|
||||
"min_disk": 0,
|
||||
"min_ram": 0,
|
||||
"name": "fakeimage123456",
|
||||
"owner": null,
|
||||
"properties": {"$ref":"ImageMetaPropsPayload.json#"},
|
||||
"protected": false,
|
||||
"size": 25165824,
|
||||
"status": "active",
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
],
|
||||
"updated_at": "2011-01-01T01:02:03Z",
|
||||
"virtual_size": null,
|
||||
"visibility": "public"
|
||||
},
|
||||
"nova_object.name": "ImageMetaPayload",
|
||||
"nova_object.version": "1.0"
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"nova_object.namespace": "nova",
|
||||
"nova_object.data": {
|
||||
"hw_architecture": "x86_64"
|
||||
},
|
||||
"nova_object.name": "ImageMetaPropsPayload",
|
||||
"nova_object.version": "1.0"
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"nova_object.version": "1.0",
|
||||
"nova_object.namespace": "nova",
|
||||
"nova_object.name": "InstanceNUMACellPayload",
|
||||
"nova_object.data": {
|
||||
"cpu_pinning_raw": null,
|
||||
"cpu_policy": null,
|
||||
"cpu_thread_policy": null,
|
||||
"cpu_topology": null,
|
||||
"cpuset": [0],
|
||||
"cpuset_reserved": null,
|
||||
"id": 0,
|
||||
"memory": 512,
|
||||
"pagesize": null
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"nova_object.version": "1.0",
|
||||
"nova_object.namespace": "nova",
|
||||
"nova_object.name": "InstanceNUMATopologyPayload",
|
||||
"nova_object.data": {
|
||||
"cells": [
|
||||
{"$ref": "InstanceNUMACellPayload.json#"}
|
||||
],
|
||||
"emulator_threads_policy": null,
|
||||
"instance_uuid": "75cab9f7-57e2-4bd1-984f-a0383d9ee60e"
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"nova_object.version": "1.0",
|
||||
"nova_object.namespace": "nova",
|
||||
"nova_object.name": "InstancePCIRequestsPayload",
|
||||
"nova_object.data":{
|
||||
"instance_uuid": "d5e6a7b7-80e5-4166-85a3-cd6115201082",
|
||||
"requests": []
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"nova_object.namespace": "nova",
|
||||
"nova_object.data": {
|
||||
"availability_zone": null,
|
||||
"flavor": {
|
||||
"$ref": "FlavorPayload.json#",
|
||||
"nova_object.data": {
|
||||
"extra_specs": {
|
||||
"hw:numa_cpus.0": "0",
|
||||
"hw:numa_mem.0": "512",
|
||||
"hw:numa_nodes": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"image": {"$ref": "ImageMetaPayload.json#"},
|
||||
"instance_uuid": "d5e6a7b7-80e5-4166-85a3-cd6115201082",
|
||||
"num_instances": 1,
|
||||
"numa_topology": {"$ref": "InstanceNUMATopologyPayload.json#"},
|
||||
"pci_requests": {"$ref": "InstancePCIRequestsPayload.json#"},
|
||||
"project_id": "6f70656e737461636b20342065766572",
|
||||
"user_id": "fake"
|
||||
},
|
||||
"nova_object.name": "RequestSpecPayload",
|
||||
"nova_object.version": "1.0"
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"event_type": "compute_task.build_instances.error",
|
||||
"payload": {"$ref":"common_payloads/ComputeTaskPayload.json#"},
|
||||
"priority": "ERROR",
|
||||
"publisher_id": "nova-conductor:fake-mini"
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"event_type": "compute_task.migrate_server.error",
|
||||
"payload": {
|
||||
"$ref":"common_payloads/ComputeTaskPayload.json#",
|
||||
"nova_object.data":{
|
||||
"state": "active"
|
||||
}
|
||||
},
|
||||
"priority": "ERROR",
|
||||
"publisher_id": "nova-conductor:fake-mini"
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"event_type": "compute_task.rebuild_server.error",
|
||||
"payload": {
|
||||
"$ref": "common_payloads/ComputeTaskPayload.json#"
|
||||
},
|
||||
"priority": "ERROR",
|
||||
"publisher_id": "nova-conductor:fake-mini"
|
||||
}
|
@ -36,6 +36,7 @@ from nova import exception
|
||||
from nova import notifications
|
||||
from nova.notifications.objects import aggregate as aggregate_notification
|
||||
from nova.notifications.objects import base as notification_base
|
||||
from nova.notifications.objects import compute_task as task_notification
|
||||
from nova.notifications.objects import exception as notification_exception
|
||||
from nova.notifications.objects import flavor as flavor_notification
|
||||
from nova.notifications.objects import instance as instance_notification
|
||||
@ -857,6 +858,41 @@ def notify_about_volume_usage(context, vol_usage, host):
|
||||
notification.emit(context)
|
||||
|
||||
|
||||
@rpc.if_notifications_enabled
|
||||
def notify_about_compute_task_error(context, action, instance_uuid,
|
||||
request_spec, state, exception, tb):
|
||||
"""Send a versioned notification about compute task error.
|
||||
|
||||
:param context: the request context
|
||||
:param action: the name of the action
|
||||
:param instance_uuid: the UUID of the instance
|
||||
:param request_spec: the request spec object or
|
||||
the dict includes request spec information
|
||||
:param state: the vm state of the instance
|
||||
:param exception: the thrown exception
|
||||
:param tb: the traceback
|
||||
"""
|
||||
if (request_spec is not None and
|
||||
not isinstance(request_spec, objects.RequestSpec)):
|
||||
request_spec = objects.RequestSpec.from_primitives(
|
||||
context, request_spec, {})
|
||||
|
||||
fault, _ = _get_fault_and_priority_from_exc_and_tb(exception, tb)
|
||||
payload = task_notification.ComputeTaskPayload(
|
||||
instance_uuid=instance_uuid, request_spec=request_spec, state=state,
|
||||
reason=fault)
|
||||
notification = task_notification.ComputeTaskNotification(
|
||||
priority=fields.NotificationPriority.ERROR,
|
||||
publisher=notification_base.NotificationPublisher(
|
||||
host=CONF.host, source=fields.NotificationSource.CONDUCTOR),
|
||||
event_type=notification_base.EventType(
|
||||
object='compute_task',
|
||||
action=action,
|
||||
phase=fields.NotificationPhase.ERROR),
|
||||
payload=payload)
|
||||
notification.emit(context)
|
||||
|
||||
|
||||
def refresh_info_cache_for_instance(context, instance):
|
||||
"""Refresh the info cache for an instance.
|
||||
|
||||
|
@ -67,7 +67,9 @@ class EventType(NotificationObject):
|
||||
# NotificationActionField enum
|
||||
# Version 1.16: CONNECT is added to NotificationActionField enum
|
||||
# Version 1.17: USAGE is added to NotificationActionField enum
|
||||
VERSION = '1.17'
|
||||
# Version 1.18: ComputeTask related values have been added to
|
||||
# NotificationActionField enum
|
||||
VERSION = '1.18'
|
||||
|
||||
fields = {
|
||||
'object': fields.StringField(nullable=False),
|
||||
@ -119,7 +121,7 @@ class NotificationPayloadBase(NotificationObject):
|
||||
self.populated = not self.SCHEMA
|
||||
|
||||
@rpc.if_notifications_enabled
|
||||
def populate_schema(self, **kwargs):
|
||||
def populate_schema(self, set_none=True, **kwargs):
|
||||
"""Populate the object based on the SCHEMA and the source objects
|
||||
|
||||
:param kwargs: A dict contains the source object at the key defined in
|
||||
@ -137,14 +139,15 @@ class NotificationPayloadBase(NotificationObject):
|
||||
NotImplementedError,
|
||||
exception.OrphanedObjectError,
|
||||
ovo_exception.OrphanedObjectError):
|
||||
# If it is unset or non lazy loadable in the source object
|
||||
# then we cannot do anything else but try to default it in the
|
||||
# payload object we are generating here.
|
||||
# NOTE(gibi): This will fail if the payload field is not
|
||||
# nullable, but that means that either the source object is not
|
||||
# properly initialized or the payload field needs to be defined
|
||||
# as nullable
|
||||
setattr(self, key, None)
|
||||
if set_none:
|
||||
# If it is unset or non lazy loadable in the source object
|
||||
# then we cannot do anything else but try to default it
|
||||
# in the payload object we are generating here.
|
||||
# NOTE(gibi): This will fail if the payload field is not
|
||||
# nullable, but that means that either the source object
|
||||
# is not properly initialized or the payload field needs
|
||||
# to be defined as nullable
|
||||
setattr(self, key, None)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error('Failed trying to populate attribute "%s" '
|
||||
|
54
nova/notifications/objects/compute_task.py
Normal file
54
nova/notifications/objects/compute_task.py
Normal file
@ -0,0 +1,54 @@
|
||||
# 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 nova.notifications.objects import base
|
||||
from nova.notifications.objects import request_spec as reqspec_payload
|
||||
from nova.objects import base as nova_base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class ComputeTaskPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'instance_uuid': fields.UUIDField(),
|
||||
# There are some cases that request_spec is None.
|
||||
# e.g. Old instances can still have no RequestSpec object
|
||||
# attached to them.
|
||||
'request_spec': fields.ObjectField('RequestSpecPayload',
|
||||
nullable=True),
|
||||
'state': fields.InstanceStateField(nullable=True),
|
||||
'reason': fields.ObjectField('ExceptionPayload')
|
||||
}
|
||||
|
||||
def __init__(self, instance_uuid, request_spec, state, reason):
|
||||
super(ComputeTaskPayload, self).__init__()
|
||||
self.instance_uuid = instance_uuid
|
||||
self.request_spec = reqspec_payload.RequestSpecPayload(
|
||||
request_spec) if request_spec is not None else None
|
||||
self.state = state
|
||||
self.reason = reason
|
||||
|
||||
|
||||
@base.notification_sample('compute_task-build_instances-error.json')
|
||||
@base.notification_sample('compute_task-migrate_server-error.json')
|
||||
@base.notification_sample('compute_task-rebuild_server-error.json')
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class ComputeTaskNotification(base.NotificationBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'payload': fields.ObjectField('ComputeTaskPayload')
|
||||
}
|
261
nova/notifications/objects/image.py
Normal file
261
nova/notifications/objects/image.py
Normal file
@ -0,0 +1,261 @@
|
||||
# Copyright 2018 NTT Corporation
|
||||
#
|
||||
# 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 nova.notifications.objects import base
|
||||
from nova.objects import base as nova_base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class ImageMetaPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
SCHEMA = {
|
||||
'id': ('image_meta', 'id'),
|
||||
'name': ('image_meta', 'name'),
|
||||
'status': ('image_meta', 'status'),
|
||||
'visibility': ('image_meta', 'visibility'),
|
||||
'protected': ('image_meta', 'protected'),
|
||||
'checksum': ('image_meta', 'checksum'),
|
||||
'owner': ('image_meta', 'owner'),
|
||||
'size': ('image_meta', 'size'),
|
||||
'virtual_size': ('image_meta', 'virtual_size'),
|
||||
'container_format': ('image_meta', 'container_format'),
|
||||
'disk_format': ('image_meta', 'disk_format'),
|
||||
'created_at': ('image_meta', 'created_at'),
|
||||
'updated_at': ('image_meta', 'updated_at'),
|
||||
'tags': ('image_meta', 'tags'),
|
||||
'direct_url': ('image_meta', 'direct_url'),
|
||||
'min_ram': ('image_meta', 'min_ram'),
|
||||
'min_disk': ('image_meta', 'min_disk')
|
||||
}
|
||||
|
||||
# NOTE(takashin): The reason that each field is nullable is as follows.
|
||||
#
|
||||
# a. It is defined as "The value might be null (JSON null data type)."
|
||||
# in the "Show image" API (GET /v2/images/{image_id})
|
||||
# in the glance API v2 Reference.
|
||||
# (https://developer.openstack.org/api-ref/image/v2/index.html)
|
||||
#
|
||||
# * checksum
|
||||
# * container_format
|
||||
# * disk_format
|
||||
# * min_disk
|
||||
# * min_ram
|
||||
# * name
|
||||
# * owner
|
||||
# * size
|
||||
# * updated_at
|
||||
# * virtual_size
|
||||
#
|
||||
# b. It is optional in the response from glance.
|
||||
# * direct_url
|
||||
#
|
||||
# a. It is defined as nullable in the ImageMeta object.
|
||||
# * created_at
|
||||
#
|
||||
# c. It cannot be got in the boot from volume case.
|
||||
# See VIM_IMAGE_ATTRIBUTES in nova/utils.py.
|
||||
#
|
||||
# * id (not 'image_id')
|
||||
# * visibility
|
||||
# * protected
|
||||
# * status
|
||||
# * tags
|
||||
fields = {
|
||||
'id': fields.UUIDField(nullable=True),
|
||||
'name': fields.StringField(nullable=True),
|
||||
'status': fields.StringField(nullable=True),
|
||||
'visibility': fields.StringField(nullable=True),
|
||||
'protected': fields.FlexibleBooleanField(nullable=True),
|
||||
'checksum': fields.StringField(nullable=True),
|
||||
'owner': fields.StringField(nullable=True),
|
||||
'size': fields.IntegerField(nullable=True),
|
||||
'virtual_size': fields.IntegerField(nullable=True),
|
||||
'container_format': fields.StringField(nullable=True),
|
||||
'disk_format': fields.StringField(nullable=True),
|
||||
'created_at': fields.DateTimeField(nullable=True),
|
||||
'updated_at': fields.DateTimeField(nullable=True),
|
||||
'tags': fields.ListOfStringsField(nullable=True),
|
||||
'direct_url': fields.StringField(nullable=True),
|
||||
'min_ram': fields.IntegerField(nullable=True),
|
||||
'min_disk': fields.IntegerField(nullable=True),
|
||||
'properties': fields.ObjectField('ImageMetaPropsPayload')
|
||||
}
|
||||
|
||||
def __init__(self, image_meta):
|
||||
super(ImageMetaPayload, self).__init__()
|
||||
self.properties = ImageMetaPropsPayload(
|
||||
image_meta_props=image_meta.properties)
|
||||
self.populate_schema(image_meta=image_meta)
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class ImageMetaPropsPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
SCHEMA = {
|
||||
'hw_architecture': ('image_meta_props', 'hw_architecture'),
|
||||
'hw_auto_disk_config': ('image_meta_props', 'hw_auto_disk_config'),
|
||||
'hw_boot_menu': ('image_meta_props', 'hw_boot_menu'),
|
||||
'hw_cdrom_bus': ('image_meta_props', 'hw_cdrom_bus'),
|
||||
'hw_cpu_cores': ('image_meta_props', 'hw_cpu_cores'),
|
||||
'hw_cpu_sockets': ('image_meta_props', 'hw_cpu_sockets'),
|
||||
'hw_cpu_max_cores': ('image_meta_props', 'hw_cpu_max_cores'),
|
||||
'hw_cpu_max_sockets': ('image_meta_props', 'hw_cpu_max_sockets'),
|
||||
'hw_cpu_max_threads': ('image_meta_props', 'hw_cpu_max_threads'),
|
||||
'hw_cpu_policy': ('image_meta_props', 'hw_cpu_policy'),
|
||||
'hw_cpu_thread_policy': ('image_meta_props', 'hw_cpu_thread_policy'),
|
||||
'hw_cpu_realtime_mask': ('image_meta_props', 'hw_cpu_realtime_mask'),
|
||||
'hw_cpu_threads': ('image_meta_props', 'hw_cpu_threads'),
|
||||
'hw_device_id': ('image_meta_props', 'hw_device_id'),
|
||||
'hw_disk_bus': ('image_meta_props', 'hw_disk_bus'),
|
||||
'hw_disk_type': ('image_meta_props', 'hw_disk_type'),
|
||||
'hw_floppy_bus': ('image_meta_props', 'hw_floppy_bus'),
|
||||
'hw_firmware_type': ('image_meta_props', 'hw_firmware_type'),
|
||||
'hw_ipxe_boot': ('image_meta_props', 'hw_ipxe_boot'),
|
||||
'hw_machine_type': ('image_meta_props', 'hw_machine_type'),
|
||||
'hw_mem_page_size': ('image_meta_props', 'hw_mem_page_size'),
|
||||
'hw_numa_nodes': ('image_meta_props', 'hw_numa_nodes'),
|
||||
'hw_numa_cpus': ('image_meta_props', 'hw_numa_cpus'),
|
||||
'hw_numa_mem': ('image_meta_props', 'hw_numa_mem'),
|
||||
'hw_pointer_model': ('image_meta_props', 'hw_pointer_model'),
|
||||
'hw_qemu_guest_agent': ('image_meta_props', 'hw_qemu_guest_agent'),
|
||||
'hw_rescue_bus': ('image_meta_props', 'hw_rescue_bus'),
|
||||
'hw_rescue_device': ('image_meta_props', 'hw_rescue_device'),
|
||||
'hw_rng_model': ('image_meta_props', 'hw_rng_model'),
|
||||
'hw_serial_port_count': ('image_meta_props', 'hw_serial_port_count'),
|
||||
'hw_scsi_model': ('image_meta_props', 'hw_scsi_model'),
|
||||
'hw_video_model': ('image_meta_props', 'hw_video_model'),
|
||||
'hw_video_ram': ('image_meta_props', 'hw_video_ram'),
|
||||
'hw_vif_model': ('image_meta_props', 'hw_vif_model'),
|
||||
'hw_vm_mode': ('image_meta_props', 'hw_vm_mode'),
|
||||
'hw_watchdog_action': ('image_meta_props', 'hw_watchdog_action'),
|
||||
'hw_vif_multiqueue_enabled': ('image_meta_props',
|
||||
'hw_vif_multiqueue_enabled'),
|
||||
'img_bittorrent': ('image_meta_props', 'img_bittorrent'),
|
||||
'img_bdm_v2': ('image_meta_props', 'img_bdm_v2'),
|
||||
'img_block_device_mapping': ('image_meta_props',
|
||||
'img_block_device_mapping'),
|
||||
'img_cache_in_nova': ('image_meta_props', 'img_cache_in_nova'),
|
||||
'img_compression_level': ('image_meta_props', 'img_compression_level'),
|
||||
'img_hv_requested_version': ('image_meta_props',
|
||||
'img_hv_requested_version'),
|
||||
'img_hv_type': ('image_meta_props', 'img_hv_type'),
|
||||
'img_config_drive': ('image_meta_props', 'img_config_drive'),
|
||||
'img_linked_clone': ('image_meta_props', 'img_linked_clone'),
|
||||
'img_mappings': ('image_meta_props', 'img_mappings'),
|
||||
'img_owner_id': ('image_meta_props', 'img_owner_id'),
|
||||
'img_root_device_name': ('image_meta_props', 'img_root_device_name'),
|
||||
'img_use_agent': ('image_meta_props', 'img_use_agent'),
|
||||
'img_version': ('image_meta_props', 'img_version'),
|
||||
'img_signature': ('image_meta_props', 'img_signature'),
|
||||
'img_signature_hash_method': ('image_meta_props',
|
||||
'img_signature_hash_method'),
|
||||
'img_signature_certificate_uuid': ('image_meta_props',
|
||||
'img_signature_certificate_uuid'),
|
||||
'img_signature_key_type': ('image_meta_props',
|
||||
'img_signature_key_type'),
|
||||
'img_hide_hypervisor_id': ('image_meta_props',
|
||||
'img_hide_hypervisor_id'),
|
||||
'os_admin_user': ('image_meta_props', 'os_admin_user'),
|
||||
'os_command_line': ('image_meta_props', 'os_command_line'),
|
||||
'os_distro': ('image_meta_props', 'os_distro'),
|
||||
'os_require_quiesce': ('image_meta_props', 'os_require_quiesce'),
|
||||
'os_secure_boot': ('image_meta_props', 'os_secure_boot'),
|
||||
'os_skip_agent_inject_files_at_boot': (
|
||||
'image_meta_props', 'os_skip_agent_inject_files_at_boot'),
|
||||
'os_skip_agent_inject_ssh': ('image_meta_props',
|
||||
'os_skip_agent_inject_ssh'),
|
||||
'os_type': ('image_meta_props', 'os_type'),
|
||||
'traits_required': ('image_meta_props', 'traits_required')
|
||||
}
|
||||
|
||||
fields = {
|
||||
'hw_architecture': fields.ArchitectureField(),
|
||||
'hw_auto_disk_config': fields.StringField(),
|
||||
'hw_boot_menu': fields.FlexibleBooleanField(),
|
||||
'hw_cdrom_bus': fields.DiskBusField(),
|
||||
'hw_cpu_cores': fields.IntegerField(),
|
||||
'hw_cpu_sockets': fields.IntegerField(),
|
||||
'hw_cpu_max_cores': fields.IntegerField(),
|
||||
'hw_cpu_max_sockets': fields.IntegerField(),
|
||||
'hw_cpu_max_threads': fields.IntegerField(),
|
||||
'hw_cpu_policy': fields.CPUAllocationPolicyField(),
|
||||
'hw_cpu_thread_policy': fields.CPUThreadAllocationPolicyField(),
|
||||
'hw_cpu_realtime_mask': fields.StringField(),
|
||||
'hw_cpu_threads': fields.IntegerField(),
|
||||
'hw_device_id': fields.IntegerField(),
|
||||
'hw_disk_bus': fields.DiskBusField(),
|
||||
'hw_disk_type': fields.StringField(),
|
||||
'hw_floppy_bus': fields.DiskBusField(),
|
||||
'hw_firmware_type': fields.FirmwareTypeField(),
|
||||
'hw_ipxe_boot': fields.FlexibleBooleanField(),
|
||||
'hw_machine_type': fields.StringField(),
|
||||
'hw_mem_page_size': fields.StringField(),
|
||||
'hw_numa_nodes': fields.IntegerField(),
|
||||
'hw_numa_cpus': fields.ListOfSetsOfIntegersField(),
|
||||
'hw_numa_mem': fields.ListOfIntegersField(),
|
||||
'hw_pointer_model': fields.PointerModelField(),
|
||||
'hw_qemu_guest_agent': fields.FlexibleBooleanField(),
|
||||
'hw_rescue_bus': fields.DiskBusField(),
|
||||
'hw_rescue_device': fields.BlockDeviceTypeField(),
|
||||
'hw_rng_model': fields.RNGModelField(),
|
||||
'hw_serial_port_count': fields.IntegerField(),
|
||||
'hw_scsi_model': fields.SCSIModelField(),
|
||||
'hw_video_model': fields.VideoModelField(),
|
||||
'hw_video_ram': fields.IntegerField(),
|
||||
'hw_vif_model': fields.VIFModelField(),
|
||||
'hw_vm_mode': fields.VMModeField(),
|
||||
'hw_watchdog_action': fields.WatchdogActionField(),
|
||||
'hw_vif_multiqueue_enabled': fields.FlexibleBooleanField(),
|
||||
'img_bittorrent': fields.FlexibleBooleanField(),
|
||||
'img_bdm_v2': fields.FlexibleBooleanField(),
|
||||
'img_block_device_mapping':
|
||||
fields.ListOfDictOfNullableStringsField(),
|
||||
'img_cache_in_nova': fields.FlexibleBooleanField(),
|
||||
'img_compression_level': fields.IntegerField(),
|
||||
'img_hv_requested_version': fields.VersionPredicateField(),
|
||||
'img_hv_type': fields.HVTypeField(),
|
||||
'img_config_drive': fields.ConfigDrivePolicyField(),
|
||||
'img_linked_clone': fields.FlexibleBooleanField(),
|
||||
'img_mappings': fields.ListOfDictOfNullableStringsField(),
|
||||
'img_owner_id': fields.StringField(),
|
||||
'img_root_device_name': fields.StringField(),
|
||||
'img_use_agent': fields.FlexibleBooleanField(),
|
||||
'img_version': fields.IntegerField(),
|
||||
'img_signature': fields.StringField(),
|
||||
'img_signature_hash_method': fields.ImageSignatureHashTypeField(),
|
||||
'img_signature_certificate_uuid': fields.UUIDField(),
|
||||
'img_signature_key_type': fields.ImageSignatureKeyTypeField(),
|
||||
'img_hide_hypervisor_id': fields.FlexibleBooleanField(),
|
||||
'os_admin_user': fields.StringField(),
|
||||
'os_command_line': fields.StringField(),
|
||||
'os_distro': fields.StringField(),
|
||||
'os_require_quiesce': fields.FlexibleBooleanField(),
|
||||
'os_secure_boot': fields.SecureBootField(),
|
||||
'os_skip_agent_inject_files_at_boot': fields.FlexibleBooleanField(),
|
||||
'os_skip_agent_inject_ssh': fields.FlexibleBooleanField(),
|
||||
'os_type': fields.OSTypeField(),
|
||||
'traits_required': fields.ListOfStringsField()
|
||||
}
|
||||
|
||||
def __init__(self, image_meta_props):
|
||||
super(ImageMetaPropsPayload, self).__init__()
|
||||
# NOTE(takashin): If fields are not set in the ImageMetaProps object,
|
||||
# it will not set the fields in the ImageMetaPropsPayload
|
||||
# in order to avoid too many fields whose values are None.
|
||||
self.populate_schema(set_none=False, image_meta_props=image_meta_props)
|
226
nova/notifications/objects/request_spec.py
Normal file
226
nova/notifications/objects/request_spec.py
Normal file
@ -0,0 +1,226 @@
|
||||
# Copyright 2018 NTT Corporation
|
||||
#
|
||||
# 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 nova.notifications.objects import base
|
||||
from nova.notifications.objects import flavor as flavor_payload
|
||||
from nova.notifications.objects import image as image_payload
|
||||
from nova.objects import base as nova_base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class RequestSpecPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
SCHEMA = {
|
||||
'instance_uuid': ('request_spec', 'instance_uuid'),
|
||||
'project_id': ('request_spec', 'project_id'),
|
||||
'user_id': ('request_spec', 'user_id'),
|
||||
'availability_zone': ('request_spec', 'availability_zone'),
|
||||
'num_instances': ('request_spec', 'num_instances')
|
||||
}
|
||||
|
||||
fields = {
|
||||
'instance_uuid': fields.UUIDField(),
|
||||
'project_id': fields.StringField(nullable=True),
|
||||
'user_id': fields.StringField(nullable=True),
|
||||
'availability_zone': fields.StringField(nullable=True),
|
||||
'flavor': fields.ObjectField('FlavorPayload', nullable=True),
|
||||
'image': fields.ObjectField('ImageMetaPayload', nullable=True),
|
||||
'numa_topology': fields.ObjectField('InstanceNUMATopologyPayload',
|
||||
nullable=True),
|
||||
'pci_requests': fields.ObjectField('InstancePCIRequestsPayload',
|
||||
nullable=True),
|
||||
'num_instances': fields.IntegerField(default=1)
|
||||
}
|
||||
|
||||
def __init__(self, request_spec):
|
||||
super(RequestSpecPayload, self).__init__()
|
||||
self.flavor = flavor_payload.FlavorPayload(
|
||||
request_spec.flavor) if request_spec.obj_attr_is_set(
|
||||
'flavor') else None
|
||||
self.image = image_payload.ImageMetaPayload(
|
||||
request_spec.image) if request_spec.image else None
|
||||
if request_spec.numa_topology is not None:
|
||||
if not request_spec.numa_topology.obj_attr_is_set('instance_uuid'):
|
||||
request_spec.numa_topology.instance_uuid = (
|
||||
request_spec.instance_uuid)
|
||||
self.numa_topology = InstanceNUMATopologyPayload(
|
||||
request_spec.numa_topology)
|
||||
else:
|
||||
self.numa_topology = None
|
||||
if request_spec.pci_requests is not None:
|
||||
if not request_spec.pci_requests.obj_attr_is_set('instance_uuid'):
|
||||
request_spec.pci_requests.instance_uuid = (
|
||||
request_spec.instance_uuid)
|
||||
self.pci_requests = InstancePCIRequestsPayload(
|
||||
request_spec.pci_requests)
|
||||
else:
|
||||
self.pci_requests = None
|
||||
self.populate_schema(request_spec=request_spec)
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class InstanceNUMATopologyPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
SCHEMA = {
|
||||
'instance_uuid': ('numa_topology', 'instance_uuid'),
|
||||
'emulator_threads_policy': ('numa_topology',
|
||||
'emulator_threads_policy')
|
||||
}
|
||||
|
||||
fields = {
|
||||
'instance_uuid': fields.UUIDField(),
|
||||
'cells': fields.ListOfObjectsField('InstanceNUMACellPayload'),
|
||||
'emulator_threads_policy': fields.CPUEmulatorThreadsPolicyField(
|
||||
nullable=True)
|
||||
}
|
||||
|
||||
def __init__(self, numa_topology):
|
||||
super(InstanceNUMATopologyPayload, self).__init__()
|
||||
self.cells = InstanceNUMACellPayload.from_numa_cell_list_obj(
|
||||
numa_topology.cells)
|
||||
self.populate_schema(numa_topology=numa_topology)
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class InstanceNUMACellPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
SCHEMA = {
|
||||
'id': ('numa_cell', 'id'),
|
||||
'cpuset': ('numa_cell', 'cpuset'),
|
||||
'memory': ('numa_cell', 'memory'),
|
||||
'pagesize': ('numa_cell', 'pagesize'),
|
||||
'cpu_pinning_raw': ('numa_cell', 'cpu_pinning_raw'),
|
||||
'cpu_policy': ('numa_cell', 'cpu_policy'),
|
||||
'cpu_thread_policy': ('numa_cell', 'cpu_thread_policy'),
|
||||
'cpuset_reserved': ('numa_cell', 'cpuset_reserved'),
|
||||
}
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
'cpuset': fields.SetOfIntegersField(),
|
||||
'memory': fields.IntegerField(),
|
||||
'pagesize': fields.IntegerField(nullable=True),
|
||||
'cpu_topology': fields.ObjectField('VirtCPUTopologyPayload',
|
||||
nullable=True),
|
||||
'cpu_pinning_raw': fields.DictOfIntegersField(nullable=True),
|
||||
'cpu_policy': fields.CPUAllocationPolicyField(nullable=True),
|
||||
'cpu_thread_policy': fields.CPUThreadAllocationPolicyField(
|
||||
nullable=True),
|
||||
'cpuset_reserved': fields.SetOfIntegersField(nullable=True)
|
||||
}
|
||||
|
||||
def __init__(self, numa_cell):
|
||||
super(InstanceNUMACellPayload, self).__init__()
|
||||
if (numa_cell.obj_attr_is_set('cpu_topology') and
|
||||
numa_cell.cpu_topology is not None):
|
||||
self.cpu_topology = VirtCPUTopologyPayload(numa_cell.cpu_topology)
|
||||
else:
|
||||
self.cpu_topology = None
|
||||
self.populate_schema(numa_cell=numa_cell)
|
||||
|
||||
@classmethod
|
||||
def from_numa_cell_list_obj(cls, numa_cell_list):
|
||||
"""Returns a list of InstanceNUMACellPayload objects
|
||||
based on the passed list of InstanceNUMACell objects.
|
||||
"""
|
||||
payloads = []
|
||||
for numa_cell in numa_cell_list:
|
||||
payloads.append(cls(numa_cell))
|
||||
return payloads
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class VirtCPUTopologyPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
SCHEMA = {
|
||||
'sockets': ('virt_cpu_topology', 'sockets'),
|
||||
'cores': ('virt_cpu_topology', 'cores'),
|
||||
'threads': ('virt_cpu_topology', 'threads'),
|
||||
}
|
||||
|
||||
fields = {
|
||||
'sockets': fields.IntegerField(nullable=True, default=1),
|
||||
'cores': fields.IntegerField(nullable=True, default=1),
|
||||
'threads': fields.IntegerField(nullable=True, default=1),
|
||||
}
|
||||
|
||||
def __init__(self, virt_cpu_topology):
|
||||
super(VirtCPUTopologyPayload, self).__init__()
|
||||
self.populate_schema(virt_cpu_topology=virt_cpu_topology)
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class InstancePCIRequestsPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
SCHEMA = {
|
||||
'instance_uuid': ('pci_requests', 'instance_uuid')
|
||||
}
|
||||
|
||||
fields = {
|
||||
'instance_uuid': fields.UUIDField(),
|
||||
'requests': fields.ListOfObjectsField('InstancePCIRequestPayload')
|
||||
}
|
||||
|
||||
def __init__(self, pci_requests):
|
||||
super(InstancePCIRequestsPayload, self).__init__()
|
||||
self.requests = InstancePCIRequestPayload.from_pci_request_list_obj(
|
||||
pci_requests.requests)
|
||||
self.populate_schema(pci_requests=pci_requests)
|
||||
|
||||
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class InstancePCIRequestPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
SCHEMA = {
|
||||
'count': ('pci_request', 'count'),
|
||||
'spec': ('pci_request', 'spec'),
|
||||
'alias_name': ('pci_request', 'alias_name'),
|
||||
'request_id': ('pci_request', 'request_id'),
|
||||
'numa_policy': ('pci_request', 'numa_policy')
|
||||
}
|
||||
|
||||
fields = {
|
||||
'count': fields.IntegerField(),
|
||||
'spec': fields.ListOfDictOfNullableStringsField(),
|
||||
'alias_name': fields.StringField(nullable=True),
|
||||
'request_id': fields.UUIDField(nullable=True),
|
||||
'numa_policy': fields.PCINUMAAffinityPolicyField(nullable=True)
|
||||
}
|
||||
|
||||
def __init__(self, pci_request):
|
||||
super(InstancePCIRequestPayload, self).__init__()
|
||||
self.populate_schema(pci_request=pci_request)
|
||||
|
||||
@classmethod
|
||||
def from_pci_request_list_obj(cls, pci_request_list):
|
||||
"""Returns a list of InstancePCIRequestPayload objects
|
||||
based on the passed list of InstancePCIRequest objects.
|
||||
"""
|
||||
payloads = []
|
||||
for pci_request in pci_request_list:
|
||||
payloads.append(cls(pci_request))
|
||||
return payloads
|
@ -833,6 +833,9 @@ class NotificationAction(BaseNovaEnum):
|
||||
UPDATE_PROP = 'update_prop'
|
||||
CONNECT = 'connect'
|
||||
USAGE = 'usage'
|
||||
BUILD_INSTANCES = 'build_instances'
|
||||
MIGRATE_SERVER = 'migrate_server'
|
||||
REBUILD_SERVER = 'rebuild_server'
|
||||
|
||||
ALL = (UPDATE, EXCEPTION, DELETE, PAUSE, UNPAUSE, RESIZE, VOLUME_SWAP,
|
||||
SUSPEND, POWER_ON, REBOOT, SHUTDOWN, SNAPSHOT, INTERFACE_ATTACH,
|
||||
@ -845,7 +848,7 @@ class NotificationAction(BaseNovaEnum):
|
||||
SOFT_DELETE, TRIGGER_CRASH_DUMP, UNRESCUE, UNSHELVE, ADD_HOST,
|
||||
REMOVE_HOST, ADD_MEMBER, UPDATE_METADATA, LOCK, UNLOCK,
|
||||
REBUILD_SCHEDULED, UPDATE_PROP, LIVE_MIGRATION_FORCE_COMPLETE,
|
||||
CONNECT, USAGE)
|
||||
CONNECT, USAGE, BUILD_INSTANCES, MIGRATE_SERVER, REBUILD_SERVER)
|
||||
|
||||
|
||||
# TODO(rlrossit): These should be changed over to be a StateMachine enum from
|
||||
|
@ -18,6 +18,7 @@ import collections
|
||||
import functools
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging as messaging
|
||||
@ -644,8 +645,10 @@ def set_vm_state_and_notify(context, instance_uuid, service, method, updates,
|
||||
reason=ex)
|
||||
|
||||
event_type = '%s.%s' % (service, method)
|
||||
# TODO(mriedem): Send a versioned notification.
|
||||
notifier.error(context, event_type, payload)
|
||||
compute_utils.notify_about_compute_task_error(
|
||||
context, method, instance_uuid, request_spec, vm_state, ex,
|
||||
traceback.format_exc())
|
||||
|
||||
|
||||
def build_filter_properties(scheduler_hints, forced_host,
|
||||
|
@ -206,6 +206,7 @@
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"architecture": "x86_64",
|
||||
"kernel_id": "nokernel",
|
||||
"ramdisk_id": "nokernel"
|
||||
},
|
||||
|
@ -157,7 +157,7 @@ class NotificationSampleTestBase(test.TestCase,
|
||||
self.assertJsonEqual(sample_obj, notification)
|
||||
|
||||
def _boot_a_server(self, expected_status='ACTIVE', extra_params=None,
|
||||
scheduler_hints=None):
|
||||
scheduler_hints=None, additional_extra_specs=None):
|
||||
|
||||
# We have to depend on a specific image and flavor to fix the content
|
||||
# of the notification that will be emitted
|
||||
@ -172,6 +172,8 @@ class NotificationSampleTestBase(test.TestCase,
|
||||
extra_specs = {
|
||||
"extra_specs": {
|
||||
"hw:watchdog_action": "disabled"}}
|
||||
if additional_extra_specs:
|
||||
extra_specs['extra_specs'].update(additional_extra_specs)
|
||||
self.admin_api.post_extra_spec(flavor_id, extra_specs)
|
||||
|
||||
# Ignore the create flavor notification
|
||||
|
@ -0,0 +1,119 @@
|
||||
# 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 nova.tests import fixtures
|
||||
from nova.tests.functional.api import client as api_client
|
||||
from nova.tests.functional.notification_sample_tests \
|
||||
import notification_sample_base
|
||||
from nova.tests.unit import fake_notifier
|
||||
|
||||
|
||||
class TestComputeTaskNotificationSample(
|
||||
notification_sample_base.NotificationSampleTestBase):
|
||||
|
||||
def setUp(self):
|
||||
self.flags(use_neutron=True)
|
||||
super(TestComputeTaskNotificationSample, self).setUp()
|
||||
self.neutron = fixtures.NeutronFixture(self)
|
||||
self.useFixture(self.neutron)
|
||||
|
||||
def test_build_instances_fault(self):
|
||||
# Force down the compute node
|
||||
service_id = self.api.get_service_id('nova-compute')
|
||||
self.admin_api.put_service_force_down(service_id, True)
|
||||
|
||||
server = self._boot_a_server(
|
||||
expected_status='ERROR',
|
||||
extra_params={'networks': [{'port': self.neutron.port_1['id']}]},
|
||||
additional_extra_specs={'hw:numa_nodes': 1,
|
||||
'hw:numa_cpus.0': '0',
|
||||
'hw:numa_mem.0': 512})
|
||||
self._wait_for_notification('compute_task.build_instances.error')
|
||||
self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||
self._verify_notification(
|
||||
'compute_task-build_instances-error',
|
||||
replacements={
|
||||
'instance_uuid': server['id'],
|
||||
'request_spec.instance_uuid': server['id'],
|
||||
'request_spec.numa_topology.instance_uuid': server['id'],
|
||||
'request_spec.pci_requests.instance_uuid': server['id'],
|
||||
'reason.function_name': self.ANY,
|
||||
'reason.module_name': self.ANY,
|
||||
'reason.traceback': self.ANY
|
||||
},
|
||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
|
||||
|
||||
def test_rebuild_fault(self):
|
||||
server = self._boot_a_server(
|
||||
extra_params={'networks': [{'port': self.neutron.port_1['id']}]},
|
||||
additional_extra_specs={'hw:numa_nodes': 1,
|
||||
'hw:numa_cpus.0': '0',
|
||||
'hw:numa_mem.0': 512})
|
||||
self._wait_for_notification('instance.create.end')
|
||||
# Force down the compute node
|
||||
service_id = self.api.get_service_id('nova-compute')
|
||||
self.admin_api.put_service_force_down(service_id, True)
|
||||
|
||||
fake_notifier.reset()
|
||||
|
||||
# NOTE(takashin): The rebuild action and the evacuate action shares
|
||||
# same code path. So the 'evacuate' action is used for this test.
|
||||
post = {'evacuate': {}}
|
||||
|
||||
self.admin_api.post_server_action(server['id'], post)
|
||||
self._wait_for_notification('compute_task.rebuild_server.error')
|
||||
# 0. instance.evacuate
|
||||
# 1. compute_task.rebuild_server.error
|
||||
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||
self._verify_notification(
|
||||
'compute_task-rebuild_server-error',
|
||||
replacements={
|
||||
'instance_uuid': server['id'],
|
||||
'request_spec.instance_uuid': server['id'],
|
||||
'request_spec.numa_topology.instance_uuid': server['id'],
|
||||
'request_spec.pci_requests.instance_uuid': server['id'],
|
||||
'reason.function_name': self.ANY,
|
||||
'reason.module_name': self.ANY,
|
||||
'reason.traceback': self.ANY
|
||||
},
|
||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
||||
|
||||
def test_migrate_fault(self):
|
||||
server = self._boot_a_server(
|
||||
extra_params={'networks': [{'port': self.neutron.port_1['id']}]},
|
||||
additional_extra_specs={'hw:numa_nodes': 1,
|
||||
'hw:numa_cpus.0': '0',
|
||||
'hw:numa_mem.0': 512})
|
||||
self._wait_for_notification('instance.create.end')
|
||||
# Force down the compute node
|
||||
service_id = self.api.get_service_id('nova-compute')
|
||||
self.admin_api.put_service_force_down(service_id, True)
|
||||
|
||||
fake_notifier.reset()
|
||||
|
||||
self.assertRaises(api_client.OpenStackApiException,
|
||||
self.admin_api.post_server_action,
|
||||
server['id'], {'migrate': None})
|
||||
self._wait_for_notification('compute_task.migrate_server.error')
|
||||
self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||
self._verify_notification(
|
||||
'compute_task-migrate_server-error',
|
||||
replacements={
|
||||
'instance_uuid': server['id'],
|
||||
'request_spec.instance_uuid': server['id'],
|
||||
'request_spec.numa_topology.instance_uuid': server['id'],
|
||||
'request_spec.pci_requests.instance_uuid': server['id'],
|
||||
'reason.function_name': self.ANY,
|
||||
'reason.module_name': self.ANY,
|
||||
'reason.traceback': self.ANY
|
||||
},
|
||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
|
@ -67,7 +67,11 @@ class ComputeManagerTestCase(test.TestCase):
|
||||
# Simulate that we're on the last retry attempt
|
||||
filter_properties = {'retry': {'num_attempts': 3}}
|
||||
request_spec = objects.RequestSpec.from_primitives(
|
||||
self.context, {}, filter_properties)
|
||||
self.context,
|
||||
{'instance_properties': {'uuid': instance.uuid},
|
||||
'instance_type': flavor,
|
||||
'image': None},
|
||||
filter_properties)
|
||||
self.compute.manager.build_and_run_instance(
|
||||
self.context, instance, {}, request_spec,
|
||||
filter_properties, block_device_mapping=[])
|
||||
|
@ -1818,8 +1818,10 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
self.assertEqual(2, build_and_run_instance.call_count)
|
||||
self.assertEqual(2, len(instance_cells))
|
||||
|
||||
@mock.patch('nova.compute.utils.notify_about_compute_task_error')
|
||||
@mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
|
||||
def test_schedule_and_build_scheduler_failure(self, select_destinations):
|
||||
def test_schedule_and_build_scheduler_failure(self, select_destinations,
|
||||
mock_notify):
|
||||
select_destinations.side_effect = Exception
|
||||
self.start_service('compute', host='fake-host')
|
||||
self.conductor.schedule_and_build_instances(**self.params)
|
||||
@ -1830,6 +1832,17 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
self.assertEqual('error', instance.vm_state)
|
||||
self.assertIsNone(instance.task_state)
|
||||
|
||||
mock_notify.assert_called_once_with(
|
||||
test.MatchType(context.RequestContext), 'build_instances',
|
||||
instance.uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(Exception), test.MatchType(str))
|
||||
request_spec_dict = mock_notify.call_args_list[0][0][3]
|
||||
for key in ('instance_type', 'num_instances', 'instance_properties',
|
||||
'image'):
|
||||
self.assertIn(key, request_spec_dict)
|
||||
tb = mock_notify.call_args_list[0][0][6]
|
||||
self.assertIn('Traceback (most recent call last):', tb)
|
||||
|
||||
@mock.patch('nova.objects.TagList.destroy')
|
||||
@mock.patch('nova.objects.TagList.create')
|
||||
@mock.patch('nova.compute.utils.notify_about_instance_action')
|
||||
@ -1987,10 +2000,12 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
self.assertTrue(bury.called)
|
||||
self.assertFalse(build_and_run.called)
|
||||
|
||||
@mock.patch('nova.compute.utils.notify_about_compute_task_error')
|
||||
@mock.patch('nova.objects.quotas.Quotas.check_deltas')
|
||||
@mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
|
||||
def test_schedule_and_build_over_quota_during_recheck(self, mock_select,
|
||||
mock_check):
|
||||
mock_check,
|
||||
mock_notify):
|
||||
mock_select.return_value = [[fake_selection1]]
|
||||
# Simulate a race where the first check passes and the recheck fails.
|
||||
# First check occurs in compute/api.
|
||||
@ -2047,6 +2062,17 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
request_specs = request_spec_get_all(self.ctxt)
|
||||
self.assertEqual(0, len(request_specs))
|
||||
|
||||
mock_notify.assert_called_once_with(
|
||||
test.MatchType(context.RequestContext), 'build_instances',
|
||||
instance.uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(exc.TooManyInstances), test.MatchType(str))
|
||||
request_spec_dict = mock_notify.call_args_list[0][0][3]
|
||||
for key in ('instance_type', 'num_instances', 'instance_properties',
|
||||
'image'):
|
||||
self.assertIn(key, request_spec_dict)
|
||||
tb = mock_notify.call_args_list[0][0][6]
|
||||
self.assertIn('Traceback (most recent call last):', tb)
|
||||
|
||||
@mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
|
||||
@mock.patch('nova.objects.quotas.Quotas.check_deltas')
|
||||
@mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
|
||||
@ -2074,7 +2100,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
build_requests=1)
|
||||
self.assertTrue(mock_cm_get.called)
|
||||
|
||||
def test_bury_in_cell0(self):
|
||||
@mock.patch('nova.compute.utils.notify_about_compute_task_error')
|
||||
def test_bury_in_cell0(self, mock_notify):
|
||||
bare_br = self.params['build_requests'][0]
|
||||
|
||||
inst_br = fake_build_request.fake_req_obj(self.ctxt)
|
||||
@ -2120,11 +2147,41 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
|
||||
self.assertEqual(expected, inst_states)
|
||||
|
||||
self.assertEqual(4, mock_notify.call_count)
|
||||
mock_notify.assert_has_calls([
|
||||
mock.call(
|
||||
test.MatchType(context.RequestContext), 'build_instances',
|
||||
bare_br.instance_uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(Exception), test.MatchType(str)),
|
||||
mock.call(
|
||||
test.MatchType(context.RequestContext), 'build_instances',
|
||||
inst_br.instance_uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(Exception), test.MatchType(str)),
|
||||
mock.call(
|
||||
test.MatchType(context.RequestContext), 'build_instances',
|
||||
deleted_br.instance_uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(Exception), test.MatchType(str)),
|
||||
mock.call(
|
||||
test.MatchType(context.RequestContext), 'build_instances',
|
||||
fast_deleted_br.instance_uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(Exception), test.MatchType(str))],
|
||||
any_order=True)
|
||||
|
||||
for i in range(0, 3):
|
||||
# traceback.format_exc() returns 'NoneType'
|
||||
# because an exception is not raised in this test.
|
||||
# So the argument for traceback is not checked.
|
||||
request_spec_dict = mock_notify.call_args_list[i][0][3]
|
||||
for key in ('instance_type', 'num_instances',
|
||||
'instance_properties', 'image'):
|
||||
self.assertIn(key, request_spec_dict)
|
||||
|
||||
@mock.patch('nova.compute.utils.notify_about_compute_task_error')
|
||||
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
|
||||
@mock.patch.object(conductor_manager.ComputeTaskManager,
|
||||
'_create_block_device_mapping')
|
||||
def test_bury_in_cell0_with_block_device_mapping(self, mock_create_bdm,
|
||||
mock_get_cell):
|
||||
mock_get_cell, mock_notify):
|
||||
mock_get_cell.return_value = self.cell_mappings['cell0']
|
||||
|
||||
inst_br = fake_build_request.fake_req_obj(self.ctxt)
|
||||
@ -2140,6 +2197,17 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
mock_create_bdm.assert_called_once_with(
|
||||
self.cell_mappings['cell0'], inst.flavor, inst.uuid,
|
||||
self.params['block_device_mapping'])
|
||||
mock_notify.assert_called_once_with(
|
||||
test.MatchType(context.RequestContext), 'build_instances',
|
||||
inst.uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(Exception), test.MatchType(str))
|
||||
# traceback.format_exc() returns 'NoneType'
|
||||
# because an exception is not raised in this test.
|
||||
# So the argument for traceback is not checked.
|
||||
request_spec_dict = mock_notify.call_args_list[0][0][3]
|
||||
for key in ('instance_type', 'num_instances', 'instance_properties',
|
||||
'image'):
|
||||
self.assertIn(key, request_spec_dict)
|
||||
|
||||
def test_reset(self):
|
||||
with mock.patch('nova.compute.rpcapi.ComputeAPI') as mock_rpc:
|
||||
@ -2724,8 +2792,10 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
block_device_mapping=mock.ANY,
|
||||
node='node2', limits=None, host_list=[])
|
||||
|
||||
@mock.patch('nova.compute.utils.notify_about_compute_task_error')
|
||||
@mock.patch('nova.objects.Instance.save')
|
||||
def test_build_instances_max_retries_exceeded(self, mock_save):
|
||||
def test_build_instances_max_retries_exceeded(self, mock_save,
|
||||
mock_notify):
|
||||
"""Tests that when populate_retry raises MaxRetriesExceeded in
|
||||
build_instances, we don't attempt to cleanup the build request.
|
||||
"""
|
||||
@ -2745,8 +2815,21 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
requested_networks, mock.sentinel.secgroups)
|
||||
mock_save.assert_called_once_with()
|
||||
|
||||
mock_notify.assert_called_once_with(
|
||||
self.context, 'build_instances',
|
||||
instance.uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(exc.MaxRetriesExceeded), test.MatchType(str))
|
||||
request_spec_dict = mock_notify.call_args_list[0][0][3]
|
||||
for key in ('instance_type', 'num_instances', 'instance_properties',
|
||||
'image'):
|
||||
self.assertIn(key, request_spec_dict)
|
||||
tb = mock_notify.call_args_list[0][0][6]
|
||||
self.assertIn('Traceback (most recent call last):', tb)
|
||||
|
||||
@mock.patch('nova.compute.utils.notify_about_compute_task_error')
|
||||
@mock.patch('nova.objects.Instance.save')
|
||||
def test_build_instances_reschedule_no_valid_host(self, mock_save):
|
||||
def test_build_instances_reschedule_no_valid_host(self, mock_save,
|
||||
mock_notify):
|
||||
"""Tests that when select_destinations raises NoValidHost in
|
||||
build_instances, we don't attempt to cleanup the build request if
|
||||
we're rescheduling (num_attempts>1).
|
||||
@ -2770,6 +2853,17 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
requested_networks, mock.sentinel.secgroups)
|
||||
mock_save.assert_called_once_with()
|
||||
|
||||
mock_notify.assert_called_once_with(
|
||||
self.context, 'build_instances',
|
||||
instance.uuid, test.MatchType(dict), 'error',
|
||||
test.MatchType(exc.NoValidHost), test.MatchType(str))
|
||||
request_spec_dict = mock_notify.call_args_list[0][0][3]
|
||||
for key in ('instance_type', 'num_instances', 'instance_properties',
|
||||
'image'):
|
||||
self.assertIn(key, request_spec_dict)
|
||||
tb = mock_notify.call_args_list[0][0][6]
|
||||
self.assertIn('Traceback (most recent call last):', tb)
|
||||
|
||||
def test_cleanup_allocated_networks_none_requested(self):
|
||||
# Tests that we don't deallocate networks if 'none' were specifically
|
||||
# requested.
|
||||
|
@ -52,6 +52,11 @@ class _FakeImageService(object):
|
||||
'container_format': 'raw',
|
||||
'disk_format': 'raw',
|
||||
'size': '25165824',
|
||||
'min_ram': 0,
|
||||
'min_disk': 0,
|
||||
'protected': False,
|
||||
'visibility': 'public',
|
||||
'tags': ['tag1', 'tag2'],
|
||||
'properties': {
|
||||
'kernel_id': 'nokernel',
|
||||
'ramdisk_id': 'nokernel',
|
||||
@ -68,6 +73,11 @@ class _FakeImageService(object):
|
||||
'container_format': 'ami',
|
||||
'disk_format': 'ami',
|
||||
'size': '58145823',
|
||||
'min_ram': 0,
|
||||
'min_disk': 0,
|
||||
'protected': False,
|
||||
'visibility': 'public',
|
||||
'tags': [],
|
||||
'properties': {'kernel_id': 'nokernel',
|
||||
'ramdisk_id': 'nokernel'}}
|
||||
|
||||
@ -82,8 +92,15 @@ class _FakeImageService(object):
|
||||
'container_format': 'bare',
|
||||
'disk_format': 'raw',
|
||||
'size': '83594576',
|
||||
'properties': {'kernel_id': 'nokernel',
|
||||
'ramdisk_id': 'nokernel'}}
|
||||
'min_ram': 0,
|
||||
'min_disk': 0,
|
||||
'protected': False,
|
||||
'visibility': 'public',
|
||||
'tags': ['tag3', 'tag4'],
|
||||
'properties': {
|
||||
'kernel_id': 'nokernel',
|
||||
'ramdisk_id': 'nokernel',
|
||||
'architecture': obj_fields.Architecture.X86_64}}
|
||||
|
||||
image4 = {'id': 'cedef40a-ed67-4d10-800e-17455edce175',
|
||||
'name': 'fakeimage123456',
|
||||
@ -96,6 +113,11 @@ class _FakeImageService(object):
|
||||
'container_format': 'ami',
|
||||
'disk_format': 'ami',
|
||||
'size': '84035174',
|
||||
'min_ram': 0,
|
||||
'min_disk': 0,
|
||||
'protected': False,
|
||||
'visibility': 'public',
|
||||
'tags': [],
|
||||
'properties': {'kernel_id': 'nokernel',
|
||||
'ramdisk_id': 'nokernel'}}
|
||||
|
||||
@ -110,6 +132,11 @@ class _FakeImageService(object):
|
||||
'container_format': 'ami',
|
||||
'disk_format': 'ami',
|
||||
'size': '26360814',
|
||||
'min_ram': 0,
|
||||
'min_disk': 0,
|
||||
'protected': False,
|
||||
'visibility': 'public',
|
||||
'tags': [],
|
||||
'properties': {'kernel_id':
|
||||
'155d900f-4e14-4e4c-a73d-069cbf4541e6',
|
||||
'ramdisk_id': None}}
|
||||
@ -125,6 +152,11 @@ class _FakeImageService(object):
|
||||
'container_format': 'ova',
|
||||
'disk_format': 'vhd',
|
||||
'size': '49163826',
|
||||
'min_ram': 0,
|
||||
'min_disk': 0,
|
||||
'protected': False,
|
||||
'visibility': 'public',
|
||||
'tags': [],
|
||||
'properties': {
|
||||
'kernel_id': 'nokernel',
|
||||
'ramdisk_id': 'nokernel',
|
||||
@ -142,6 +174,11 @@ class _FakeImageService(object):
|
||||
'container_format': 'ova',
|
||||
'disk_format': 'vhd',
|
||||
'size': '74185822',
|
||||
'min_ram': 0,
|
||||
'min_disk': 0,
|
||||
'protected': False,
|
||||
'visibility': 'public',
|
||||
'tags': [],
|
||||
'properties': {
|
||||
'kernel_id': 'nokernel',
|
||||
'ramdisk_id': 'nokernel',
|
||||
|
@ -40,7 +40,8 @@ class FakeImageServiceTestCase(test.NoDBTestCase):
|
||||
'updated_at', 'deleted_at', 'deleted',
|
||||
'status', 'is_public', 'properties',
|
||||
'disk_format', 'container_format',
|
||||
'size']))
|
||||
'size', 'min_disk', 'min_ram',
|
||||
'protected', 'tags', 'visibility']))
|
||||
self.assertIsInstance(image['created_at'], datetime.datetime)
|
||||
self.assertIsInstance(image['updated_at'], datetime.datetime)
|
||||
|
||||
|
@ -370,11 +370,15 @@ notification_object_data = {
|
||||
'AuditPeriodPayload': '1.0-2b429dd307b8374636703b843fa3f9cb',
|
||||
'BandwidthPayload': '1.0-ee2616a7690ab78406842a2b68e34130',
|
||||
'BlockDevicePayload': '1.0-29751e1b6d41b1454e36768a1e764df8',
|
||||
'EventType': '1.17-242397275522a04130b3af4c0ea926e2',
|
||||
'ComputeTaskNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'ComputeTaskPayload': '1.0-e3d34762c14d131c98337b72e8c600e1',
|
||||
'EventType': '1.18-44f33a06fd08fdba0b7dc266116c017b',
|
||||
'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'ExceptionPayload': '1.1-6c43008bd81885a63bc7f7c629f0793b',
|
||||
'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'FlavorPayload': '1.4-2e7011b8b4e59167fe8b7a0a81f0d452',
|
||||
'ImageMetaPayload': '1.0-0e65beeacb3393beed564a57bc2bc989',
|
||||
'ImageMetaPropsPayload': '1.0-0665065e198b4ab1b03aa80f442d2302',
|
||||
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'InstanceActionPayload': '1.7-8c77f0c85a83d325fded152376ca809a',
|
||||
'InstanceActionRebuildNotification':
|
||||
@ -398,6 +402,10 @@ notification_object_data = {
|
||||
'InstanceActionSnapshotPayload': '1.8-6a3a66f823b56268ea4b759c83e38c31',
|
||||
'InstanceExistsNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'InstanceExistsPayload': '1.1-b7095abb18f5b75f39dc1aa59942535d',
|
||||
'InstanceNUMACellPayload': '1.0-2f13614648bc46f2e29578a206561ef6',
|
||||
'InstanceNUMATopologyPayload': '1.0-247361b152047c18ae9ad1da2544a3c9',
|
||||
'InstancePCIRequestPayload': '1.0-12d0d61baf183daaafd93cbeeed2956f',
|
||||
'InstancePCIRequestsPayload': '1.0-6751cffe0c0fabd212aad624f672429a',
|
||||
'InstanceStateUpdatePayload': '1.0-07e111c0fa0f6db0f79b0726d593e3da',
|
||||
'InstanceUpdateNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'InstanceUpdatePayload': '1.8-375131acb12e612a460f68211a2b3a35',
|
||||
@ -410,10 +418,12 @@ notification_object_data = {
|
||||
'MetricsNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'MetricsPayload': '1.0-65c69b15b4de5a8c01971cb5bb9ab650',
|
||||
'NotificationPublisher': '2.2-b6ad48126247e10b46b6b0240e52e614',
|
||||
'RequestSpecPayload': '1.0-ef9936c8da44e442e397b02dec3f6914',
|
||||
'ServerGroupNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'ServerGroupPayload': '1.1-4ded2997ea1b07038f7af33ef5c45f7f',
|
||||
'ServiceStatusNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'ServiceStatusPayload': '1.1-7b6856bd879db7f3ecbcd0ca9f35f92f',
|
||||
'VirtCPUTopologyPayload': '1.0-1b1600fe55465209682d96bbe3209f27',
|
||||
'VolumeUsageNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'VolumeUsagePayload': '1.0-5f99d8b978a32040eecac0975e5a53e9',
|
||||
}
|
||||
|
@ -59,10 +59,12 @@ class SchedulerUtilsTestCase(test.NoDBTestCase):
|
||||
mock_get.assert_called_once_with()
|
||||
self.assertIsInstance(request_spec['instance_properties'], dict)
|
||||
|
||||
@mock.patch('nova.compute.utils.notify_about_compute_task_error')
|
||||
@mock.patch('nova.rpc.LegacyValidatingNotifier')
|
||||
@mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
|
||||
@mock.patch.object(objects.Instance, 'save')
|
||||
def _test_set_vm_state_and_notify(self, mock_save, mock_add, mock_notifier,
|
||||
mock_notify_task,
|
||||
request_spec, payload_request_spec):
|
||||
expected_uuid = uuids.instance
|
||||
updates = dict(vm_state='fake-vm-state')
|
||||
@ -94,6 +96,10 @@ class SchedulerUtilsTestCase(test.NoDBTestCase):
|
||||
mock_notifier.return_value.error.assert_called_once_with(self.context,
|
||||
event_type,
|
||||
payload)
|
||||
mock_notify_task.assert_called_once_with(
|
||||
self.context, method, expected_uuid,
|
||||
payload_request_spec, updates['vm_state'],
|
||||
exc_info, test.MatchType(str))
|
||||
|
||||
def test_set_vm_state_and_notify_request_spec_dict(self):
|
||||
"""Tests passing a legacy dict format request spec to
|
||||
|
Loading…
x
Reference in New Issue
Block a user