Merge "Transform compute_task notifications"

This commit is contained in:
Zuul 2018-12-09 15:36:21 +00:00 committed by Gerrit Code Review
commit f7252b586b
28 changed files with 1031 additions and 24 deletions

View File

@ -207,6 +207,7 @@
}
],
"metadata": {
"architecture": "x86_64",
"kernel_id": "nokernel",
"ramdisk_id": "nokernel"
},

View File

@ -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"
}
}

View File

@ -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 ..."
}
}

View File

@ -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"
}

View File

@ -0,0 +1,8 @@
{
"nova_object.namespace": "nova",
"nova_object.data": {
"hw_architecture": "x86_64"
},
"nova_object.name": "ImageMetaPropsPayload",
"nova_object.version": "1.0"
}

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -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": []
}
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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.

View File

@ -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" '

View 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')
}

View 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)

View 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

View File

@ -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

View File

@ -17,6 +17,7 @@
import collections
import re
import sys
import traceback
from oslo_log import log as logging
from oslo_serialization import jsonutils
@ -642,8 +643,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,

View File

@ -206,6 +206,7 @@
}
],
"metadata": {
"architecture": "x86_64",
"kernel_id": "nokernel",
"ramdisk_id": "nokernel"
},

View File

@ -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

View File

@ -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])

View File

@ -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=[])

View File

@ -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.

View File

@ -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',

View File

@ -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)

View File

@ -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',
}

View File

@ -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