diff --git a/releasenotes/notes/nova-versioned-notifications-3c5ff450b9fe69f0.yaml b/releasenotes/notes/nova-versioned-notifications-3c5ff450b9fe69f0.yaml new file mode 100644 index 000000000..1c42d1d52 --- /dev/null +++ b/releasenotes/notes/nova-versioned-notifications-3c5ff450b9fe69f0.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added support for Nova versioned notifications. diff --git a/vitrage/datasources/nova/instance/driver.py b/vitrage/datasources/nova/instance/driver.py index 142a3c8d4..8a507826a 100644 --- a/vitrage/datasources/nova/instance/driver.py +++ b/vitrage/datasources/nova/instance/driver.py @@ -19,8 +19,87 @@ from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE from vitrage.datasources.nova.nova_driver_base import NovaDriverBase +# versioned notifications +VERSIONED_NOTIFICATIONS = { + 'instance.create.end', + 'instance.create.error', + 'instance.delete.end', + 'instance.delete.start', + 'instance.evacuate', + 'instance.interface_attach.end', + 'instance.interface_attach.error', + 'instance.interface_detach.end', + 'instance.live_migration_abort.end', + 'instance.live_migration_force_complete.end', + 'instance.live_migration_post.end', + 'instance.live_migration_post_dest.end', + 'instance.live_migration_rollback.end', + 'instance.live_migration_rollback_dest.end', + 'instance.lock', + 'instance.pause.end', + 'instance.power_off.end', + 'instance.power_on.end', + 'instance.reboot.end', + 'instance.reboot.error', + 'instance.rebuild.end', + 'instance.rebuild.error', + 'instance.rescue.end', + 'instance.resize.end', + 'instance.resize.error', + 'instance.resize_confirm.end', + 'instance.resize_finish.end', + 'instance.resize_prep.end', + 'instance.resize_revert.end', + 'instance.restore.end', + 'instance.resume.end', + 'instance.shelve.end', + 'instance.shelve_offload.end', + 'instance.shutdown.end', + 'instance.soft_delete.end', + 'instance.snapshot.end', + 'instance.suspend.end', + 'instance.unlock', + 'instance.unpause.end', + 'instance.unrescue.end', + 'instance.unshelve.end', + 'instance.update', + 'instance.volume_attach.end', + 'instance.volume_attach.error', + 'instance.volume_detach.end', + 'instance.volume_swap.end', + 'instance.volume_swap.error', +} + +# legacy (unversioned) notifications +LEGACY_NOTIFICATIONS = { + 'compute.instance.create.error', + 'compute.instance.create.end', + 'compute.instance.delete.start', + 'compute.instance.delete.end', + 'compute.instance.finish_resize.end', + 'compute.instance.live_migration.post.dest.end', + 'compute.instance.live_migration._post.end', + 'compute.instance.power_off.end', + 'compute.instance.power_on.end', + 'compute.instance.reboot.end', + 'compute.instance.rebuild.end', + 'compute.instance.resize.end', + 'compute.instance.resize.revert.end', + 'compute.instance.resume.end', + 'compute.instance.shutdown.end', + 'compute.instance.suspend.end', + 'compute.instance.volume.attach', + 'compute.instance.volume.detach', + 'compute.instance.pause.end', + 'compute.instance.unpause.end' +} + + class InstanceDriver(NovaDriverBase): + def __init__(self, conf): + super(InstanceDriver, self).__init__(conf) + @staticmethod def extract_events(instances): events = [instance.__dict__ for instance in instances] @@ -38,11 +117,19 @@ class InstanceDriver(NovaDriverBase): *self.properties_to_filter_out()) def enrich_event(self, event, event_type): - event[DSProps.EVENT_TYPE] = event_type + use_versioned = self.conf.use_nova_versioned_notifications - return InstanceDriver.make_pickleable([event], - NOVA_INSTANCE_DATASOURCE, - DatasourceAction.UPDATE)[0] + # Send to the processor only events of the matching types. Nova may + # send both versioned and legacy notifications, and we don't want to + # handle a similar event twice. + if (use_versioned and event_type in VERSIONED_NOTIFICATIONS) or \ + ((not use_versioned) and event_type in LEGACY_NOTIFICATIONS): + event[DSProps.EVENT_TYPE] = event_type + return InstanceDriver.make_pickleable([event], + NOVA_INSTANCE_DATASOURCE, + DatasourceAction.UPDATE)[0] + + return [] @staticmethod def properties_to_filter_out(): @@ -50,27 +137,7 @@ class InstanceDriver(NovaDriverBase): @staticmethod def get_event_types(): - # Add event_types to receive notifications about - return ['compute.instance.create.error', - 'compute.instance.create.end', - 'compute.instance.delete.start', - 'compute.instance.delete.end', - 'compute.instance.finish_resize.end', - 'compute.instance.live_migration.post.dest.end', - 'compute.instance.live_migration._post.end', - 'compute.instance.power_off.end', - 'compute.instance.power_on.end', - 'compute.instance.reboot.end', - 'compute.instance.rebuild.end', - 'compute.instance.resize.end', - 'compute.instance.resize.revert.end', - 'compute.instance.resume.end', - 'compute.instance.shutdown.end', - 'compute.instance.suspend.end', - 'compute.instance.volume.attach', - 'compute.instance.volume.detach', - 'compute.instance.pause.end', - 'compute.instance.unpause.end'] + return list(VERSIONED_NOTIFICATIONS | LEGACY_NOTIFICATIONS) @staticmethod def should_delete_outdated_entities(): diff --git a/vitrage/datasources/nova/instance/field_extractor.py b/vitrage/datasources/nova/instance/field_extractor.py new file mode 100644 index 000000000..e204054ff --- /dev/null +++ b/vitrage/datasources/nova/instance/field_extractor.py @@ -0,0 +1,92 @@ +# Copyright 2018 - Nokia +# +# 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 abc +from vitrage.datasources.transformer_base import extract_field_value + + +class EventFieldExtractor(object): + """Extract field values from a Nova instance event""" + @abc.abstractmethod + def name(self, event): + pass + + @abc.abstractmethod + def entity_id(self, event): + pass + + @abc.abstractmethod + def state(self, event): + pass + + @abc.abstractmethod + def host(self, event): + pass + + @abc.abstractmethod + def tenant_id(self, event): + pass + + +class SnapshotEventFieldExtractor(EventFieldExtractor): + """Extract field values from an event generated by nova.list API call""" + def name(self, event): + return extract_field_value(event, 'name') + + def entity_id(self, event): + return extract_field_value(event, 'id') + + def state(self, event): + return extract_field_value(event, 'status') + + def host(self, event): + return extract_field_value(event, 'OS-EXT-SRV-ATTR:host') + + def tenant_id(self, event): + return extract_field_value(event, 'tenant_id') + + +class LegacyNotificationFieldExtractor(EventFieldExtractor): + """Extract field values from a Nova legacy notification""" + def name(self, event): + return extract_field_value(event, 'hostname') + + def entity_id(self, event): + return extract_field_value(event, 'instance_id') + + def state(self, event): + return extract_field_value(event, 'state') + + def host(self, event): + return extract_field_value(event, 'host') + + def tenant_id(self, event): + return extract_field_value(event, 'tenant_id') + + +class VersionedNotificationFieldExtractor(EventFieldExtractor): + """Extract field values from a Nova versioned notification""" + def name(self, event): + return extract_field_value(event, 'nova_object.data', 'host_name') + + def entity_id(self, event): + return extract_field_value(event, 'nova_object.data', 'uuid') + + def state(self, event): + return extract_field_value(event, 'nova_object.data', 'state') + + def host(self, event): + return extract_field_value(event, 'nova_object.data', 'host') + + def tenant_id(self, event): + return extract_field_value(event, 'nova_object.data', 'tenant_id') diff --git a/vitrage/datasources/nova/instance/transformer.py b/vitrage/datasources/nova/instance/transformer.py index e4704302e..2db4d5828 100644 --- a/vitrage/datasources/nova/instance/transformer.py +++ b/vitrage/datasources/nova/instance/transformer.py @@ -19,6 +19,12 @@ from vitrage.common.constants import EntityCategory from vitrage.common.constants import GraphAction from vitrage.common.constants import VertexProperties as VProps from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE +from vitrage.datasources.nova.instance.field_extractor import \ + LegacyNotificationFieldExtractor +from vitrage.datasources.nova.instance.field_extractor import \ + SnapshotEventFieldExtractor +from vitrage.datasources.nova.instance.field_extractor import \ + VersionedNotificationFieldExtractor from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE from vitrage.datasources.resource_transformer_base import \ ResourceTransformerBase @@ -31,38 +37,38 @@ LOG = logging.getLogger(__name__) class InstanceTransformer(ResourceTransformerBase): + snapshot_extractor = SnapshotEventFieldExtractor() + legacy_notifications_extractor = LegacyNotificationFieldExtractor() + versioned_notifications_extractor = VersionedNotificationFieldExtractor() + # graph actions which need to refer them differently GRAPH_ACTION_MAPPING = { 'compute.instance.delete.end': GraphAction.DELETE_ENTITY, + 'instance.delete.end': GraphAction.DELETE_ENTITY, } def __init__(self, transformers, conf): super(InstanceTransformer, self).__init__(transformers, conf) def _create_snapshot_entity_vertex(self, entity_event): - - name = extract_field_value(entity_event, 'name') - entity_id = extract_field_value(entity_event, 'id') - state = extract_field_value(entity_event, 'status') - host = extract_field_value(entity_event, 'OS-EXT-SRV-ATTR:host') - - return self._create_vertex(entity_event, name, entity_id, state, host) + LOG.debug('got snapshot') + return self._create_vertex(entity_event) def _create_update_entity_vertex(self, entity_event): + LOG.debug('got event: %s', entity_event[DSProps.EVENT_TYPE]) + return self._create_vertex(entity_event) - name = extract_field_value(entity_event, 'hostname') - entity_id = extract_field_value(entity_event, 'instance_id') - state = extract_field_value(entity_event, 'state') - host = extract_field_value(entity_event, 'host') - - return self._create_vertex(entity_event, name, entity_id, state, host) - - def _create_vertex(self, entity_event, name, entity_id, state, host): + def _create_vertex(self, entity_event): + field_extractor = self._get_field_extractor(entity_event) + if not field_extractor: + LOG.warning('Failed to identify event type for event: %s', + entity_event) + return metadata = { - VProps.NAME: name, - VProps.PROJECT_ID: entity_event.get('tenant_id', None), - 'host_id': host + VProps.NAME: field_extractor.name(entity_event), + VProps.PROJECT_ID: field_extractor.tenant_id(entity_event), + 'host_id': field_extractor.host(entity_event) } vitrage_sample_timestamp = entity_event[DSProps.SAMPLE_DATE] @@ -78,21 +84,26 @@ class InstanceTransformer(ResourceTransformerBase): vitrage_category=EntityCategory.RESOURCE, vitrage_type=NOVA_INSTANCE_DATASOURCE, vitrage_sample_timestamp=vitrage_sample_timestamp, - entity_id=entity_id, - entity_state=state, + entity_id=field_extractor.entity_id(entity_event), + entity_state=field_extractor.state(entity_event), update_timestamp=update_timestamp, metadata=metadata) def _create_snapshot_neighbors(self, entity_event): - return self._create_instance_neighbors(entity_event, - 'OS-EXT-SRV-ATTR:host') + return self._create_instance_neighbors(entity_event) def _create_update_neighbors(self, entity_event): - return self._create_instance_neighbors(entity_event, - 'host') + return self._create_instance_neighbors(entity_event) + + def _create_instance_neighbors(self, entity_event): + field_extractor = self._get_field_extractor(entity_event) + if not field_extractor: + LOG.warning('Failed to identify event type for event: %s', + entity_event) + return [] + + host_name = field_extractor.host(entity_event) - def _create_instance_neighbors(self, entity_event, host_property_name): - host_name = entity_event.get(host_property_name) host_neighbor = self._create_neighbor(entity_event, host_name, NOVA_HOST_DATASOURCE, @@ -104,14 +115,22 @@ class InstanceTransformer(ResourceTransformerBase): def _create_entity_key(self, event): LOG.debug('Creating key for instance event: %s', str(event)) - instance_id = 'instance_id' if tbase.is_update_event(event) else 'id' - key_fields = self._key_values(NOVA_INSTANCE_DATASOURCE, - extract_field_value(event, - instance_id)) + instance_id = self._get_field_extractor(event).entity_id(event) + key_fields = self._key_values(NOVA_INSTANCE_DATASOURCE, instance_id) key = tbase.build_key(key_fields) + LOG.debug('Created key: %s', key) return key def get_vitrage_type(self): return NOVA_INSTANCE_DATASOURCE + + def _get_field_extractor(self, event): + """Return an object that extracts the field values from the event""" + if tbase.is_update_event(event): + return self.versioned_notifications_extractor if \ + self.conf.use_nova_versioned_notifications is True else \ + self.legacy_notifications_extractor + else: + return self.snapshot_extractor diff --git a/vitrage/os_clients.py b/vitrage/os_clients.py index d339d09ce..9a4160b18 100644 --- a/vitrage/os_clients.py +++ b/vitrage/os_clients.py @@ -28,7 +28,14 @@ OPTS = [ cfg.StrOpt('glance_version', default='2', help='Glance version'), cfg.StrOpt('heat_version', default='1', help='Heat version'), cfg.StrOpt('mistral_version', default='2', help='Mistral version'), - cfg.StrOpt('gnocchi_version', default='1', help='Gnocchi version') + cfg.StrOpt('gnocchi_version', default='1', help='Gnocchi version'), + cfg.BoolOpt('use_nova_versioned_notifications', + default=False, + help='Indicates whether to use Nova versioned notifications.' + 'The default is True. If False, the deprecated Nova ' + 'legacy notifications will be used.' + 'This flag must be set to False if notification_format ' + 'is set to "unversioned" in nova.conf'), ] _client_modules = { diff --git a/vitrage/tests/functional/base.py b/vitrage/tests/functional/base.py index fca317d80..359139356 100644 --- a/vitrage/tests/functional/base.py +++ b/vitrage/tests/functional/base.py @@ -45,9 +45,9 @@ class TestFunctionalBase(TestEntityGraphUnitBase): snap_vals={DSProps.DATASOURCE_ACTION: DatasourceAction.INIT_SNAPSHOT}) gen_list += mock_driver.simple_instance_generators( - self.NUM_HOSTS, - self.NUM_INSTANCES, - self.NUM_INSTANCES, + host_num=self.NUM_HOSTS, + vm_num=self.NUM_INSTANCES, + snapshot_events=self.NUM_INSTANCES, snap_vals={DSProps.DATASOURCE_ACTION: DatasourceAction.INIT_SNAPSHOT}) return mock_driver.generate_sequential_events_list(gen_list) diff --git a/vitrage/tests/mocks/mock_driver.py b/vitrage/tests/mocks/mock_driver.py index c6b9552c2..966379b19 100644 --- a/vitrage/tests/mocks/mock_driver.py +++ b/vitrage/tests/mocks/mock_driver.py @@ -88,6 +88,7 @@ def generate_sequential_events_list(generator_spec_list): def simple_instance_generators(host_num, vm_num, snapshot_events=0, update_events=0, + use_nova_versioned_format=True, snap_vals=None, update_vals=None): """A function for returning vm event generators. @@ -98,6 +99,8 @@ def simple_instance_generators(host_num, vm_num, :param vm_num: number of vms :param snapshot_events: number of snapshot events per instance :param update_events: number of update events per instance + :param use_nova_versioned_format: use the format of Nova versioned + notifications for the update events :param snap_vals: preset vals for ALL snapshot events :param update_vals: preset vals for ALL update events :return: generators for vm_num vms as specified @@ -118,9 +121,13 @@ def simple_instance_generators(host_num, vm_num, tg.NUM_EVENTS: snapshot_events } ) + + dynamic_info = tg.DRIVER_INST_UPDATE_VERSIONED_D \ + if use_nova_versioned_format else tg.DRIVER_INST_UPDATE_LEGACY_D + if update_events: test_entity_spec_list.append( - {tg.DYNAMIC_INFO_FKEY: tg.DRIVER_INST_UPDATE_D, + {tg.DYNAMIC_INFO_FKEY: dynamic_info, tg.STATIC_INFO_FKEY: None, tg.EXTERNAL_INFO_KEY: update_vals, tg.MAPPING_KEY: mapping, @@ -128,6 +135,7 @@ def simple_instance_generators(host_num, vm_num, tg.NUM_EVENTS: update_events } ) + return tg.get_trace_generators(test_entity_spec_list) diff --git a/vitrage/tests/mocks/trace_generator.py b/vitrage/tests/mocks/trace_generator.py index 63ee34633..9da2d3132 100644 --- a/vitrage/tests/mocks/trace_generator.py +++ b/vitrage/tests/mocks/trace_generator.py @@ -53,7 +53,8 @@ DRIVER_COLLECTD_UPDATE_D = 'driver_collectd_update_dynamic.json' DRIVER_HOST_SNAPSHOT_D = 'driver_host_snapshot_dynamic.json' DRIVER_INST_SNAPSHOT_D = 'driver_inst_snapshot_dynamic.json' DRIVER_INST_SNAPSHOT_S = 'driver_inst_snapshot_static.json' -DRIVER_INST_UPDATE_D = 'driver_inst_update_dynamic.json' +DRIVER_INST_UPDATE_LEGACY_D = 'driver_inst_update_legacy_dynamic.json' +DRIVER_INST_UPDATE_VERSIONED_D = 'driver_inst_update_versioned_dynamic.json' DRIVER_NAGIOS_SNAPSHOT_D = 'driver_nagios_snapshot_dynamic.json' DRIVER_NAGIOS_SNAPSHOT_S = 'driver_nagios_snapshot_static.json' DRIVER_PROMETHEUS_UPDATE_D = 'driver_prometheus_update_dynamic.json' @@ -123,7 +124,9 @@ class EventTraceGenerator(object): DRIVER_COLLECTD_UPDATE_D: _get_simple_update_driver_values, DRIVER_KUBE_SNAPSHOT_D: _get_k8s_node_snapshot_driver_values, DRIVER_INST_SNAPSHOT_D: _get_vm_snapshot_driver_values, - DRIVER_INST_UPDATE_D: _get_vm_update_driver_values, + DRIVER_INST_UPDATE_LEGACY_D: _get_vm_update_legacy_driver_values, + DRIVER_INST_UPDATE_VERSIONED_D: + _get_vm_update_versioned_driver_values, DRIVER_HOST_SNAPSHOT_D: _get_host_snapshot_driver_values, DRIVER_ZONE_SNAPSHOT_D: _get_zone_snapshot_driver_values, DRIVER_VOLUME_SNAPSHOT_D: _get_volume_snapshot_driver_values, @@ -487,7 +490,7 @@ def _get_trans_vm_snapshot_values(spec): return static_values -def _get_vm_update_driver_values(spec): +def _get_vm_update_legacy_driver_values(spec): """Generates the static driver values for each vm, for updates. :param spec: specification of event generation. @@ -511,6 +514,35 @@ def _get_vm_update_driver_values(spec): return static_values +def _get_vm_update_versioned_driver_values(spec): + """Generates the static driver values for each vm, for updates. + + :param spec: specification of event generation. + :type spec: dict + :return: list of static driver values for each vm updates. + :rtype: list + """ + + vm_host_mapping = spec[MAPPING_KEY] + static_info = None + if spec[STATIC_INFO_FKEY] is not None: + static_info = utils.load_specs(spec[STATIC_INFO_FKEY]) + static_values = [] + for vm_name, host_name in vm_host_mapping: + mapping = { + 'nova_object.data': { + 'host': host_name, + 'display_name': vm_name + } + + } + static_values.append(combine_data( + static_info, mapping, spec.get(EXTERNAL_INFO_KEY, None) + )) + + return static_values + + def _get_static_snapshot_driver_values(spec): """Generates the static driver values for static datasource. diff --git a/vitrage/tests/resources/mock_configurations/driver/driver_inst_update_dynamic.json b/vitrage/tests/resources/mock_configurations/driver/driver_inst_update_legacy_dynamic.json similarity index 100% rename from vitrage/tests/resources/mock_configurations/driver/driver_inst_update_dynamic.json rename to vitrage/tests/resources/mock_configurations/driver/driver_inst_update_legacy_dynamic.json diff --git a/vitrage/tests/resources/mock_configurations/driver/driver_inst_update_versioned_dynamic.json b/vitrage/tests/resources/mock_configurations/driver/driver_inst_update_versioned_dynamic.json new file mode 100644 index 000000000..8e0366ea6 --- /dev/null +++ b/vitrage/tests/resources/mock_configurations/driver/driver_inst_update_versioned_dynamic.json @@ -0,0 +1,103 @@ +{ + "vitrage_datasource_action": "update", + "vitrage_sample_date": "2015-12-01T12:46:41Z", + "vitrage_event_type": "instance.pause.end|instance.delete.end|instance.create.end", + "nova_object.data": { + "action_initiator_project": "6f70656e737461636b20342065766572", + "action_initiator_user": "fake", + "architecture": "x86_64", + "auto_disk_config": "MANUAL", + "availability_zone": "nova", + "block_devices": [], + "created_at": "2012-10-29T13:42:11Z", + "deleted_at": null, + "display_description": "some-server", + "display_name": "some-server", + "fault": null, + "flavor": { + "nova_object.data": { + "description": null, + "disabled": false, + "ephemeral_gb": 0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "is_public": true, + "memory_mb": 512, + "name": "test_flavor", + "projects": null, + "root_gb": 1, + "rxtx_factor": 1.0, + "swap": 0, + "vcpu_weight": 0, + "vcpus": 1 + }, + "nova_object.name": "FlavorPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.4" + }, + "host": "compute", + "host_name": "some-server", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ip_addresses": [ + { + "nova_object.data": { + "address": "192.168.1.3", + "device_name": "tapce531f90-19", + "label": "private-network", + "mac": "fa:16:3e:4c:2c:30", + "meta": {}, + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "version": 4 + }, + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + } + ], + "kernel_id": "", + "key_name": "my-key", + "keypairs": [ + { + "nova_object.data": { + "fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", + "name": "my-key", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated-by-Nova", + "type": "ssh", + "user_id": "fake" + }, + "nova_object.name": "KeypairPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0" + } + ], + "launched_at": "2012-10-29T13:42:11Z", + "locked": false, + "metadata": {}, + "node": "fake-mini", + "os_type": null, + "power_state": "running", + "progress": 0, + "ramdisk_id": "", + "request_id": "req-5b6c791d-5709-4f36-8fbe-c3e02869e35d", + "reservation_id": "r-npxv0e40", + "state": "active", + "tags": [ + "tag" + ], + "task_state": null, + "tenant_id": "6f70656e737461636b20342065766572", + "terminated_at": null, + "trusted_image_certificates": [ + "cert-id-1", + "cert-id-2" + ], + "updated_at": "2012-10-29T13:42:11Z", + "user_id": "fake", + "uuid": "178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name": "InstanceCreatePayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.10" +} diff --git a/vitrage/tests/unit/datasources/nova/test_nova_instance_transformer.py b/vitrage/tests/unit/datasources/nova/base_nova_instance_transformer.py similarity index 61% rename from vitrage/tests/unit/datasources/nova/test_nova_instance_transformer.py rename to vitrage/tests/unit/datasources/nova/base_nova_instance_transformer.py index 276d2373a..c597c7bed 100644 --- a/vitrage/tests/unit/datasources/nova/test_nova_instance_transformer.py +++ b/vitrage/tests/unit/datasources/nova/base_nova_instance_transformer.py @@ -12,13 +12,13 @@ # License for the specific language governing permissions and limitations # under the License. +import abc import datetime from oslo_config import cfg from oslo_log import log as logging from testtools import matchers -from vitrage.common.constants import DatasourceAction from vitrage.common.constants import DatasourceOpts as DSOpts from vitrage.common.constants import DatasourceProperties as DSProps from vitrage.common.constants import EdgeLabel @@ -39,7 +39,7 @@ LOG = logging.getLogger(__name__) # noinspection PyProtectedMember -class NovaInstanceTransformerTest(base.BaseTest): +class BaseNovaInstanceTransformerTest(base.BaseTest): OPTS = [ cfg.StrOpt(DSOpts.UPDATE_METHOD, @@ -49,16 +49,17 @@ class NovaInstanceTransformerTest(base.BaseTest): # noinspection PyAttributeOutsideInit,PyPep8Naming @classmethod def setUpClass(cls): - super(NovaInstanceTransformerTest, cls).setUpClass() + super(BaseNovaInstanceTransformerTest, cls).setUpClass() cls.transformers = {} cls.conf = cfg.ConfigOpts() cls.conf.register_opts(cls.OPTS, group=NOVA_INSTANCE_DATASOURCE) + cls.conf.register_opts(cls._get_default_group_opts()) cls.transformers[NOVA_HOST_DATASOURCE] = HostTransformer( cls.transformers, cls.conf) cls.transformers[NOVA_INSTANCE_DATASOURCE] = \ InstanceTransformer(cls.transformers, cls.conf) - def test_create_placeholder_vertex(self): + def _test_create_placeholder_vertex(self): LOG.debug('Test create placeholder vertex') # Tests setup @@ -100,53 +101,14 @@ class NovaInstanceTransformerTest(base.BaseTest): vitrage_is_placeholder = placeholder.get(VProps.VITRAGE_IS_PLACEHOLDER) self.assertTrue(vitrage_is_placeholder) - def test_snapshot_event_transform(self): - LOG.debug('Test tactual transform action for ' - 'snapshot and snapshot init events') - - # Test setup - spec_list = mock_sync.simple_instance_generators(host_num=1, - vm_num=1, - snapshot_events=10, - update_events=0) - instance_events = mock_sync.generate_random_events_list(spec_list) - + def _test_update_event_transform(self, instance_events): for event in instance_events: # Test action - wrapper = self.transformers[NOVA_INSTANCE_DATASOURCE].transform( - event) + transformer = self.transformers[NOVA_INSTANCE_DATASOURCE] + wrapper = transformer.transform(event) # Test assertions - self._validate_vertex_props(wrapper.vertex, event) - - self.assertThat(wrapper.neighbors, matchers.HasLength(1), - 'Instance has only one host neighbor') - host_neighbor = wrapper.neighbors[0] - self._validate_host_neighbor(host_neighbor, event) - - datasource_action = event[DSProps.DATASOURCE_ACTION] - if datasource_action == DatasourceAction.INIT_SNAPSHOT: - self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action) - elif datasource_action == DatasourceAction.SNAPSHOT: - self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action) - - def test_update_event_transform(self): - LOG.debug('Test tactual transform action for update events') - - # Test setup - spec_list = mock_sync.simple_instance_generators(host_num=1, - vm_num=1, - snapshot_events=0, - update_events=10) - instance_events = mock_sync.generate_random_events_list(spec_list) - - for event in instance_events: - # Test action - wrapper = self.transformers[NOVA_INSTANCE_DATASOURCE].transform( - event) - - # Test assertions - self._validate_vertex_props(wrapper.vertex, event) + self._validate_vertex_props(transformer, wrapper.vertex, event) # Validate the neighbors: only one valid host neighbor neighbors = wrapper.neighbors @@ -161,16 +123,12 @@ class NovaInstanceTransformerTest(base.BaseTest): else: self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action) - def _validate_vertex_props(self, vertex, event): + def _validate_vertex_props(self, transformer, vertex, event): self.assertThat(vertex.properties, matchers.HasLength(14)) - is_update_event = tbase.is_update_event(event) - - extract_value = tbase.extract_field_value - - instance_id = 'instance_id' if is_update_event else 'id' - expected_id = extract_value(event, instance_id) + field_extractor = transformer._get_field_extractor(event) + expected_id = field_extractor.entity_id(event) observed_id = vertex[VProps.ID] self.assertEqual(expected_id, observed_id) @@ -181,12 +139,11 @@ class NovaInstanceTransformerTest(base.BaseTest): self.assertEqual(NOVA_INSTANCE_DATASOURCE, vertex[VProps.VITRAGE_TYPE]) - expected_project = extract_value(event, 'tenant_id') + expected_project = field_extractor.tenant_id(event) observed_project = vertex[VProps.PROJECT_ID] self.assertEqual(expected_project, observed_project) - state = 'state' if is_update_event else 'status' - expected_state = extract_value(event, state) + expected_state = field_extractor.state(event) observed_state = vertex[VProps.STATE] self.assertEqual(expected_state, observed_state) @@ -194,8 +151,7 @@ class NovaInstanceTransformerTest(base.BaseTest): observed_timestamp = vertex[VProps.VITRAGE_SAMPLE_TIMESTAMP] self.assertEqual(expected_timestamp, observed_timestamp) - name = 'hostname' if is_update_event else 'name' - expected_name = extract_value(event, name) + expected_name = field_extractor.name(event) observed_name = vertex[VProps.NAME] self.assertEqual(expected_name, observed_name) @@ -207,14 +163,13 @@ class NovaInstanceTransformerTest(base.BaseTest): def _validate_host_neighbor(self, h_neighbor, event): - it = self.transformers[NOVA_INSTANCE_DATASOURCE] + inst_transformer = self.transformers[NOVA_INSTANCE_DATASOURCE] + field_extractor = inst_transformer._get_field_extractor(event) - name = 'host' if tbase.is_update_event(event) \ - else 'OS-EXT-SRV-ATTR:host' - host_name = tbase.extract_field_value(event, name) + host_name = field_extractor.host(event) time = event[DSProps.SAMPLE_DATE] - ht = self.transformers[NOVA_HOST_DATASOURCE] + host_transformer = self.transformers[NOVA_HOST_DATASOURCE] properties = { VProps.ID: host_name, VProps.VITRAGE_TYPE: NOVA_HOST_DATASOURCE, @@ -222,28 +177,24 @@ class NovaInstanceTransformerTest(base.BaseTest): VProps.VITRAGE_SAMPLE_TIMESTAMP: time } expected_neighbor = \ - ht.create_neighbor_placeholder_vertex(**properties) + host_transformer.create_neighbor_placeholder_vertex(**properties) self.assertEqual(expected_neighbor, h_neighbor.vertex) # Validate neighbor edge edge = h_neighbor.edge - entity_key = it._create_entity_key(event) + entity_key = inst_transformer._create_entity_key(event) entity_uuid = \ TransformerBase.uuid_from_deprecated_vitrage_id(entity_key) self.assertEqual(edge.source_id, h_neighbor.vertex.vertex_id) self.assertEqual(edge.target_id, entity_uuid) self.assertEqual(edge.label, EdgeLabel.CONTAINS) - def test_create_entity_key(self): + def _test_create_entity_key(self): LOG.debug('Test get key from nova instance transformer') # Test setup spec_list = mock_sync.simple_instance_generators( - host_num=1, - vm_num=1, - snapshot_events=1, - update_events=0 - ) + host_num=1, vm_num=1, snapshot_events=1) instance_events = mock_sync.generate_random_events_list(spec_list) instance_transformer = self.transformers[NOVA_INSTANCE_DATASOURCE] @@ -272,7 +223,7 @@ class NovaInstanceTransformerTest(base.BaseTest): self.assertEqual(expected_key, observed_key) - def test_build_instance_key(self): + def _test_build_instance_key(self): LOG.debug('Test build instance key') # Test setup @@ -289,44 +240,7 @@ class NovaInstanceTransformerTest(base.BaseTest): observed_key = tbase.build_key(key_fields) self.assertEqual(expected_key, observed_key) - def test_create_host_neighbor(self): - LOG.debug('Test create host neighbor') - - # Test setup - host_name = 'host123' - vertex_key = 'RESOURCE:nova.instance:instance321' - vertex_id = \ - TransformerBase.uuid_from_deprecated_vitrage_id(vertex_key) - time = datetime.datetime.utcnow() - entity_event = { - '_info': { - 'host_name': host_name - }, - DSProps.DATASOURCE_ACTION: 'SNAPSHOT', - 'id': 'instance321', - DSProps.SAMPLE_DATE: time - } - - # Test action - instance_transformer = self.transformers[NOVA_INSTANCE_DATASOURCE] - neighbor = \ - instance_transformer._create_neighbor(entity_event, - host_name, - NOVA_HOST_DATASOURCE, - EdgeLabel.CONTAINS, - is_entity_source=False) - - # Test assertions - host_vertex_id = \ - TransformerBase.uuid_from_deprecated_vitrage_id( - 'RESOURCE:nova.host:host123') - self.assertEqual(host_vertex_id, neighbor.vertex.vertex_id) - self.assertEqual( - time, - neighbor.vertex.get(VProps.VITRAGE_SAMPLE_TIMESTAMP) - ) - - # test relation edge - self.assertEqual(host_vertex_id, neighbor.edge.source_id) - self.assertEqual(vertex_id, neighbor.edge.target_id) - self.assertEqual(EdgeLabel.CONTAINS, neighbor.edge.label) + @classmethod + @abc.abstractmethod + def _get_default_group_opts(cls): + pass diff --git a/vitrage/tests/unit/datasources/nova/test_instance_transformer_legacy_notifications.py b/vitrage/tests/unit/datasources/nova/test_instance_transformer_legacy_notifications.py new file mode 100644 index 000000000..22113a69e --- /dev/null +++ b/vitrage/tests/unit/datasources/nova/test_instance_transformer_legacy_notifications.py @@ -0,0 +1,62 @@ +# Copyright 2018 - Nokia +# +# 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 oslo_config import cfg +from oslo_log import log as logging + +from vitrage.tests.mocks import mock_driver as mock_sync +from vitrage.tests.unit.datasources.nova.base_nova_instance_transformer \ + import BaseNovaInstanceTransformerTest + +LOG = logging.getLogger(__name__) + + +# noinspection PyProtectedMember +class NovaInstanceTransformerLegacyNotifTest( + BaseNovaInstanceTransformerTest): + + DEFAULT_GROUP_OPTS = [ + cfg.BoolOpt('use_nova_versioned_notifications', + default=False), + ] + + # noinspection PyAttributeOutsideInit,PyPep8Naming + @classmethod + def setUpClass(cls): + super(NovaInstanceTransformerLegacyNotifTest, cls).setUpClass() + + def test_update_event_transform(self): + LOG.debug('Test actual transform action for update events') + + # Test setup + spec_list = mock_sync.simple_instance_generators( + host_num=1, vm_num=1, update_events=10, + use_nova_versioned_format=False + ) + instance_events = mock_sync.generate_random_events_list(spec_list) + + self._test_update_event_transform(instance_events) + + def test_create_placeholder_vertex(self): + self._test_create_placeholder_vertex() + + def test_create_entity_key(self): + self._test_create_entity_key() + + def test_build_instance_key(self): + self._test_build_instance_key() + + @classmethod + def _get_default_group_opts(cls): + return cls.DEFAULT_GROUP_OPTS diff --git a/vitrage/tests/unit/datasources/nova/test_instance_transformer_snapshot_events.py b/vitrage/tests/unit/datasources/nova/test_instance_transformer_snapshot_events.py new file mode 100644 index 000000000..bdbda48c0 --- /dev/null +++ b/vitrage/tests/unit/datasources/nova/test_instance_transformer_snapshot_events.py @@ -0,0 +1,130 @@ +# Copyright 2018 - Nokia +# +# 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 datetime +from oslo_config import cfg +from oslo_log import log as logging +from testtools import matchers + +from vitrage.common.constants import DatasourceAction +from vitrage.common.constants import DatasourceProperties as DSProps +from vitrage.common.constants import EdgeLabel +from vitrage.common.constants import GraphAction +from vitrage.common.constants import VertexProperties as VProps +from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE +from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE +from vitrage.datasources.transformer_base import TransformerBase +from vitrage.tests.mocks import mock_driver as mock_sync +from vitrage.tests.unit.datasources.nova.base_nova_instance_transformer \ + import BaseNovaInstanceTransformerTest + +LOG = logging.getLogger(__name__) + + +# noinspection PyProtectedMember +class NovaInstanceTransformerSnapshotTest( + BaseNovaInstanceTransformerTest): + + DEFAULT_GROUP_OPTS = [ + cfg.BoolOpt('use_nova_versioned_notifications', + default=False), + ] + + # noinspection PyAttributeOutsideInit,PyPep8Naming + @classmethod + def setUpClass(cls): + super(NovaInstanceTransformerSnapshotTest, cls).setUpClass() + + def test_snapshot_event_transform(self): + LOG.debug('Test tactual transform action for ' + 'snapshot and snapshot init events') + + # Test setup + spec_list = mock_sync.simple_instance_generators( + host_num=1, vm_num=1, snapshot_events=10) + instance_events = mock_sync.generate_random_events_list(spec_list) + + for event in instance_events: + # Test action + transformer = self.transformers[NOVA_INSTANCE_DATASOURCE] + wrapper = transformer.transform(event) + + # Test assertions + self._validate_vertex_props(transformer, wrapper.vertex, event) + + self.assertThat(wrapper.neighbors, matchers.HasLength(1), + 'Instance has only one host neighbor') + host_neighbor = wrapper.neighbors[0] + self._validate_host_neighbor(host_neighbor, event) + + datasource_action = event[DSProps.DATASOURCE_ACTION] + if datasource_action == DatasourceAction.INIT_SNAPSHOT: + self.assertEqual(GraphAction.CREATE_ENTITY, wrapper.action) + elif datasource_action == DatasourceAction.SNAPSHOT: + self.assertEqual(GraphAction.UPDATE_ENTITY, wrapper.action) + + def test_create_host_neighbor(self): + LOG.debug('Test create host neighbor') + + # Test setup + host_name = 'host123' + vertex_key = 'RESOURCE:nova.instance:instance321' + vertex_id = \ + TransformerBase.uuid_from_deprecated_vitrage_id(vertex_key) + time = datetime.datetime.utcnow() + entity_event = { + '_info': { + 'host_name': host_name + }, + DSProps.DATASOURCE_ACTION: 'SNAPSHOT', + 'id': 'instance321', + DSProps.SAMPLE_DATE: time + } + + # Test action + instance_transformer = self.transformers[NOVA_INSTANCE_DATASOURCE] + neighbor = \ + instance_transformer._create_neighbor(entity_event, + host_name, + NOVA_HOST_DATASOURCE, + EdgeLabel.CONTAINS, + is_entity_source=False) + + # Test assertions + host_vertex_id = \ + TransformerBase.uuid_from_deprecated_vitrage_id( + 'RESOURCE:nova.host:host123') + self.assertEqual(host_vertex_id, neighbor.vertex.vertex_id) + self.assertEqual( + time, + neighbor.vertex.get(VProps.VITRAGE_SAMPLE_TIMESTAMP) + ) + + # test relation edge + self.assertEqual(host_vertex_id, neighbor.edge.source_id) + self.assertEqual(vertex_id, neighbor.edge.target_id) + self.assertEqual(EdgeLabel.CONTAINS, neighbor.edge.label) + + def test_create_placeholder_vertex(self): + self._test_create_placeholder_vertex() + + def test_create_entity_key(self): + self._test_create_entity_key() + + def test_build_instance_key(self): + self._test_build_instance_key() + + @classmethod + def _get_default_group_opts(cls): + return cls.DEFAULT_GROUP_OPTS diff --git a/vitrage/tests/unit/datasources/nova/test_instance_transformer_versioned_notifications.py b/vitrage/tests/unit/datasources/nova/test_instance_transformer_versioned_notifications.py new file mode 100644 index 000000000..f4523879c --- /dev/null +++ b/vitrage/tests/unit/datasources/nova/test_instance_transformer_versioned_notifications.py @@ -0,0 +1,62 @@ +# Copyright 2018 - Nokia +# +# 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 oslo_config import cfg +from oslo_log import log as logging + +from vitrage.tests.mocks import mock_driver as mock_sync +from vitrage.tests.unit.datasources.nova.base_nova_instance_transformer \ + import BaseNovaInstanceTransformerTest + +LOG = logging.getLogger(__name__) + + +# noinspection PyProtectedMember +class NovaInstanceTransformerVersionedNotifTest( + BaseNovaInstanceTransformerTest): + + DEFAULT_GROUP_OPTS = [ + cfg.BoolOpt('use_nova_versioned_notifications', + default=True), + ] + + # noinspection PyAttributeOutsideInit,PyPep8Naming + @classmethod + def setUpClass(cls): + super(NovaInstanceTransformerVersionedNotifTest, cls).setUpClass() + + def test_update_event_transform(self): + LOG.debug('Test tactual transform action for update events') + + # Test setup + spec_list = mock_sync.simple_instance_generators( + host_num=1, vm_num=1, update_events=10, + use_nova_versioned_format=True + ) + instance_events = mock_sync.generate_random_events_list(spec_list) + + self._test_update_event_transform(instance_events) + + def test_create_placeholder_vertex(self): + self._test_create_placeholder_vertex() + + def test_create_entity_key(self): + self._test_create_entity_key() + + def test_build_instance_key(self): + self._test_build_instance_key() + + @classmethod + def _get_default_group_opts(cls): + return cls.DEFAULT_GROUP_OPTS diff --git a/vitrage/tests/unit/datasources/nova/test_nova_instance_driver.py b/vitrage/tests/unit/datasources/nova/test_nova_instance_driver.py new file mode 100644 index 000000000..b50d53c09 --- /dev/null +++ b/vitrage/tests/unit/datasources/nova/test_nova_instance_driver.py @@ -0,0 +1,98 @@ +# Copyright 2018 - Nokia +# +# 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 oslo_config import cfg +from oslo_log import log as logging + +from vitrage.common.constants import DatasourceOpts as DSOpts +from vitrage.common.constants import UpdateMethod +from vitrage.datasources.nova.instance.driver import InstanceDriver +from vitrage.tests import base +from vitrage.tests.mocks import mock_driver as mock_sync + +LOG = logging.getLogger(__name__) + + +# noinspection PyProtectedMember +class NovaHostTransformerTest(base.BaseTest): + + OPTS = [ + cfg.StrOpt(DSOpts.UPDATE_METHOD, + default=UpdateMethod.PUSH), + ] + + # noinspection PyAttributeOutsideInit,PyPep8Naming + @classmethod + def setUpClass(cls): + super(NovaHostTransformerTest, cls).setUpClass() + + def test_use_versioned_notifications(self): + LOG.debug('Nova instance driver test') + + # Test setup + driver = InstanceDriver(cfg.ConfigOpts()) + driver.conf.register_opts([ + cfg.BoolOpt('use_nova_versioned_notifications', + default=True, required=True), + ]) + update_versioned_event, update_legacy_event = self._create_events() + + # Test action + events = driver.enrich_event(update_versioned_event, + 'instance.create.end') + self.assert_is_not_empty(events) + + # Test action + events = driver.enrich_event(update_legacy_event, + 'compute.instance.create.end') + self.assert_is_empty(events) + + def test_use_legacy_notifications(self): + LOG.debug('Nova instance driver test') + + # Test setup + driver = InstanceDriver(cfg.ConfigOpts()) + driver.conf.register_opts([ + cfg.BoolOpt('use_nova_versioned_notifications', + default=False, required=True), + ]) + update_versioned_event, update_legacy_event = self._create_events() + + # Test action + events = driver.enrich_event(update_versioned_event, + 'instance.create.end') + self.assert_is_empty(events) + + # Test action + events = driver.enrich_event(update_legacy_event, + 'compute.instance.create.end') + self.assert_is_not_empty(events) + + @staticmethod + def _create_events(): + spec_list = mock_sync.simple_instance_generators( + host_num=1, vm_num=1, update_events=1, + use_nova_versioned_format=True + ) + update_versioned_event = \ + mock_sync.generate_random_events_list(spec_list)[0] + + spec_list = mock_sync.simple_instance_generators( + host_num=1, vm_num=1, update_events=1, + use_nova_versioned_format=False + ) + update_legacy_event = \ + mock_sync.generate_random_events_list(spec_list)[0] + + return update_versioned_event, update_legacy_event diff --git a/vitrage/tests/unit/entity_graph/base.py b/vitrage/tests/unit/entity_graph/base.py index cf60a400f..dc9dba3ec 100644 --- a/vitrage/tests/unit/entity_graph/base.py +++ b/vitrage/tests/unit/entity_graph/base.py @@ -63,6 +63,11 @@ class TestEntityGraphUnitBase(base.BaseTest): min=1) ] + OS_CLIENTS_OPTS = [ + cfg.BoolOpt('use_nova_versioned_notifications', + default=False, required=True), + ] + NUM_CLUSTERS = 1 NUM_ZONES = 2 NUM_HOSTS = 4 @@ -98,9 +103,9 @@ class TestEntityGraphUnitBase(base.BaseTest): snap_vals={DSProps.DATASOURCE_ACTION: DatasourceAction.INIT_SNAPSHOT}) gen_list += mock_sync.simple_instance_generators( - self.NUM_HOSTS, - self.NUM_INSTANCES, - self.NUM_INSTANCES, + host_num=self.NUM_HOSTS, + vm_num=self.NUM_INSTANCES, + snapshot_events=self.NUM_INSTANCES, snap_vals={DSProps.DATASOURCE_ACTION: DatasourceAction.INIT_SNAPSHOT}) return mock_sync.generate_sequential_events_list(gen_list) @@ -138,7 +143,8 @@ class TestEntityGraphUnitBase(base.BaseTest): event_type=None, properties=None): # generate event - spec_list = mock_sync.simple_instance_generators(1, 1, 1) + spec_list = mock_sync.simple_instance_generators(host_num=1, vm_num=1, + snapshot_events=1) events_list = mock_sync.generate_random_events_list( spec_list) @@ -161,7 +167,7 @@ class TestEntityGraphUnitBase(base.BaseTest): project_id=None, vitrage_resource_project_id=None, metadata=None, - vitrage_sample_timestamp=None, + vitrage_sample_timestamp='', datasource_name=None, is_deleted=False): return graph_utils.create_vertex( diff --git a/vitrage/tests/unit/entity_graph/processor/test_processor.py b/vitrage/tests/unit/entity_graph/processor/test_processor.py index bbf58626c..f5cd437c4 100644 --- a/vitrage/tests/unit/entity_graph/processor/test_processor.py +++ b/vitrage/tests/unit/entity_graph/processor/test_processor.py @@ -46,6 +46,7 @@ class TestProcessor(TestEntityGraphUnitBase): cls.conf = cfg.ConfigOpts() cls.conf.register_opts(cls.PROCESSOR_OPTS, group='entity_graph') cls.conf.register_opts(cls.DATASOURCES_OPTS, group='datasources') + cls.conf.register_opts(cls.OS_CLIENTS_OPTS) cls.load_datasources(cls.conf) def test_process_event(self): @@ -57,13 +58,14 @@ class TestProcessor(TestEntityGraphUnitBase): self._check_graph(processor, self.NUM_VERTICES_AFTER_CREATION, self.NUM_EDGES_AFTER_CREATION) - # check update instance even + # check update instance event event[DSProps.DATASOURCE_ACTION] = DSAction.UPDATE event[DSProps.EVENT_TYPE] = 'compute.instance.volume.attach' event['hostname'] = 'new_host' event['instance_id'] = event['id'] event['state'] = event['status'] event['host'] = event['OS-EXT-SRV-ATTR:host'] + processor.process_event(event) self._check_graph(processor, self.NUM_VERTICES_AFTER_CREATION, self.NUM_EDGES_AFTER_CREATION)