Transform scheduler.select_destinations notification

Transform the scheduler.select_destinations.start and
scheduler.select_destinations.end notifications to the
versioned notification framework.

Change-Id: I019e88fabd1d386c0d6395a7b1969315873485fd
Implements: bp versioned-notification-transformation-stein
This commit is contained in:
Előd Illés 2017-09-29 14:38:49 +02:00
parent fe4e47d989
commit 70c7ba7324
16 changed files with 462 additions and 63 deletions

View File

@ -5,7 +5,21 @@
"nova_object.data": {
"instance_uuid": "d5e6a7b7-80e5-4166-85a3-cd6115201082",
"reason": {"$ref": "ExceptionPayload.json#"},
"request_spec": {"$ref": "RequestSpecPayload.json#"},
"request_spec": {
"$ref": "RequestSpecPayload.json#",
"nova_object.data": {
"flavor": {
"nova_object.data": {
"extra_specs": {
"hw:numa_cpus.0": "0",
"hw:numa_mem.0": "512",
"hw:numa_nodes": "1"
}
}
},
"numa_topology": {"$ref": "InstanceNUMATopologyPayload.json#"}
}
},
"state": "error"
}
}

View File

@ -2,24 +2,23 @@
"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"
}
}
},
"flavor": {"$ref": "FlavorPayload.json#"},
"ignore_hosts": null,
"image": {"$ref": "ImageMetaPayload.json#"},
"instance_uuid": "d5e6a7b7-80e5-4166-85a3-cd6115201082",
"num_instances": 1,
"numa_topology": {"$ref": "InstanceNUMATopologyPayload.json#"},
"numa_topology": null,
"pci_requests": {"$ref": "InstancePCIRequestsPayload.json#"},
"project_id": "6f70656e737461636b20342065766572",
"scheduler_hints": {},
"security_groups": ["default"],
"force_hosts": null,
"force_nodes": null,
"instance_group": null,
"requested_destination": null,
"retry": null,
"user_id": "fake"
},
"nova_object.name": "RequestSpecPayload",
"nova_object.version": "1.0"
"nova_object.version": "1.1"
}

View File

@ -0,0 +1,6 @@
{
"priority": "INFO",
"payload": {"$ref": "common_payloads/RequestSpecPayload.json#"},
"event_type": "scheduler.select_destinations.end",
"publisher_id": "nova-scheduler:fake-mini"
}

View File

@ -0,0 +1,6 @@
{
"priority": "INFO",
"payload": {"$ref": "common_payloads/RequestSpecPayload.json#"},
"event_type": "scheduler.select_destinations.start",
"publisher_id": "nova-scheduler:fake-mini"
}

View File

@ -43,6 +43,8 @@ from nova.notifications.objects import instance as instance_notification
from nova.notifications.objects import keypair as keypair_notification
from nova.notifications.objects import libvirt as libvirt_notification
from nova.notifications.objects import metrics as metrics_notification
from nova.notifications.objects import request_spec as reqspec_notification
from nova.notifications.objects import scheduler as scheduler_notification
from nova.notifications.objects import server_group as sg_notification
from nova.notifications.objects import volume as volume_notification
from nova import objects
@ -471,6 +473,31 @@ def notify_about_instance_create(context, instance, host, phase=None,
notification.emit(context)
@rpc.if_notifications_enabled
def notify_about_scheduler_action(context, request_spec, action, phase=None,
source=fields.NotificationSource.SCHEDULER):
"""Send versioned notification about the action made by the scheduler
:param context: the RequestContext object
:param request_spec: the RequestSpec object
:param action: the name of the action
:param phase: the phase of the action
:param source: the source of the notification
"""
payload = reqspec_notification.RequestSpecPayload(
request_spec=request_spec)
notification = scheduler_notification.SelectDestinationsNotification(
context=context,
priority=fields.NotificationPriority.INFO,
publisher=notification_base.NotificationPublisher(
host=CONF.host, source=source),
event_type=notification_base.EventType(
object='scheduler',
action=action,
phase=phase),
payload=payload)
notification.emit(context)
@rpc.if_notifications_enabled
def notify_about_volume_attach_detach(context, instance, host, action, phase,
volume_id=None, exception=None, tb=None):

View File

@ -69,7 +69,9 @@ class EventType(NotificationObject):
# Version 1.17: USAGE is added to NotificationActionField enum
# Version 1.18: ComputeTask related values have been added to
# NotificationActionField enum
VERSION = '1.18'
# Version 1.19: SELECT_DESTINATIONS is added to the NotificationActionField
# enum
VERSION = '1.19'
fields = {
'object': fields.StringField(nullable=False),

View File

@ -15,6 +15,7 @@
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.notifications.objects import server_group as server_group_payload
from nova.objects import base as nova_base
from nova.objects import fields
@ -22,14 +23,19 @@ from nova.objects import fields
@nova_base.NovaObjectRegistry.register_notification
class RequestSpecPayload(base.NotificationPayloadBase):
# Version 1.0: Initial version
VERSION = '1.0'
# Version 1.1: Add force_hosts, force_nodes, ignore_hosts, image_meta,
# instance_group, requested_destination, retry,
# scheduler_hints and security_groups fields
VERSION = '1.1'
SCHEMA = {
'ignore_hosts': ('request_spec', 'ignore_hosts'),
'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')
'num_instances': ('request_spec', 'num_instances'),
'scheduler_hints': ('request_spec', 'scheduler_hints'),
}
fields = {
@ -38,12 +44,23 @@ class RequestSpecPayload(base.NotificationPayloadBase):
'user_id': fields.StringField(nullable=True),
'availability_zone': fields.StringField(nullable=True),
'flavor': fields.ObjectField('FlavorPayload', nullable=True),
'force_hosts': fields.StringField(nullable=True),
'force_nodes': fields.StringField(nullable=True),
'ignore_hosts': fields.ListOfStringsField(nullable=True),
'image_meta': fields.ObjectField('ImageMetaPayload', nullable=True),
'instance_group': fields.ObjectField('ServerGroupPayload',
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)
'num_instances': fields.IntegerField(default=1),
'requested_destination': fields.ObjectField('DestinationPayload',
nullable=True),
'retry': fields.ObjectField('SchedulerRetriesPayload', nullable=True),
'scheduler_hints': fields.DictOfListOfStringsField(nullable=True),
'security_groups': fields.ListOfStringsField(),
}
def __init__(self, request_spec):
@ -69,6 +86,32 @@ class RequestSpecPayload(base.NotificationPayloadBase):
request_spec.pci_requests)
else:
self.pci_requests = None
if 'requested_destination' in request_spec \
and request_spec.requested_destination:
self.requested_destination = DestinationPayload(
destination=request_spec.requested_destination)
else:
self.requested_destination = None
if 'retry' in request_spec and request_spec.retry:
self.retry = SchedulerRetriesPayload(
retry=request_spec.retry)
else:
self.retry = None
self.security_groups = [
sec_group.identifier for sec_group in request_spec.security_groups]
if 'instance_group' in request_spec and request_spec.instance_group:
self.instance_group = server_group_payload.ServerGroupPayload(
group=request_spec.instance_group)
else:
self.instance_group = None
if 'force_hosts' in request_spec and request_spec.force_hosts:
self.force_hosts = request_spec.force_hosts[0]
else:
self.force_hosts = None
if 'force_nodes' in request_spec and request_spec.force_nodes:
self.force_nodes = request_spec.force_nodes[0]
else:
self.force_nodes = None
self.populate_schema(request_spec=request_spec)
@ -224,3 +267,82 @@ class InstancePCIRequestPayload(base.NotificationPayloadBase):
for pci_request in pci_request_list:
payloads.append(cls(pci_request))
return payloads
@nova_base.NovaObjectRegistry.register_notification
class DestinationPayload(base.NotificationPayloadBase):
# Version 1.0: Initial version
VERSION = '1.0'
SCHEMA = {
'aggregates': ('destination', 'aggregates'),
}
fields = {
'host': fields.StringField(),
'node': fields.StringField(nullable=True),
'cell': fields.ObjectField('CellMappingPayload', nullable=True),
'aggregates': fields.ListOfStringsField(nullable=True,
default=None),
}
def __init__(self, destination):
super(DestinationPayload, self).__init__()
if (destination.obj_attr_is_set('host') and
destination.host is not None):
self.host = destination.host
if (destination.obj_attr_is_set('node') and
destination.node is not None):
self.node = destination.node
if (destination.obj_attr_is_set('cell') and
destination.cell is not None):
self.cell = CellMappingPayload(destination.cell)
self.populate_schema(destination=destination)
@nova_base.NovaObjectRegistry.register_notification
class SchedulerRetriesPayload(base.NotificationPayloadBase):
# Version 1.0: Initial version
VERSION = '1.0'
SCHEMA = {
'num_attempts': ('retry', 'num_attempts'),
}
fields = {
'num_attempts': fields.IntegerField(),
'hosts': fields.ListOfStringsField(),
}
def __init__(self, retry):
super(SchedulerRetriesPayload, self).__init__()
self.hosts = []
for compute_node in retry.hosts:
self.hosts.append(compute_node.hypervisor_hostname)
self.populate_schema(retry=retry)
@nova_base.NovaObjectRegistry.register_notification
class CellMappingPayload(base.NotificationPayloadBase):
# Version 1.0: Initial version
VERSION = '1.0'
SCHEMA = {
'uuid': ('cell', 'uuid'),
'name': ('cell', 'name'),
'transport_url': ('cell', 'transport_url'),
'database_connection': ('cell', 'database_connection'),
'disabled': ('cell', 'disabled'),
}
fields = {
'uuid': fields.UUIDField(),
'name': fields.StringField(nullable=True),
'transport_url': fields.StringField(),
'database_connection': fields.StringField(),
'disabled': fields.BooleanField(default=False),
}
def __init__(self, cell):
super(CellMappingPayload, self).__init__()
self.populate_schema(cell=cell)

View File

@ -0,0 +1,29 @@
# Copyright 2017 Ericsson
#
# 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
@base.notification_sample('scheduler-select_destinations-start.json')
@base.notification_sample('scheduler-select_destinations-end.json')
@nova_base.NovaObjectRegistry.register_notification
class SelectDestinationsNotification(base.NotificationBase):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'payload': fields.ObjectField('RequestSpecPayload')
}

View File

@ -819,6 +819,7 @@ class NotificationAction(BaseNovaEnum):
RESIZE_CONFIRM = 'resize_confirm'
RESIZE_PREP = 'resize_prep'
RESIZE_REVERT = 'resize_revert'
SELECT_DESTINATIONS = 'select_destinations'
SHELVE_OFFLOAD = 'shelve_offload'
SOFT_DELETE = 'soft_delete'
TRIGGER_CRASH_DUMP = 'trigger_crash_dump'
@ -848,7 +849,8 @@ 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, BUILD_INSTANCES, MIGRATE_SERVER, REBUILD_SERVER)
CONNECT, USAGE, BUILD_INSTANCES, MIGRATE_SERVER, REBUILD_SERVER,
SELECT_DESTINATIONS)
# TODO(rlrossit): These should be changed over to be a StateMachine enum from

View File

@ -24,10 +24,12 @@ import random
from oslo_log import log as logging
from six.moves import range
from nova.compute import utils as compute_utils
import nova.conf
from nova import exception
from nova.i18n import _
from nova import objects
from nova.objects import fields as fields_obj
from nova import rpc
from nova.scheduler import client
from nova.scheduler import driver
@ -85,6 +87,10 @@ class FilterScheduler(driver.Scheduler):
self.notifier.info(
context, 'scheduler.select_destinations.start',
dict(request_spec=spec_obj.to_legacy_request_spec_dict()))
compute_utils.notify_about_scheduler_action(
context=context, request_spec=spec_obj,
action=fields_obj.NotificationAction.SELECT_DESTINATIONS,
phase=fields_obj.NotificationPhase.START)
host_selections = self._schedule(context, spec_obj, instance_uuids,
alloc_reqs_by_rp_uuid, provider_summaries,
@ -92,6 +98,10 @@ class FilterScheduler(driver.Scheduler):
self.notifier.info(
context, 'scheduler.select_destinations.end',
dict(request_spec=spec_obj.to_legacy_request_spec_dict()))
compute_utils.notify_about_scheduler_action(
context=context, request_spec=spec_obj,
action=fields_obj.NotificationAction.SELECT_DESTINATIONS,
phase=fields_obj.NotificationPhase.END)
return host_selections
def _schedule(self, context, spec_obj, instance_uuids,

View File

@ -156,6 +156,23 @@ class NotificationSampleTestBase(test.TestCase,
self.assertJsonEqual(sample_obj, notification)
def _pop_and_verify_dest_select_notification(self,
server_id, replacements=None):
replacements = replacements or {}
replacements['instance_uuid'] = server_id
replacements['pci_requests.instance_uuid'] = server_id
replacements['flavor.extra_specs'] = self.ANY
replacements['numa_topology'] = self.ANY
scheduler_expected_notifications = [
'scheduler-select_destinations-start',
'scheduler-select_destinations-end']
self.assertLessEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
for notification in scheduler_expected_notifications:
self._verify_notification(
notification,
replacements=replacements,
actual=fake_notifier.VERSIONED_NOTIFICATIONS.pop(0))
def _boot_a_server(self, expected_status='ACTIVE', extra_params=None,
scheduler_hints=None, additional_extra_specs=None):
@ -227,6 +244,11 @@ class NotificationSampleTestBase(test.TestCase,
expected_status)
found_server['reservation_id'] = reservation_id
# Note(elod.illes): let's just pop and verify the dest_select
# notifications if we don't have a special case
if scheduler_hints is None and expected_status != 'ERROR':
self._pop_and_verify_dest_select_notification(found_server['id'])
if found_server['status'] == 'ACTIVE':
self.api.put_server_tags(found_server['id'], ['tag1'])
return found_server

View File

@ -38,19 +38,22 @@ class TestComputeTaskNotificationSample(
'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))
# 0. scheduler.select_destinations.start
# 1. compute_task.rebuild_server.error
self.assertEqual(2, 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.security_groups': [],
'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])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
def test_rebuild_fault(self):
server = self._boot_a_server(
@ -72,20 +75,22 @@ class TestComputeTaskNotificationSample(
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))
# 1. scheduler.select_destinations.start
# 2. compute_task.rebuild_server.error
self.assertEqual(3, 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.security_groups': [],
'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])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
def test_migrate_fault(self):
server = self._boot_a_server(
@ -104,16 +109,19 @@ class TestComputeTaskNotificationSample(
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))
# 0. scheduler.select_destinations.start
# 1. compute_task.migrate_server.error
self.assertEqual(2, 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.security_groups': [],
'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])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])

View File

@ -273,23 +273,25 @@ class TestInstanceNotificationSampleWithMultipleCompute(
self._wait_for_notification(
'instance.live_migration_force_complete.end')
# 0. instance.live_migration_pre.start
# 1. instance.live_migration_pre.end
# 2. instance.live_migration_force_complete.start
# 3. instance.live_migration_force_complete.end
self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS))
# 0. scheduler.select_destinations.start
# 1. scheduler.select_destinations.end
# 2. instance.live_migration_pre.start
# 3. instance.live_migration_pre.end
# 4. instance.live_migration_force_complete.start
# 5. instance.live_migration_force_complete.end
self.assertEqual(6, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'instance-live_migration_force_complete-start',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[4])
self._verify_notification(
'instance-live_migration_force_complete-end',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[3])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[5])
class TestInstanceNotificationSampleWithMultipleComputeOldAttachFlow(
@ -435,9 +437,13 @@ class TestInstanceNotificationSample(
'tags': ['tag'],
'trusted_image_certificates': fake_trusted_certs})
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
# 0. scheduler.select_destinations.start
# 1. scheduler.select_destinations.end
# 2. instance-create-start
# 3. instance-create-error
self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS))
tb = fake_notifier.VERSIONED_NOTIFICATIONS[1]['payload'][
tb = fake_notifier.VERSIONED_NOTIFICATIONS[3]['payload'][
'nova_object.data']['fault']['nova_object.data']['traceback']
self.assertIn('raise exception.FlavorDiskTooSmall()', tb)
@ -446,14 +452,14 @@ class TestInstanceNotificationSample(
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
self._verify_notification(
'instance-create-error',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id'],
'fault.traceback': self.ANY},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[3])
fake_notifier.reset()
@ -825,19 +831,19 @@ class TestInstanceNotificationSample(
post = {'unshelve': None}
self.api.post_server_action(server['id'], post)
self._wait_for_state_change(self.admin_api, server, 'ACTIVE')
self.assertEqual(7, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self.assertEqual(9, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'instance-unshelve-start',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[5])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[7])
self._verify_notification(
'instance-unshelve-end',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[6])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[8])
def _test_suspend_resume_server(self, server):
post = {'suspend': {}}
@ -920,6 +926,29 @@ class TestInstanceNotificationSample(
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[3])
def _build_destination_payload(self):
cell1 = self.cell_mappings.get('cell1')
return {
'nova_object.version': '1.0',
'nova_object.namespace': 'nova',
'nova_object.name': 'DestinationPayload',
'nova_object.data': {
'aggregates': None,
'cell': {
'nova_object.version': '1.0',
'nova_object.namespace': 'nova',
'nova_object.name': 'CellMappingPayload',
'nova_object.data': {
'database_connection': cell1.database_connection,
'disabled': False,
'name': u'cell1',
'transport_url': u'fake://nowhere/',
'uuid': cell1.uuid
}
}
}
}
def _test_resize_and_revert_server(self, server):
self.flags(allow_resize_to_same_host=True)
other_flavor_body = {
@ -948,9 +977,19 @@ class TestInstanceNotificationSample(
self.api.post_server_action(server['id'], post)
self._wait_for_state_change(self.api, server, 'VERIFY_RESIZE')
self._pop_and_verify_dest_select_notification(server['id'],
replacements={
'ignore_hosts': [],
'flavor.memory_mb': other_flavor_body['flavor']['ram'],
'flavor.name': other_flavor_body['flavor']['name'],
'flavor.flavorid': other_flavor_id,
'flavor.extra_specs': extra_specs['extra_specs'],
'requested_destination': self._build_destination_payload()})
self.assertEqual(7, len(fake_notifier.VERSIONED_NOTIFICATIONS))
# ignore instance.exists
fake_notifier.VERSIONED_NOTIFICATIONS.pop(0)
# This list needs to be in order.
expected_notifications = [
'instance-resize_prep-start',
@ -1025,6 +1064,13 @@ class TestInstanceNotificationSample(
mock_prep_resize.side_effect = _build_resources
self.api.post_server_action(server['id'], post)
self._wait_for_notification('instance.resize.error')
self._pop_and_verify_dest_select_notification(server['id'],
replacements={
'ignore_hosts': [],
'flavor.name': other_flavor_body['flavor']['name'],
'flavor.flavorid': other_flavor_id,
'flavor.extra_specs': {},
'requested_destination': self._build_destination_payload()})
# 0: instance-exists
# 1: instance-resize_prep-start
# 2: instance-resize-error
@ -1089,7 +1135,8 @@ class TestInstanceNotificationSample(
self.api.post_server_action(server['id'], post)
self._wait_for_state_change(self.api, server, expected_status='ERROR')
self._wait_for_notification('compute.exception')
# There should be the following notifications.
# There should be the following notifications after scheduler's
# select_destination notifications:
# 0: instance-exists
# 1: instance-resize_prep-start
# 2: instance-resize-error
@ -1097,6 +1144,13 @@ class TestInstanceNotificationSample(
# 4: compute.exception
# (via the wrap_exception decorator on
# the ComputeManager.prep_resize method.)
self._pop_and_verify_dest_select_notification(server['id'],
replacements={
'ignore_hosts': [],
'flavor.name': other_flavor_body['flavor']['name'],
'flavor.flavorid': other_flavor_id,
'flavor.extra_specs': {},
'requested_destination': self._build_destination_payload()})
self.assertEqual(5, len(fake_notifier.VERSIONED_NOTIFICATIONS),
'Unexpected number of notifications: %s' %
fake_notifier.VERSIONED_NOTIFICATIONS)
@ -1146,10 +1200,11 @@ class TestInstanceNotificationSample(
fake_notifier.reset()
image_ref = 'a2459075-d96c-40d5-893e-577ff92e721c'
post = {
'rebuild': {
'imageRef': 'a2459075-d96c-40d5-893e-577ff92e721c',
'metadata': {},
'imageRef': image_ref,
'metadata': {}
}
}
self.api.post_server_action(server['id'], post)
@ -1160,6 +1215,22 @@ class TestInstanceNotificationSample(
self._wait_for_state_change(self.api, server,
expected_status='ACTIVE')
self._pop_and_verify_dest_select_notification(server['id'],
replacements={
'image.container_format': 'ami',
'image.disk_format': 'ami',
'image.id': image_ref,
'image.properties': {
'nova_object.data': {},
'nova_object.name': 'ImageMetaPropsPayload',
'nova_object.namespace': 'nova',
'nova_object.version': u'1.0'},
'image.size': 58145823,
'image.tags': [],
'scheduler_hints': {'_nova_check_type': ['rebuild']},
'force_hosts': 'compute',
'force_nodes': 'fake-mini'})
# 0. instance.rebuild_scheduled
# 1. instance.exists
# 2. instance.rebuild.start
@ -1222,10 +1293,11 @@ class TestInstanceNotificationSample(
fake_notifier.reset()
image_ref = 'a2459075-d96c-40d5-893e-577ff92e721c'
rebuild_trusted_certs = ['rebuild-cert-id-1', 'rebuild-cert-id-2']
post = {
'rebuild': {
'imageRef': 'a2459075-d96c-40d5-893e-577ff92e721c',
'imageRef': image_ref,
'metadata': {},
'trusted_image_certificates': rebuild_trusted_certs,
}
@ -1238,6 +1310,22 @@ class TestInstanceNotificationSample(
self._wait_for_state_change(self.api, server,
expected_status='ACTIVE')
self._pop_and_verify_dest_select_notification(server['id'],
replacements={
'image.container_format': 'ami',
'image.disk_format': 'ami',
'image.id': image_ref,
'image.properties': {
'nova_object.data': {},
'nova_object.name': 'ImageMetaPropsPayload',
'nova_object.namespace': 'nova',
'nova_object.version': u'1.0'},
'image.size': 58145823,
'image.tags': [],
'scheduler_hints': {'_nova_check_type': ['rebuild']},
'force_hosts': 'compute',
'force_nodes': 'fake-mini'})
# 0. instance.rebuild_scheduled
# 1. instance.exists
# 2. instance.rebuild.start

View File

@ -61,11 +61,13 @@ class TestServerGroupNotificationSample(
scheduler_hints={"group": group['id']})
self._wait_for_notification('instance.update')
# 0: server_group.add_member
# 1: instance.create.start
# 2: instance.create.end
# 3: instance.update
# 1: scheduler-select_destinations-start
# 2: scheduler-select_destinations-end
# 3: instance.create.start
# 4: instance.create.end
# 5: instance.update
# (Due to adding server tags in the '_boot_a_server' method.)
self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self.assertEqual(6, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'server_group-add_member',
replacements={'uuid': group['id'],

View File

@ -370,9 +370,11 @@ notification_object_data = {
'AuditPeriodPayload': '1.0-2b429dd307b8374636703b843fa3f9cb',
'BandwidthPayload': '1.0-ee2616a7690ab78406842a2b68e34130',
'BlockDevicePayload': '1.0-29751e1b6d41b1454e36768a1e764df8',
'CellMappingPayload': '1.0-cf7faeb3cdd6b0c742ff74c80b88fb11',
'ComputeTaskNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'ComputeTaskPayload': '1.0-e3d34762c14d131c98337b72e8c600e1',
'EventType': '1.18-44f33a06fd08fdba0b7dc266116c017b',
'DestinationPayload': '1.0-4ccf26318dd18c4377dada2b1e74ec2e',
'EventType': '1.19-000a76e83b06a9de11d365465a755a5e',
'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'ExceptionPayload': '1.1-6c43008bd81885a63bc7f7c629f0793b',
'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
@ -418,7 +420,9 @@ notification_object_data = {
'MetricsNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'MetricsPayload': '1.0-65c69b15b4de5a8c01971cb5bb9ab650',
'NotificationPublisher': '2.2-b6ad48126247e10b46b6b0240e52e614',
'RequestSpecPayload': '1.0-ef9936c8da44e442e397b02dec3f6914',
'RequestSpecPayload': '1.1-64d30723a2e381d0cd6a16a877002c64',
'SchedulerRetriesPayload': '1.0-03a07d09575ef52cced5b1b24301d0b4',
'SelectDestinationsNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'ServerGroupNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'ServerGroupPayload': '1.1-4ded2997ea1b07038f7af33ef5c45f7f',
'ServiceStatusNotification': '1.0-a73147b93b520ff0061865849d3dfa56',

View File

@ -83,7 +83,10 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
instance_group=None, instance_uuid=uuids.instance)
# Reset the RequestSpec changes so they don't interfere with the
@ -149,7 +152,10 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
instance_group=group)
@ -198,7 +204,10 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
instance_group=None)
@ -268,7 +277,10 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
instance_group=None)
@ -321,7 +333,10 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
instance_group=None)
@ -500,7 +515,10 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
instance_group=ig, instance_uuid=uuids.instance0)
# Reset the RequestSpec changes so they don't interfere with the
@ -765,9 +783,16 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
num_instances=1)
num_instances=1,
image=None,
numa_topology=None,
pci_requests=None,
instance_uuid=uuids.instance_id)
mock_schedule.return_value = [[fake_selection]]
dests = self.driver.select_destinations(self.context, spec_obj,
@ -793,9 +818,16 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
num_instances=2)
num_instances=2,
image=None,
numa_topology=None,
pci_requests=None,
instance_uuid=uuids.instance_id)
mock_schedule.return_value = [[fake_selection]]
dests = self.driver.select_destinations(self.context, spec_obj,
@ -824,8 +856,12 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1),
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor"),
project_id=uuids.project_id,
instance_uuid=uuids.instance_id,
instance_group=None)
host_state = mock.Mock(spec=host_manager.HostState, host="fake_host",
@ -1020,16 +1056,32 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
self._test_not_enough_alternates(num_hosts=3, max_attempts=5)
self._test_not_enough_alternates(num_hosts=20, max_attempts=5)
@mock.patch('nova.compute.utils.notify_about_scheduler_action')
@mock.patch.object(filter_scheduler.FilterScheduler, '_schedule')
def test_select_destinations_notifications(self, mock_schedule):
def test_select_destinations_notifications(self, mock_schedule,
mock_notify):
mock_schedule.return_value = ([[mock.Mock()]], [[mock.Mock()]])
with mock.patch.object(self.driver.notifier, 'info') as mock_info:
flavor = objects.Flavor(memory_mb=512,
root_gb=512,
ephemeral_gb=0,
swap=0,
vcpus=1,
disabled=False,
is_public=True,
name="small_flavor")
expected = {'num_instances': 1,
'instance_properties': {'uuid': uuids.instance},
'instance_type': {},
'instance_properties': {
'uuid': uuids.instance,
'ephemeral_gb': 0,
'memory_mb': 512,
'vcpus': 1,
'root_gb': 512},
'instance_type': flavor,
'image': {}}
spec_obj = objects.RequestSpec(num_instances=1,
flavor=flavor,
instance_uuid=uuids.instance)
self.driver.select_destinations(self.context, spec_obj,
@ -1042,6 +1094,12 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
dict(request_spec=expected))]
self.assertEqual(expected, mock_info.call_args_list)
mock_notify.assert_has_calls([
mock.call(context=self.context, request_spec=spec_obj,
action='select_destinations', phase='start'),
mock.call(context=self.context, request_spec=spec_obj,
action='select_destinations', phase='end')])
def test_get_all_host_states_provider_summaries_is_none(self):
"""Tests that HostManager.get_host_states_by_uuids is called with
compute_uuids being None when the incoming provider_summaries is None.