Notification and CDM partial update

In this changeset, I implemented the notification handling (Rx only)
system for consuming incoming notifications, more especially the Nova
ones. The notifications handlers also contain the logic which
incrementally updates the Compute model.

Change-Id: Ia036a5a2be6caa64b7f180de38821b57c624300c
Partially-implements: blueprint cluster-model-objects-wrapper
This commit is contained in:
Vincent Françoise
2016-08-10 11:11:18 +02:00
parent a1cb142009
commit 77b7fae41e
60 changed files with 2154 additions and 240 deletions

View File

@@ -73,9 +73,9 @@ class TestOneShotAuditHandler(base.DbTestCase):
'audit_uuid': self.audit.uuid})
calls = [call_on_going, call_succeeded]
messaging.status_topic_handler.publish_event.assert_has_calls(calls)
messaging.publish_status_event.assert_has_calls(calls)
self.assertEqual(
2, messaging.status_topic_handler.publish_event.call_count)
2, messaging.publish_status_event.call_count)
class TestContinuousAuditHandler(base.DbTestCase):

View File

@@ -23,6 +23,10 @@ from watcher.tests import base as test_base
class DummyClusterDataModelCollector(base.BaseClusterDataModelCollector):
@property
def notification_endpoints(self):
return []
def execute(self):
model = model_root.ModelRoot()
# Do something here...

View File

@@ -0,0 +1,70 @@
{
"event_type": "instance.update",
"payload": {
"nova_object.data": {
"architecture": "x86_64",
"audit_period": {
"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"
},
"availability_zone": null,
"bandwidth": [],
"created_at": "2012-10-29T13:42:11Z",
"deleted_at": null,
"display_name": "some-server",
"host": "compute",
"host_name": "some-server",
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"kernel_id": "",
"launched_at": null,
"metadata": {},
"node": "fake-mini",
"old_display_name": null,
"os_type": null,
"progress": 0,
"ramdisk_id": "",
"reservation_id": "r-sd3ygfjj",
"state": "active",
"task_state": "scheduling",
"power_state": "pending",
"ip_addresses": [],
"state_update": {
"nova_object.version": "1.0",
"nova_object.name": "InstanceStateUpdatePayload",
"nova_object.namespace": "nova",
"nova_object.data": {
"old_state": "building",
"new_task_state": null,
"old_task_state": "spawning",
"state": "active"
}
},
"tenant_id": "6f70656e737461636b20342065766572",
"terminated_at": null,
"flavor": {
"nova_object.name": "FlavorPayload",
"nova_object.data": {
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
"root_gb": 1,
"vcpus": 1,
"ephemeral_gb": 0,
"memory_mb": 512
},
"nova_object.version": "1.0",
"nova_object.namespace": "nova"
},
"user_id": "fake",
"uuid": "c03c0bf9-f46e-4e4f-93f1-817568567ee2"
},
"nova_object.name": "InstanceUpdatePayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
},
"priority": "INFO",
"publisher_id": "nova-compute:compute"
}

View File

@@ -0,0 +1,49 @@
{
"event_type":"instance.delete.end",
"payload":{
"nova_object.data":{
"architecture":"x86_64",
"availability_zone":null,
"created_at":"2012-10-29T13:42:11Z",
"deleted_at":"2012-10-29T13:42:11Z",
"display_name":"some-server",
"fault":null,
"host":"compute",
"host_name":"some-server",
"ip_addresses":[],
"kernel_id":"",
"launched_at":"2012-10-29T13:42:11Z",
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"metadata":{},
"node":"fake-mini",
"os_type":null,
"progress":0,
"ramdisk_id":"",
"reservation_id":"r-npxv0e40",
"state":"deleted",
"task_state":null,
"power_state":"pending",
"tenant_id":"6f70656e737461636b20342065766572",
"terminated_at":"2012-10-29T13:42:11Z",
"flavor": {
"nova_object.name": "FlavorPayload",
"nova_object.data": {
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
"root_gb": 1,
"vcpus": 1,
"ephemeral_gb": 0,
"memory_mb": 512
},
"nova_object.version": "1.0",
"nova_object.namespace": "nova"
},
"user_id":"fake",
"uuid":"178b0921-8f85-4257-88b6-2e743b5a975c"
},
"nova_object.name":"InstanceActionPayload",
"nova_object.namespace":"nova",
"nova_object.version":"1.0"
},
"priority":"INFO",
"publisher_id":"nova-compute:compute"
}

View File

@@ -0,0 +1,65 @@
{
"event_type": "instance.update",
"payload": {
"nova_object.data": {
"architecture": "x86_64",
"audit_period": {
"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"
},
"availability_zone": null,
"bandwidth": [],
"created_at": "2012-10-29T13:42:11Z",
"deleted_at": null,
"display_name": "some-server",
"host": "compute",
"host_name": "some-server",
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"kernel_id": "",
"launched_at": null,
"metadata": {},
"node": "fake-mini",
"old_display_name": null,
"os_type": null,
"progress": 0,
"ramdisk_id": "",
"reservation_id": "r-sd3ygfjj",
"state": "active",
"task_state": "scheduling",
"power_state": "pending",
"ip_addresses": [],
"state_update": {
"nova_object.data": {
"new_task_state": null,
"old_state": null,
"old_task_state": null,
"state": "active"},
"nova_object.name": "InstanceStateUpdatePayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"},
"tenant_id": "6f70656e737461636b20342065766572",
"terminated_at": null,
"flavor": {
"nova_object.name": "FlavorPayload",
"nova_object.data": {
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
"root_gb": 1,
"vcpus": 1,
"ephemeral_gb": 0,
"memory_mb": 512
},
"nova_object.version": "1.0",
"nova_object.namespace": "nova"
},
"user_id": "fake",
"uuid": "c03c0bf9-f46e-4e4f-93f1-817568567ee2"},
"nova_object.name": "InstanceUpdatePayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"},
"priority": "INFO",
"publisher_id": "nova-compute:compute"
}

View File

@@ -0,0 +1,70 @@
{
"event_type": "instance.update",
"payload": {
"nova_object.data": {
"architecture": "x86_64",
"audit_period": {
"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"
},
"availability_zone": null,
"bandwidth": [],
"created_at": "2012-10-29T13:42:11Z",
"deleted_at": null,
"display_name": "some-server",
"host": "Node_0",
"host_name": "some-server",
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"kernel_id": "",
"launched_at": null,
"metadata": {},
"node": "hostname_0",
"old_display_name": null,
"os_type": null,
"progress": 0,
"ramdisk_id": "",
"reservation_id": "r-sd3ygfjj",
"state": "active",
"task_state": "scheduling",
"power_state": "pending",
"ip_addresses": [],
"state_update": {
"nova_object.version": "1.0",
"nova_object.name": "InstanceStateUpdatePayload",
"nova_object.namespace": "nova",
"nova_object.data": {
"old_state": "building",
"new_task_state": null,
"old_task_state": "spawning",
"state": "active"
}
},
"tenant_id": "6f70656e737461636b20342065766572",
"terminated_at": null,
"flavor": {
"nova_object.name": "FlavorPayload",
"nova_object.data": {
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
"root_gb": 1,
"vcpus": 1,
"ephemeral_gb": 0,
"memory_mb": 512
},
"nova_object.version": "1.0",
"nova_object.namespace": "nova"
},
"user_id": "fake",
"uuid": "c03c0bf9-f46e-4e4f-93f1-817568567ee2"
},
"nova_object.name": "InstanceUpdatePayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
},
"priority": "INFO",
"publisher_id": "nova-compute:Node_0"
}

View File

@@ -0,0 +1,49 @@
{
"event_type":"instance.delete.end",
"payload":{
"nova_object.data":{
"architecture":"x86_64",
"availability_zone":null,
"created_at":"2012-10-29T13:42:11Z",
"deleted_at":"2012-10-29T13:42:11Z",
"display_name":"some-server",
"fault":null,
"host":"Node_0",
"host_name":"some-server",
"ip_addresses":[],
"kernel_id":"",
"launched_at":"2012-10-29T13:42:11Z",
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"metadata":{},
"node":"fake-mini",
"os_type":null,
"progress":0,
"ramdisk_id":"",
"reservation_id":"r-npxv0e40",
"state":"deleted",
"task_state":null,
"power_state":"pending",
"tenant_id":"6f70656e737461636b20342065766572",
"terminated_at":"2012-10-29T13:42:11Z",
"flavor": {
"nova_object.name": "FlavorPayload",
"nova_object.data": {
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
"root_gb": 1,
"vcpus": 1,
"ephemeral_gb": 0,
"memory_mb": 512
},
"nova_object.version": "1.0",
"nova_object.namespace": "nova"
},
"user_id":"fake",
"uuid":"73b09e16-35b7-4922-804e-e8f5d9b740fc"
},
"nova_object.name":"InstanceActionPayload",
"nova_object.namespace":"nova",
"nova_object.version":"1.0"
},
"priority":"INFO",
"publisher_id":"nova-compute:Node_0"
}

View File

@@ -0,0 +1,65 @@
{
"event_type": "instance.update",
"payload": {
"nova_object.data": {
"architecture": "x86_64",
"audit_period": {
"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"
},
"availability_zone": null,
"bandwidth": [],
"created_at": "2012-10-29T13:42:11Z",
"deleted_at": null,
"display_name": "NEW_INSTANCE0",
"host": "Node_0",
"host_name": "NEW_INSTANCE0",
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"kernel_id": "",
"launched_at": null,
"metadata": {},
"node": "hostname_0",
"old_display_name": null,
"os_type": null,
"progress": 0,
"ramdisk_id": "",
"reservation_id": "r-sd3ygfjj",
"state": "paused",
"task_state": "scheduling",
"power_state": "pending",
"ip_addresses": [],
"state_update": {
"nova_object.data": {
"old_task_state": null,
"new_task_state": null,
"old_state": "paused",
"state": "paused"},
"nova_object.name": "InstanceStateUpdatePayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"},
"tenant_id": "6f70656e737461636b20342065766572",
"terminated_at": null,
"flavor": {
"nova_object.name": "FlavorPayload",
"nova_object.data": {
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
"root_gb": 1,
"vcpus": 1,
"ephemeral_gb": 0,
"memory_mb": 512
},
"nova_object.version": "1.0",
"nova_object.namespace": "nova"
},
"user_id": "fake",
"uuid": "73b09e16-35b7-4922-804e-e8f5d9b740fc"},
"nova_object.name": "InstanceUpdatePayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"},
"priority": "INFO",
"publisher_id": "nova-compute:Node_0"
}

View File

@@ -0,0 +1,62 @@
{
"event_type": "compute.instance.create.end",
"metadata": {
"message_id": "577bfd11-88e0-4044-b8ae-496e3257efe2",
"timestamp": "2016-08-19 10:20:59.279903"
},
"payload": {
"access_ip_v4": null,
"access_ip_v6": null,
"architecture": null,
"availability_zone": "nova",
"cell_name": "",
"created_at": "2016-08-19 10:20:49+00:00",
"deleted_at": "",
"disk_gb": 1,
"display_name": "INSTANCE_0",
"ephemeral_gb": 0,
"fixed_ips": [
{
"address": "192.168.1.197",
"floating_ips": [],
"label": "demo-net",
"meta": {},
"type": "fixed",
"version": 4,
"vif_mac": "fa:16:3e:a3:c0:0f"
}
],
"host": "Node_0",
"hostname": "INSTANCE_0",
"image_meta": {
"base_image_ref": "205f96f5-91f9-42eb-9138-03fffcea2b97",
"container_format": "bare",
"disk_format": "qcow2",
"min_disk": "1",
"min_ram": "0"
},
"image_ref_url": "http://127.0.0.1:9292/images/205f96f5-91f9-42eb-9138-03fffcea2b97",
"instance_flavor_id": "1",
"instance_id": "c03c0bf9-f46e-4e4f-93f1-817568567ee2",
"instance_type": "m1.tiny",
"instance_type_id": 2,
"kernel_id": "",
"launched_at": "2016-08-19T10:20:59.135390",
"memory_mb": 512,
"message": "Success",
"metadata": {},
"node": "Node_0",
"os_type": null,
"progress": "",
"ramdisk_id": "",
"reservation_id": "r-56edz88e",
"root_gb": 1,
"state": "active",
"state_description": "",
"tenant_id": "57ab04ad6d3b495789a58258bc00842b",
"terminated_at": "",
"user_id": "cd7d93be51e4460ab51514b2a925b23a",
"vcpus": 1
},
"publisher_id": "compute.Node_0"
}

View File

@@ -0,0 +1,46 @@
{
"publisher_id": "compute:compute",
"event_type": "compute.instance.delete.end",
"payload": {
"access_ip_v4": null,
"access_ip_v6": null,
"architecture": null,
"availability_zone": "nova",
"cell_name": "",
"created_at": "2016-08-17 15:10:12+00:00",
"deleted_at": "2016-08-17T15:10:33.000000",
"disk_gb": 1,
"display_name": "some-server",
"ephemeral_gb": 0,
"host": "Node_0",
"hostname": "some-server",
"image_meta": {
"base_image_ref": "205f96f5-91f9-42eb-9138-03fffcea2b97",
"container_format": "bare",
"disk_format": "qcow2",
"min_disk": "1",
"min_ram": "0"
},
"image_ref_url": "http://10.50.254.222:9292/images/205f96f5-91f9-42eb-9138-03fffcea2b97",
"instance_flavor_id": "1",
"instance_id": "73b09e16-35b7-4922-804e-e8f5d9b740fc",
"instance_type": "m1.tiny",
"instance_type_id": 2,
"kernel_id": "",
"launched_at": "2016-08-17T15:10:23.000000",
"memory_mb": 512,
"metadata": {},
"node": "Node_0",
"os_type": null,
"progress": "",
"ramdisk_id": "",
"reservation_id": "r-z76fnsyy",
"root_gb": 1,
"state": "deleted",
"state_description": "",
"tenant_id": "15995ea2694e4268b3631db32e38678b",
"terminated_at": "2016-08-17T15:10:33.008164",
"user_id": "cd7d93be51e4460ab51514b2a925b23a",
"vcpus": 1
}
}

View File

@@ -0,0 +1,52 @@
{
"publisher_id": "compute:Node_0",
"event_type": "compute.instance.update",
"payload": {
"access_ip_v4": null,
"access_ip_v6": null,
"architecture": null,
"audit_period_beginning": "2016-08-17T13:00:00.000000",
"audit_period_ending": "2016-08-17T13:56:05.262440",
"availability_zone": "nova",
"bandwidth": {},
"cell_name": "",
"created_at": "2016-08-17 13:53:23+00:00",
"deleted_at": "",
"disk_gb": 1,
"display_name": "NEW_INSTANCE0",
"ephemeral_gb": 0,
"host": "Node_0",
"hostname": "NEW_INSTANCE0",
"image_meta": {
"base_image_ref": "205f96f5-91f9-42eb-9138-03fffcea2b97",
"container_format": "bare",
"disk_format": "qcow2",
"min_disk": "1",
"min_ram": "0"
},
"image_ref_url": "http://10.50.0.222:9292/images/205f96f5-91f9-42eb-9138-03fffcea2b97",
"instance_flavor_id": "1",
"instance_id": "73b09e16-35b7-4922-804e-e8f5d9b740fc",
"instance_type": "m1.tiny",
"instance_type_id": 2,
"kernel_id": "",
"launched_at": "2016-08-17T13:53:35.000000",
"memory_mb": 512,
"metadata": {},
"new_task_state": null,
"node": "hostname_0",
"old_state": "paused",
"old_task_state": null,
"os_type": null,
"progress": "",
"ramdisk_id": "",
"reservation_id": "r-0822ymml",
"root_gb": 1,
"state": "paused",
"state_description": "paused",
"tenant_id": "a4b4772d93c74d5e8b7c68cdd2a014e1",
"terminated_at": "",
"user_id": "ce64facc93354bbfa90f4f9f9a3e1e75",
"vcpus": 1
}
}

View File

@@ -0,0 +1,61 @@
{
"event_type": "compute.instance.live_migration.post.dest.end",
"metadata": {
"message_id": "9f58cad4-ff90-40f8-a8e4-633807f4a995",
"timestamp": "2016-08-19 10:13:44.645575"
},
"payload": {
"access_ip_v4": null,
"access_ip_v6": null,
"architecture": null,
"availability_zone": "nova",
"cell_name": "",
"created_at": "2016-08-18 09:49:23+00:00",
"deleted_at": "",
"disk_gb": 1,
"display_name": "INSTANCE_0",
"ephemeral_gb": 0,
"fixed_ips": [
{
"address": "192.168.1.196",
"floating_ips": [],
"label": "demo-net",
"meta": {},
"type": "fixed",
"version": 4,
"vif_mac": "fa:16:3e:cc:ba:81"
}
],
"host": "Node_1",
"hostname": "INSTANCE_0",
"image_meta": {
"base_image_ref": "205f96f5-91f9-42eb-9138-03fffcea2b97",
"container_format": "bare",
"disk_format": "qcow2",
"min_disk": "1",
"min_ram": "0"
},
"image_ref_url": "http://10.50.254.222:9292/images/205f96f5-91f9-42eb-9138-03fffcea2b97",
"instance_flavor_id": "1",
"instance_id": "73b09e16-35b7-4922-804e-e8f5d9b740fc",
"instance_type": "m1.tiny",
"instance_type_id": 2,
"kernel_id": "",
"launched_at": "2016-08-18T09:49:33.000000",
"memory_mb": 512,
"metadata": {},
"node": "Node_1",
"os_type": null,
"progress": "",
"ramdisk_id": "",
"reservation_id": "r-he04tfco",
"root_gb": 1,
"state": "active",
"state_description": "",
"tenant_id": "57ab04ad6d3b495789a58258bc00842b",
"terminated_at": "",
"user_id": "cd7d93be51e4460ab51514b2a925b23a",
"vcpus": 1
},
"publisher_id": "compute.Node_1"
}

View File

@@ -0,0 +1,21 @@
{
"priority": "INFO",
"payload": {
"nova_object.namespace": "nova",
"nova_object.name": "ServiceStatusPayload",
"nova_object.version": "1.0",
"nova_object.data": {
"host": "Node_0",
"disabled": true,
"last_seen_up": "2012-10-29T13:42:05Z",
"binary": "nova-compute",
"topic": "compute",
"disabled_reason": null,
"report_count": 1,
"forced_down": true,
"version": 15
}
},
"event_type": "service.update",
"publisher_id": "nova-compute:Node_0"
}

View File

@@ -0,0 +1,21 @@
{
"priority": "INFO",
"payload": {
"nova_object.namespace": "nova",
"nova_object.name": "ServiceStatusPayload",
"nova_object.version": "1.0",
"nova_object.data": {
"host": "host1",
"disabled": false,
"last_seen_up": "2012-10-29T13:42:05Z",
"binary": "nova-compute",
"topic": "compute",
"disabled_reason": null,
"report_count": 1,
"forced_down": false,
"version": 15
}
},
"event_type": "service.update",
"publisher_id": "nova-compute:host1"
}

View File

@@ -0,0 +1,53 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Authors: Vincent FRANCOISE <vincent.francoise@b-com.com>
#
# 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 watcher.decision_engine.model.notification import nova as novanotification
from watcher.tests.decision_engine.strategy.strategies \
import faker_cluster_state
class FakeManager(object):
API_VERSION = '1.0'
def __init__(self):
self.api_version = self.API_VERSION
# fake cluster instead on Nova CDM
self.fake_cdmc = faker_cluster_state.FakerModelCollector()
self.publisher_id = 'test_publisher_id'
self.conductor_topic = 'test_conductor_topic'
self.status_topic = 'test_status_topic'
self.notification_topics = ['nova']
self.conductor_endpoints = [] # Disable audit endpoint
self.status_endpoints = []
self.notification_endpoints = [
novanotification.ServiceUpdated(self.fake_cdmc),
novanotification.InstanceCreated(self.fake_cdmc),
novanotification.InstanceUpdated(self.fake_cdmc),
novanotification.InstanceDeletedEnd(self.fake_cdmc),
novanotification.LegacyInstanceCreatedEnd(self.fake_cdmc),
novanotification.LegacyInstanceUpdated(self.fake_cdmc),
novanotification.LegacyLiveMigratedEnd(self.fake_cdmc),
novanotification.LegacyInstanceDeletedEnd(self.fake_cdmc),
]

View File

@@ -0,0 +1,106 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Authors: Vincent FRANCOISE <vincent.francoise@b-com.com>
#
# 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.
import os
import mock
from oslo_serialization import jsonutils
from watcher.common import context
from watcher.common import service as watcher_service
from watcher.decision_engine.model.notification import base
from watcher.decision_engine.model.notification import filtering
from watcher.tests import base as base_test
from watcher.tests.decision_engine.model.notification import fake_managers
class DummyManager(fake_managers.FakeManager):
def __init__(self):
super(DummyManager, self).__init__()
self.notification_endpoints = [DummyNotification(self.fake_cdmc)]
class DummyNotification(base.NotificationEndpoint):
@property
def filter_rule(self):
return filtering.NotificationFilter(
publisher_id=r'.*',
event_type=r'compute.dummy',
payload={'data': {'nested': r'^T.*'}},
)
def info(self, ctxt, publisher_id, event_type, payload, metadata):
pass
class NotificationTestCase(base_test.TestCase):
def load_message(self, filename):
cwd = os.path.abspath(os.path.dirname(__file__))
data_folder = os.path.join(cwd, "data")
with open(os.path.join(data_folder, filename), 'rb') as json_file:
json_data = jsonutils.load(json_file)
return json_data
class TestReceiveNotifications(NotificationTestCase):
def setUp(self):
super(TestReceiveNotifications, self).setUp()
p_from_dict = mock.patch.object(context.RequestContext, 'from_dict')
m_from_dict = p_from_dict.start()
m_from_dict.return_value = self.context
self.addCleanup(p_from_dict.stop)
@mock.patch.object(DummyNotification, 'info')
def test_receive_dummy_notification(self, m_info):
message = {
'publisher_id': 'nova-compute',
'event_type': 'compute.dummy',
'payload': {'data': {'nested': 'TEST'}},
'priority': 'INFO',
}
de_service = watcher_service.Service(DummyManager)
incoming = mock.Mock(ctxt=self.context.to_dict(), message=message)
de_service.notification_handler.dispatcher.dispatch(incoming)
m_info.assert_called_once_with(
self.context, 'nova-compute', 'compute.dummy',
{'data': {'nested': 'TEST'}},
{'message_id': None, 'timestamp': None})
@mock.patch.object(DummyNotification, 'info')
def test_skip_unwanted_notification(self, m_info):
message = {
'publisher_id': 'nova-compute',
'event_type': 'compute.dummy',
'payload': {'data': {'nested': 'unwanted'}},
'priority': 'INFO',
}
de_service = watcher_service.Service(DummyManager)
incoming = mock.Mock(ctxt=self.context.to_dict(), message=message)
de_service.notification_handler.dispatcher.dispatch(incoming)
self.assertEqual(0, m_info.call_count)

View File

@@ -0,0 +1,450 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Authors: Vincent FRANCOISE <vincent.francoise@b-com.com>
#
# 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.
import os
import mock
from oslo_serialization import jsonutils
from watcher.common import context
from watcher.common import exception
from watcher.common import service as watcher_service
from watcher.decision_engine.model import element
from watcher.decision_engine.model import model_root
from watcher.decision_engine.model.notification import nova as novanotification
from watcher.tests import base as base_test
from watcher.tests.decision_engine.model.notification import fake_managers
from watcher.tests.decision_engine.strategy.strategies \
import faker_cluster_state
class NotificationTestCase(base_test.TestCase):
def load_message(self, filename):
cwd = os.path.abspath(os.path.dirname(__file__))
data_folder = os.path.join(cwd, "data")
with open(os.path.join(data_folder, filename), 'rb') as json_file:
json_data = jsonutils.load(json_file)
return json_data
class TestReceiveNovaNotifications(NotificationTestCase):
FAKE_METADATA = {'message_id': None, 'timestamp': None}
def setUp(self):
super(TestReceiveNovaNotifications, self).setUp()
p_from_dict = mock.patch.object(context.RequestContext, 'from_dict')
m_from_dict = p_from_dict.start()
m_from_dict.return_value = self.context
self.addCleanup(p_from_dict.stop)
@mock.patch.object(novanotification.ServiceUpdated, 'info')
def test_nova_receive_service_update(self, m_info):
message = self.load_message('service-update.json')
expected_message = message['payload']
de_service = watcher_service.Service(fake_managers.FakeManager)
incoming = mock.Mock(ctxt=self.context.to_dict(), message=message)
de_service.notification_handler.dispatcher.dispatch(incoming)
m_info.assert_called_once_with(
self.context, 'nova-compute:host1', 'service.update',
expected_message, self.FAKE_METADATA)
@mock.patch.object(novanotification.InstanceCreated, 'info')
def test_nova_receive_instance_create(self, m_info):
message = self.load_message('instance-create.json')
expected_message = message['payload']
de_service = watcher_service.Service(fake_managers.FakeManager)
incoming = mock.Mock(ctxt=self.context.to_dict(), message=message)
de_service.notification_handler.dispatcher.dispatch(incoming)
m_info.assert_called_once_with(
self.context, 'nova-compute:compute', 'instance.update',
expected_message, self.FAKE_METADATA)
@mock.patch.object(novanotification.InstanceUpdated, 'info')
def test_nova_receive_instance_update(self, m_info):
message = self.load_message('instance-update.json')
expected_message = message['payload']
de_service = watcher_service.Service(fake_managers.FakeManager)
incoming = mock.Mock(ctxt=self.context.to_dict(), message=message)
de_service.notification_handler.dispatcher.dispatch(incoming)
m_info.assert_called_once_with(
self.context, 'nova-compute:compute', 'instance.update',
expected_message, self.FAKE_METADATA)
@mock.patch.object(novanotification.InstanceDeletedEnd, 'info')
def test_nova_receive_instance_delete_end(self, m_info):
message = self.load_message('instance-delete-end.json')
expected_message = message['payload']
de_service = watcher_service.Service(fake_managers.FakeManager)
incoming = mock.Mock(ctxt=self.context.to_dict(), message=message)
de_service.notification_handler.dispatcher.dispatch(incoming)
m_info.assert_called_once_with(
self.context, 'nova-compute:compute', 'instance.delete.end',
expected_message, self.FAKE_METADATA)
class TestNovaNotifications(NotificationTestCase):
FAKE_METADATA = {'message_id': None, 'timestamp': None}
def setUp(self):
super(TestNovaNotifications, self).setUp()
# fake cluster
self.fake_cdmc = faker_cluster_state.FakerModelCollector()
def test_nova_service_update(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.ServiceUpdated(self.fake_cdmc)
node0_uuid = 'Node_0'
node0 = compute_model.get_node_from_id(node0_uuid)
message = self.load_message('scenario3_service-update.json')
self.assertEqual('hostname_0', node0.hostname)
self.assertEqual(element.ServiceState.ONLINE.value, node0.state)
self.assertEqual(element.ServiceState.ENABLED.value, node0.status)
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
self.assertEqual('Node_0', node0.hostname)
self.assertEqual(element.ServiceState.OFFLINE.value, node0.state)
self.assertEqual(element.ServiceState.DISABLED.value, node0.status)
def test_nova_instance_update(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.InstanceUpdated(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
instance0 = compute_model.get_instance_from_id(instance0_uuid)
message = self.load_message('scenario3_instance-update.json')
self.assertEqual(element.InstanceState.ACTIVE.value, instance0.state)
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
self.assertEqual(element.InstanceState.PAUSED.value, instance0.state)
def test_nova_instance_update_notfound_creates(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.InstanceUpdated(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
message = self.load_message('scenario3_instance-update.json')
with mock.patch.object(
model_root.ModelRoot, 'get_instance_from_id'
) as m_get_instance_from_id:
m_get_instance_from_id.side_effect = exception.InstanceNotFound(
name='TEST')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
instance0 = compute_model.get_instance_from_id(instance0_uuid)
cpu_capacity = compute_model.get_resource_from_id(
element.ResourceType.cpu_cores)
disk_capacity = compute_model.get_resource_from_id(
element.ResourceType.disk)
memory_capacity = compute_model.get_resource_from_id(
element.ResourceType.memory)
self.assertEqual(element.InstanceState.PAUSED.value, instance0.state)
self.assertEqual(1, cpu_capacity.get_capacity(instance0))
self.assertEqual(1, disk_capacity.get_capacity(instance0))
self.assertEqual(512, memory_capacity.get_capacity(instance0))
def test_nova_instance_create(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.InstanceCreated(self.fake_cdmc)
instance0_uuid = 'c03c0bf9-f46e-4e4f-93f1-817568567ee2'
self.assertRaises(
exception.InstanceNotFound,
compute_model.get_instance_from_id, instance0_uuid)
message = self.load_message('scenario3_instance-create.json')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
instance0 = compute_model.get_instance_from_id(instance0_uuid)
cpu_capacity = compute_model.get_resource_from_id(
element.ResourceType.cpu_cores)
disk_capacity = compute_model.get_resource_from_id(
element.ResourceType.disk)
memory_capacity = compute_model.get_resource_from_id(
element.ResourceType.memory)
self.assertEqual(element.InstanceState.ACTIVE.value, instance0.state)
self.assertEqual(1, cpu_capacity.get_capacity(instance0))
self.assertEqual(1, disk_capacity.get_capacity(instance0))
self.assertEqual(512, memory_capacity.get_capacity(instance0))
def test_nova_instance_delete_end(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.InstanceDeletedEnd(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
# Before
self.assertTrue(compute_model.get_instance_from_id(instance0_uuid))
for resource in compute_model.resource.values():
self.assertIn(instance0_uuid, resource.mapping)
message = self.load_message('scenario3_instance-delete-end.json')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
# After
self.assertRaises(
exception.InstanceNotFound,
compute_model.get_instance_from_id, instance0_uuid)
for resource in compute_model.resource.values():
self.assertNotIn(instance0_uuid, resource.mapping)
class TestLegacyNovaNotifications(NotificationTestCase):
FAKE_METADATA = {'message_id': None, 'timestamp': None}
def setUp(self):
super(TestLegacyNovaNotifications, self).setUp()
# fake cluster
self.fake_cdmc = faker_cluster_state.FakerModelCollector()
def test_legacy_instance_created_end(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.LegacyInstanceCreatedEnd(self.fake_cdmc)
instance0_uuid = 'c03c0bf9-f46e-4e4f-93f1-817568567ee2'
self.assertRaises(
exception.InstanceNotFound,
compute_model.get_instance_from_id, instance0_uuid)
message = self.load_message(
'scenario3_legacy_instance-create-end.json')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
instance0 = compute_model.get_instance_from_id(instance0_uuid)
cpu_capacity = compute_model.get_resource_from_id(
element.ResourceType.cpu_cores)
disk_capacity = compute_model.get_resource_from_id(
element.ResourceType.disk)
memory_capacity = compute_model.get_resource_from_id(
element.ResourceType.memory)
self.assertEqual(element.InstanceState.ACTIVE.value, instance0.state)
self.assertEqual(1, cpu_capacity.get_capacity(instance0))
self.assertEqual(1, disk_capacity.get_capacity(instance0))
self.assertEqual(512, memory_capacity.get_capacity(instance0))
def test_legacy_instance_updated(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.LegacyInstanceUpdated(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
instance0 = compute_model.get_instance_from_id(instance0_uuid)
message = self.load_message('scenario3_legacy_instance-update.json')
self.assertEqual(element.InstanceState.ACTIVE.value, instance0.state)
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
self.assertEqual(element.InstanceState.PAUSED.value, instance0.state)
def test_legacy_instance_update_notfound_creates(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.LegacyInstanceUpdated(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
message = self.load_message('scenario3_legacy_instance-update.json')
with mock.patch.object(
model_root.ModelRoot, 'get_instance_from_id'
) as m_get_instance_from_id:
m_get_instance_from_id.side_effect = exception.InstanceNotFound(
name='TEST')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
instance0 = compute_model.get_instance_from_id(instance0_uuid)
self.assertEqual(element.InstanceState.PAUSED.value, instance0.state)
def test_legacy_instance_update_node_notfound_stil_creates(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.LegacyInstanceUpdated(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
message = self.load_message('scenario3_legacy_instance-update.json')
with mock.patch.object(
model_root.ModelRoot, 'get_instance_from_id'
) as m_get_instance_from_id:
m_get_instance_from_id.side_effect = exception.InstanceNotFound(
name='TEST')
with mock.patch.object(
model_root.ModelRoot, 'get_node_from_id'
) as m_get_node_from_id:
m_get_node_from_id.side_effect = exception.ComputeNodeNotFound(
name='TEST')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
instance0 = compute_model.get_instance_from_id(instance0_uuid)
cpu_capacity = compute_model.get_resource_from_id(
element.ResourceType.cpu_cores)
disk_capacity = compute_model.get_resource_from_id(
element.ResourceType.disk)
memory_capacity = compute_model.get_resource_from_id(
element.ResourceType.memory)
self.assertEqual(element.InstanceState.PAUSED.value, instance0.state)
self.assertEqual(1, cpu_capacity.get_capacity(instance0))
self.assertEqual(1, disk_capacity.get_capacity(instance0))
self.assertEqual(512, memory_capacity.get_capacity(instance0))
def test_legacy_live_migrated_end(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.LegacyLiveMigratedEnd(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
instance0 = compute_model.get_instance_from_id(instance0_uuid)
node = compute_model.get_node_from_instance_id(instance0_uuid)
self.assertEqual('Node_0', node.uuid)
message = self.load_message(
'scenario3_legacy_livemigration-post-dest-end.json')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
node = compute_model.get_node_from_instance_id(instance0_uuid)
self.assertEqual('Node_1', node.uuid)
self.assertEqual(element.InstanceState.ACTIVE.value, instance0.state)
def test_legacy_instance_deleted_end(self):
compute_model = self.fake_cdmc.generate_scenario_3_with_2_nodes()
self.fake_cdmc.cluster_data_model = compute_model
handler = novanotification.LegacyInstanceDeletedEnd(self.fake_cdmc)
instance0_uuid = '73b09e16-35b7-4922-804e-e8f5d9b740fc'
# Before
self.assertTrue(compute_model.get_instance_from_id(instance0_uuid))
for resource in compute_model.resource.values():
self.assertIn(instance0_uuid, resource.mapping)
message = self.load_message(
'scenario3_legacy_instance-delete-end.json')
handler.info(
ctxt=self.context,
publisher_id=message['publisher_id'],
event_type=message['event_type'],
payload=message['payload'],
metadata=self.FAKE_METADATA,
)
# After
self.assertRaises(
exception.InstanceNotFound,
compute_model.get_instance_from_id, instance0_uuid)
for resource in compute_model.resource.values():
self.assertNotIn(instance0_uuid, resource.mapping)

View File

@@ -80,16 +80,16 @@ class TestMapping(base.TestCase):
self.assertEqual(
False,
model.mapping.migrate_instance(instance1, node1, node1))
model.migrate_instance(instance1, node1, node1))
self.assertEqual(
False,
model.mapping.migrate_instance(instance1, node0, node0))
model.migrate_instance(instance1, node0, node0))
self.assertEqual(
True,
model.mapping.migrate_instance(instance1, node1, node0))
model.migrate_instance(instance1, node1, node0))
self.assertEqual(
True,
model.mapping.migrate_instance(instance1, node0, node1))
model.migrate_instance(instance1, node0, node1))
def test_unmap_from_id_log_warning(self):
model = self.fake_cluster.generate_scenario_3_with_2_nodes()

View File

@@ -73,11 +73,11 @@ class TestModel(base.TestCase):
node.uuid = id_
model.add_node(node)
self.assertIsInstance(node.state, element.ServiceState)
self.assertIn(node.state, [el.value for el in element.ServiceState])
node = model.get_node_from_id(id_)
node.state = element.ServiceState.OFFLINE
self.assertIsInstance(node.state, element.ServiceState)
node.state = element.ServiceState.OFFLINE.value
self.assertIn(node.state, [el.value for el in element.ServiceState])
def test_node_from_id_raise(self):
model = model_root.ModelRoot()

View File

@@ -31,6 +31,10 @@ class FakerModelCollector(base.BaseClusterDataModelCollector):
config = mock.Mock()
super(FakerModelCollector, self).__init__(config)
@property
def notification_endpoints(self):
return []
def execute(self):
return self.generate_scenario_1()

View File

@@ -30,8 +30,12 @@ class FakerModelCollector(base.BaseClusterDataModelCollector):
config = mock.Mock(period=777)
super(FakerModelCollector, self).__init__(config)
@property
def notification_endpoints(self):
return []
def execute(self):
return self.generate_scenario_1()
return self._cluster_data_model or self.generate_scenario_1()
def generate_scenario_1(self):
instances = []

View File

@@ -95,10 +95,12 @@ class TestUniformAirflow(base.TestCase):
self.strategy.threshold_inlet_t = 22
n1, n2 = self.strategy.group_hosts_by_airflow()
instance_to_mig = self.strategy.choose_instance_to_migrate(n1)
self.assertEqual(instance_to_mig[0].uuid, 'Node_0')
self.assertEqual(len(instance_to_mig[1]), 1)
self.assertEqual(instance_to_mig[1][0].uuid,
"cae81432-1631-4d4e-b29c-6f3acdcde906")
self.assertIn(instance_to_mig[1][0].uuid,
{'cae81432-1631-4d4e-b29c-6f3acdcde906',
'73b09e16-35b7-4922-804e-e8f5d9b740fc'})
def test_choose_instance_to_migrate_all(self):
model = self.fake_cluster.generate_scenario_7_with_2_nodes()
@@ -107,10 +109,12 @@ class TestUniformAirflow(base.TestCase):
self.strategy.threshold_inlet_t = 25
n1, n2 = self.strategy.group_hosts_by_airflow()
instance_to_mig = self.strategy.choose_instance_to_migrate(n1)
self.assertEqual(instance_to_mig[0].uuid, 'Node_0')
self.assertEqual(len(instance_to_mig[1]), 2)
self.assertEqual(instance_to_mig[1][1].uuid,
"73b09e16-35b7-4922-804e-e8f5d9b740fc")
self.assertEqual({'cae81432-1631-4d4e-b29c-6f3acdcde906',
'73b09e16-35b7-4922-804e-e8f5d9b740fc'},
{inst.uuid for inst in instance_to_mig[1]})
def test_choose_instance_notfound(self):
model = self.fake_cluster.generate_scenario_7_with_2_nodes()
@@ -132,10 +136,12 @@ class TestUniformAirflow(base.TestCase):
instance_to_mig = self.strategy.choose_instance_to_migrate(n1)
dest_hosts = self.strategy.filter_destination_hosts(
n2, instance_to_mig[1])
self.assertEqual(len(dest_hosts), 1)
self.assertEqual(dest_hosts[0]['node'].uuid, 'Node_1')
self.assertEqual(dest_hosts[0]['instance'].uuid,
'cae81432-1631-4d4e-b29c-6f3acdcde906')
self.assertIn(instance_to_mig[1][0].uuid,
{'cae81432-1631-4d4e-b29c-6f3acdcde906',
'73b09e16-35b7-4922-804e-e8f5d9b740fc'})
def test_exception_model(self):
self.m_model.return_value = None