Support Nova versioned notifications
In Stein release, Nova will deprecate their legacy (unversioned) notifications that were used by Vitrage. Vitrage should support both the versioned notifications and the legacy notifications. In another change, we will switch the default behavior to prefer the versioned notifications (in case both are sent by Nova). More info: http://lists.openstack.org/pipermail/openstack-dev/2018-September/134721.html Change-Id: Ia5fece6e07766b68af316034736bd7a51b84f38b Story: 2004052 Task: 27058
This commit is contained in:
parent
dc48a3ecfa
commit
a96bc0642b
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Added support for Nova versioned notifications.
|
@ -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():
|
||||
|
92
vitrage/datasources/nova/instance/field_extractor.py
Normal file
92
vitrage/datasources/nova/instance/field_extractor.py
Normal file
@ -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')
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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"
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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(
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user