Transform instance.exists notification

This patch contains the instance.exists related notification
transformation. The functional test for instance_usage_audit
periodic task case is added in a subsequent patch.

Change-Id: I1a0afa0e8740c229db77c18b932e316196880de5
Implements: bp versioned-notification-transformation-rocky
This commit is contained in:
Alex Szarka 2017-07-20 15:36:31 +02:00 committed by Matt Riedemann
parent 0a642e2eee
commit e7bdd19f0e
13 changed files with 273 additions and 67 deletions

View File

@ -0,0 +1,9 @@
{
"nova_object.data": {
"audit_period_beginning": "2012-10-01T00:00:00Z",
"audit_period_ending": "2012-10-29T13:42:11Z"
},
"nova_object.name": "AuditPeriodPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
}

View File

@ -0,0 +1,10 @@
{
"nova_object.data": {
"network_name": "private-network",
"out_bytes": 0,
"in_bytes": 0
},
"nova_object.name": "BandwidthPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
}

View File

@ -0,0 +1,12 @@
{
"$ref": "InstancePayload.json",
"nova_object.data":{
"audit_period": {"$ref": "AuditPeriodPayload.json#"},
"bandwidth": [
{"$ref": "BandwidthPayload.json#"}
]
},
"nova_object.name":"InstanceExistsPayload",
"nova_object.namespace":"nova",
"nova_object.version":"1.0"
}

View File

@ -0,0 +1,13 @@
{
"event_type":"instance.exists",
"payload":{
"$ref":"common_payloads/InstanceExistsPayload.json#",
"nova_object.data":{
"architecture":null,
"image_uuid":"a2459075-d96c-40d5-893e-577ff92e721c",
"task_state":"rebuilding"
}
},
"priority":"INFO",
"publisher_id":"nova-compute:compute"
}

View File

@ -3019,7 +3019,7 @@ class ComputeManager(manager.Manager):
context)
extra_usage_info = {'image_ref_url': orig_image_ref_url}
compute_utils.notify_usage_exists(
self.notifier, context, instance,
self.notifier, context, instance, self.host,
current_period=True, system_metadata=orig_sys_metadata,
extra_usage_info=extra_usage_info)
@ -3610,7 +3610,7 @@ class ComputeManager(manager.Manager):
reason=_("Driver Error: %s") % e)
compute_utils.notify_usage_exists(self.notifier, context, instance,
current_period=True)
self.host, current_period=True)
instance.vm_state = vm_states.RESCUED
instance.task_state = None
@ -3873,7 +3873,7 @@ class ComputeManager(manager.Manager):
# NOTE(comstud): A revert_resize is essentially a resize back to
# the old size, so we need to send a usage event here.
compute_utils.notify_usage_exists(self.notifier, context, instance,
current_period=True)
self.host, current_period=True)
with self._error_out_instance_on_exception(context, instance):
# NOTE(tr3buchet): tear down networks on destination host
@ -4131,7 +4131,7 @@ class ComputeManager(manager.Manager):
with self._error_out_instance_on_exception(context, instance), \
errors_out_migration_ctxt(migration):
compute_utils.notify_usage_exists(self.notifier, context, instance,
current_period=True)
self.host, current_period=True)
self._notify_about_instance_usage(
context, instance, "resize.prep.start")
compute_utils.notify_about_resize_prep_instance(
@ -4748,7 +4748,7 @@ class ComputeManager(manager.Manager):
else:
bdms = None
compute_utils.notify_usage_exists(self.notifier, context, instance,
current_period=True)
self.host, current_period=True)
self._notify_about_instance_usage(context, instance, 'shelve.start')
compute_utils.notify_about_instance_action(context, instance,
self.host, action=fields.NotificationAction.SHELVE,
@ -6869,7 +6869,7 @@ class ComputeManager(manager.Manager):
for instance in instances:
try:
compute_utils.notify_usage_exists(
self.notifier, context, instance,
self.notifier, context, instance, self.host,
ignore_missing_network_data=False)
successes += 1
except Exception:

View File

@ -261,16 +261,17 @@ def get_value_from_system_metadata(instance, key, type, default):
return default
def notify_usage_exists(notifier, context, instance_ref, current_period=False,
ignore_missing_network_data=True,
def notify_usage_exists(notifier, context, instance_ref, host,
current_period=False, ignore_missing_network_data=True,
system_metadata=None, extra_usage_info=None):
"""Generates 'exists' unversioned legacy notification for an instance for
usage auditing purposes.
"""Generates 'exists' unversioned legacy and transformed notification
for an instance for usage auditing purposes.
:param notifier: a messaging.Notifier
:param context: request context for the current operation
:param instance_ref: nova.objects.Instance object from which to report
usage
:param host: the host emitting the notification
:param current_period: if True, this will generate a usage for the
current usage period; if False, this will generate a usage for the
previous audit period.
@ -303,6 +304,33 @@ def notify_usage_exists(notifier, context, instance_ref, current_period=False,
notify_about_instance_usage(notifier, context, instance_ref, 'exists',
extra_usage_info=extra_info)
audit_period = instance_notification.AuditPeriodPayload(
audit_period_beginning=audit_start,
audit_period_ending=audit_end)
bandwidth = [instance_notification.BandwidthPayload(
network_name=label,
in_bytes=b['bw_in'],
out_bytes=b['bw_out'])
for label, b in bw.items()]
payload = instance_notification.InstanceExistsPayload(
context=context,
instance=instance_ref,
audit_period=audit_period,
bandwidth=bandwidth)
notification = instance_notification.InstanceExistsNotification(
context=context,
priority=fields.NotificationPriority.INFO,
publisher=notification_base.NotificationPublisher(
host=host, source=fields.NotificationSource.COMPUTE),
event_type=notification_base.EventType(
object='instance',
action=fields.NotificationAction.EXISTS),
payload=payload)
notification.emit(context)
def notify_about_instance_usage(notifier, context, instance, event_suffix,
network_info=None, extra_usage_info=None,

View File

@ -627,3 +627,30 @@ class InstanceActionSnapshotPayload(InstanceActionPayload):
instance=instance,
fault=fault)
self.snapshot_image_id = snapshot_image_id
@nova_base.NovaObjectRegistry.register_notification
class InstanceExistsPayload(InstancePayload):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'audit_period': fields.ObjectField('AuditPeriodPayload'),
'bandwidth': fields.ListOfObjectsField('BandwidthPayload'),
}
def __init__(self, context, instance, audit_period, bandwidth):
super(InstanceExistsPayload, self).__init__(context=context,
instance=instance)
self.audit_period = audit_period
self.bandwidth = bandwidth
@base.notification_sample('instance-exists.json')
@nova_base.NovaObjectRegistry.register_notification
class InstanceExistsNotification(base.NotificationBase):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'payload': fields.ObjectField('InstanceExistsPayload')
}

View File

@ -376,6 +376,46 @@ class TestInstanceNotificationSample(
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
def test_instance_exists_usage_audit(self):
# TODO(xavvior): Should create a functional test for the
# "instance_usage_audit" periodic task. We didn't find usable
# solution for this problem, however we tried to test it in
# several ways.
pass
def test_instance_exists(self):
server = self._boot_a_server(
extra_params={'networks': [{'port': self.neutron.port_1['id']}]})
self._attach_volume_to_server(server, self.cinder.SWAP_OLD_VOL)
# Let's generate some bandwidth usage data.
# Just call the periodic task directly for simplicity
self.compute.manager._poll_bandwidth_usage(context.get_admin_context())
fake_notifier.reset()
post = {
'rebuild': {
'imageRef': 'a2459075-d96c-40d5-893e-577ff92e721c',
'metadata': {}
}
}
self.api.post_server_action(server['id'], post)
self._wait_for_state_change(self.api, server,
expected_status='REBUILD')
self._wait_for_state_change(self.api, server,
expected_status='ACTIVE')
notifications = self._get_notifications('instance.exists')
self._verify_notification(
'instance-exists',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']
},
actual=notifications[0])
def _verify_instance_update_steps(self, steps, notifications,
initial=None):
replacements = {}
@ -600,19 +640,19 @@ class TestInstanceNotificationSample(
self._wait_for_state_change(self.api, server,
expected_status='SHELVED')
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self.assertEqual(3, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'instance-shelve-start',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
self._verify_notification(
'instance-shelve-end',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
fake_notifier.reset()
self.api.post_server_action(server['id'], {'shelveOffload': {}})
@ -657,19 +697,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(6, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self.assertEqual(7, 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[4])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[5])
self._verify_notification(
'instance-unshelve-end',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[5])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[6])
def _test_suspend_resume_server(self, server):
post = {'suspend': {}}
@ -780,8 +820,9 @@ class TestInstanceNotificationSample(
self.api.post_server_action(server['id'], post)
self._wait_for_state_change(self.api, server, 'VERIFY_RESIZE')
self.assertEqual(6, len(fake_notifier.VERSIONED_NOTIFICATIONS))
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',
@ -799,24 +840,27 @@ class TestInstanceNotificationSample(
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[idx])
fake_notifier.reset()
# the following is the revert server request
post = {'revertResize': None}
self.api.post_server_action(server['id'], post)
self._wait_for_state_change(self.api, server, 'ACTIVE')
self.assertEqual(8, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self.assertEqual(3, len(fake_notifier.VERSIONED_NOTIFICATIONS))
# ignore instance.exists
fake_notifier.VERSIONED_NOTIFICATIONS.pop(0)
self._verify_notification(
'instance-resize_revert-start',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[6])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
self._verify_notification(
'instance-resize_revert-end',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[7])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
@mock.patch('nova.compute.manager.ComputeManager._reschedule',
return_value=True)
@ -853,18 +897,21 @@ class TestInstanceNotificationSample(
mock_prep_resize.side_effect = _build_resources
self.api.post_server_action(server['id'], post)
self._wait_for_notification('instance.resize.error')
# 0: instance-resize_prep-start
# 1: instance-resize-error
# 2: instance-resize_prep-end
# 0: instance-exists
# 1: instance-resize_prep-start
# 2: instance-resize-error
# 3: instance-resize_prep-end
self.assertLessEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS),
'Unexpected number of notifications: %s' %
fake_notifier.VERSIONED_NOTIFICATIONS)
# Note(gibi): There is also an instance.exists notification emitted
# during the rescheduling
self._verify_notification('instance-resize-error',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']
},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
@mock.patch('nova.compute.manager.ComputeManager._reschedule')
@mock.patch('nova.compute.manager.ComputeManager._prep_resize')
@ -908,14 +955,15 @@ 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 four notifications.
# 0: instance-resize_prep-start
# 1: instance-resize-error
# 2: instance-resize_prep-end
# 3: compute.exception
# There should be the following notifications.
# 0: instance-exists
# 1: instance-resize_prep-start
# 2: instance-resize-error
# 3: instance-resize_prep-end
# 4: compute.exception
# (via the wrap_exception decorator on
# the ComputeManager.prep_resize method.)
self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS),
self.assertEqual(5, len(fake_notifier.VERSIONED_NOTIFICATIONS),
'Unexpected number of notifications: %s' %
fake_notifier.VERSIONED_NOTIFICATIONS)
self._verify_notification('instance-resize-error',
@ -923,7 +971,7 @@ class TestInstanceNotificationSample(
'reservation_id': server['reservation_id'],
'uuid': server['id']
},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
def _test_snapshot_server(self, server):
post = {'createImage': {'name': 'test-snap'}}
@ -973,13 +1021,13 @@ class TestInstanceNotificationSample(
expected_status='ACTIVE')
# The compute/manager will detach every volume during rebuild
self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self.assertEqual(5, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'instance-rebuild-start',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
self._verify_notification(
'instance-volume_detach-start',
replacements={
@ -988,7 +1036,7 @@ class TestInstanceNotificationSample(
'architecture': None,
'image_uuid': 'a2459075-d96c-40d5-893e-577ff92e721c',
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
self._verify_notification(
'instance-volume_detach-end',
replacements={
@ -997,13 +1045,13 @@ class TestInstanceNotificationSample(
'architecture': None,
'image_uuid': 'a2459075-d96c-40d5-893e-577ff92e721c',
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[3])
self._verify_notification(
'instance-rebuild-end',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[3])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[4])
@mock.patch('nova.compute.manager.ComputeManager.'
'_do_rebuild_instance_with_claim')
@ -1301,7 +1349,10 @@ class TestInstanceNotificationSample(
self.api.post_server_action(server['id'], post)
self._wait_for_state_change(self.admin_api, server, 'RESCUE')
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
# 0. instance.rescue.start
# 1. instance.exists
# 2. instance.rescue.end
self.assertEqual(3, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'instance-rescue-start',
replacements={
@ -1313,7 +1364,7 @@ class TestInstanceNotificationSample(
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
fake_notifier.reset()
# unrescue notification asserts

View File

@ -40,9 +40,11 @@ def resolve_refs(obj_with_refs, base_path):
if '$ref' in obj_with_refs.keys():
ref = obj_with_refs.pop('$ref')
resolved_ref = _resolve_ref(ref, base_path)
# the rest of the ref dict contains overrides for the ref. Apply
# those overrides recursively here.
_update_dict_recursively(resolved_ref, obj_with_refs)
# the rest of the ref dict contains overrides for the ref. Resolve
# refs in the overrides then apply those overrides recursively
# here.
resolved_overrides = resolve_refs(obj_with_refs, base_path)
_update_dict_recursively(resolved_ref, resolved_overrides)
return resolved_ref
else:
for key, value in obj_with_refs.items():

View File

@ -1580,7 +1580,13 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
mock_instance_list.assert_called_with(self.context, expected_filters,
use_slave=True)
def test_instance_usage_audit(self):
@mock.patch.object(compute_utils, 'notify_usage_exists')
@mock.patch.object(objects.TaskLog, 'end_task')
@mock.patch.object(objects.TaskLog, 'begin_task')
@mock.patch.object(objects.InstanceList, 'get_active_by_window_joined')
@mock.patch.object(objects.TaskLog, 'get')
def test_instance_usage_audit(self, mock_get, mock_get_active, mock_begin,
mock_end, mock_notify):
instances = [objects.Instance(uuid=uuids.instance)]
def fake_task_log(*a, **k):
@ -1589,26 +1595,19 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
def fake_get(*a, **k):
return instances
mock_get.side_effect = fake_task_log
mock_get_active.side_effect = fake_get
mock_begin.side_effect = fake_task_log
mock_end.side_effect = fake_task_log
self.flags(instance_usage_audit=True)
with test.nested(
mock.patch.object(objects.TaskLog, 'get',
side_effect=fake_task_log),
mock.patch.object(objects.InstanceList,
'get_active_by_window_joined',
side_effect=fake_get),
mock.patch.object(objects.TaskLog, 'begin_task',
side_effect=fake_task_log),
mock.patch.object(objects.TaskLog, 'end_task',
side_effect=fake_task_log),
mock.patch.object(compute_utils, 'notify_usage_exists')
) as (mock_get, mock_get_active, mock_begin, mock_end, mock_notify):
self.compute._instance_usage_audit(self.context)
mock_notify.assert_called_once_with(self.compute.notifier,
self.context, instances[0], ignore_missing_network_data=False)
self.assertTrue(mock_get.called)
self.assertTrue(mock_get_active.called)
self.assertTrue(mock_begin.called)
self.assertTrue(mock_end.called)
self.compute._instance_usage_audit(self.context)
mock_notify.assert_called_once_with(
self.compute.notifier, self.context, instances[0], 'fake-mini',
ignore_missing_network_data=False)
self.assertTrue(mock_get.called)
self.assertTrue(mock_get_active.called)
self.assertTrue(mock_begin.called)
self.assertTrue(mock_end.called)
@mock.patch.object(objects.InstanceList, 'get_by_host')
def test_sync_power_states(self, mock_get):
@ -3416,7 +3415,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
'verybadpass')
notify_usage_exists.assert_called_once_with(self.compute.notifier,
self.context, instance, current_period=True)
self.context, instance, 'fake-mini', current_period=True)
mock_notify.assert_has_calls([
mock.call(self.context, instance, 'fake-mini', None,
phase='start'),

View File

@ -425,7 +425,7 @@ class UsageInfoTestCase(test.TestCase):
instance.system_metadata.update(sys_metadata)
instance.save()
compute_utils.notify_usage_exists(
rpc.get_notifier('compute'), self.context, instance)
rpc.get_notifier('compute'), self.context, instance, 'fake-host')
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 1)
msg = fake_notifier.NOTIFICATIONS[0]
self.assertEqual(msg.priority, 'INFO')
@ -446,12 +446,39 @@ class UsageInfoTestCase(test.TestCase):
self.assertIn(attr, payload,
"Key %s not in payload" % attr)
self.assertEqual(payload['image_meta'],
{'md_key1': 'val1', 'md_key2': 'val2'})
{'md_key1': 'val1', 'md_key2': 'val2'})
image_ref_url = "%s/images/%s" % (
glance.generate_glance_url(self.context), uuids.fake_image_ref)
self.assertEqual(payload['image_ref_url'], image_ref_url)
self.compute.terminate_instance(self.context, instance, [])
def test_notify_usage_exists_emits_versioned(self):
# Ensure 'exists' notification generates appropriate usage data.
instance = create_instance(self.context)
compute_utils.notify_usage_exists(
rpc.get_notifier('compute'), self.context, instance, 'fake-host')
self.assertEqual(len(fake_notifier.VERSIONED_NOTIFICATIONS), 1)
msg = fake_notifier.VERSIONED_NOTIFICATIONS[0]
self.assertEqual(msg['priority'], 'INFO')
self.assertEqual(msg['event_type'], 'instance.exists')
payload = msg['payload']['nova_object.data']
self.assertEqual(payload['tenant_id'], self.project_id)
self.assertEqual(payload['user_id'], self.user_id)
self.assertEqual(payload['uuid'], instance['uuid'])
flavor = payload['flavor']['nova_object.data']
self.assertEqual(flavor['name'], 'm1.tiny')
flavorid = flavors.get_flavor_by_name('m1.tiny')['flavorid']
self.assertEqual(str(flavor['flavorid']), str(flavorid))
for attr in ('display_name', 'created_at', 'launched_at',
'state', 'bandwidth', 'audit_period'):
self.assertIn(attr, payload,
"Key %s not in payload" % attr)
self.assertEqual(payload['image_uuid'], uuids.fake_image_ref)
self.compute.terminate_instance(self.context, instance, [])
def test_notify_usage_exists_deleted_instance(self):
# Ensure 'exists' notification generates appropriate usage data.
instance = create_instance(self.context)
@ -463,7 +490,7 @@ class UsageInfoTestCase(test.TestCase):
instance.save()
self.compute.terminate_instance(self.context, instance, [])
compute_utils.notify_usage_exists(
rpc.get_notifier('compute'), self.context, instance)
rpc.get_notifier('compute'), self.context, instance, 'fake-host')
msg = fake_notifier.NOTIFICATIONS[-1]
self.assertEqual(msg.priority, 'INFO')
self.assertEqual(msg.event_type, 'compute.instance.exists')
@ -825,7 +852,7 @@ class UsageInfoTestCase(test.TestCase):
instance = create_instance(self.context)
self.compute.terminate_instance(self.context, instance, [])
compute_utils.notify_usage_exists(
rpc.get_notifier('compute'), self.context, instance)
rpc.get_notifier('compute'), self.context, instance, 'fake-host')
msg = fake_notifier.NOTIFICATIONS[-1]
self.assertEqual(msg.priority, 'INFO')
self.assertEqual(msg.event_type, 'compute.instance.exists')

View File

@ -392,6 +392,8 @@ notification_object_data = {
'InstanceActionSnapshotNotification':
'1.0-a73147b93b520ff0061865849d3dfa56',
'InstanceActionSnapshotPayload': '1.7-73f96d93ca47750bb6a45e4ab1d268fd',
'InstanceExistsNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'InstanceExistsPayload': '1.0-9b0c1232136e6c850647dfabb63cff07',
'InstanceStateUpdatePayload': '1.0-07e111c0fa0f6db0f79b0726d593e3da',
'InstanceUpdateNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'InstanceUpdatePayload': '1.7-d48dd2cf8310c8f250dfeb65fd9df97a',

View File

@ -133,6 +133,32 @@ class TestJsonRef(test.NoDBTestCase):
actual)
mock_open.assert_called_once_with('some/base/path/another.json', 'r+b')
@mock.patch('oslo_serialization.jsonutils.load')
@mock.patch('nova.tests.json_ref.open')
def test_resolve_ref_with_override_having_refs(self, mock_open,
mock_json_load):
mock_json_load.side_effect = [
{'baz': 13,
'boo': 42},
{'something': 0}]
actual = json_ref.resolve_refs(
{'foo': 1,
'bar': {'$ref': 'another.json#',
'boo': {'$ref': 'override_ref.json#'}}},
'some/base/path/')
self.assertDictEqual({'foo': 1,
'bar': {'baz': 13,
'boo': {'something': 0}}},
actual)
self.assertEqual(2, mock_open.call_count)
# any_order=True is needed as context manager calls also done on open
mock_open.assert_has_calls(
[mock.call('some/base/path/another.json', 'r+b'),
mock.call('some/base/path/override_ref.json', 'r+b')],
any_order=True)
def test_ref_with_json_path_not_supported(self):
self.assertRaises(