nova instance transformer and tests

Change-Id: Id3eb11592c36ac196680f6b6d21bf40f1a67aebf
This commit is contained in:
Liat Har-Tal 2015-12-08 13:29:02 +00:00
parent c7942a5d85
commit 797240c436
18 changed files with 239 additions and 71 deletions

View File

@ -6,9 +6,10 @@ pbr>=1.6
Babel>=1.3 Babel>=1.3
networkx>=1.10 networkx>=1.10
oslo.log>=1.12.0 # Apache-2.0 oslo.log>=1.12.0 # Apache-2.0
oslo.policy>=0.3.0
pecan>=0.8.0 pecan>=0.8.0
PasteDeploy>=1.5.0 PasteDeploy>=1.5.0
Werkzeug>=0.7 Werkzeug>=0.7
oslo.policy>=0.3.0
keystonemiddleware>=2.3.0 keystonemiddleware>=2.3.0
stevedore>=1.5.0 # Apache-2.0
exrex>=0.9.4 exrex>=0.9.4

View File

@ -39,3 +39,8 @@ all_files = 1
[upload_sphinx] [upload_sphinx]
upload-dir = doc/build/html upload-dir = doc/build/html
[entry_points]
vitrage.transformers =
nova.instance = vitrage.entity_graph.transformer.nova_transformer.InstanceTransformer
nova.host = vitrage.entity_graph.transformer.nova_transformer.HostTransformer

View File

@ -15,3 +15,5 @@ testrepository>=0.0.18
testscenarios>=0.4 testscenarios>=0.4
testtools>=1.4.0 testtools>=1.4.0
exrex>=0.9.4 exrex>=0.9.4
stevedore>=1.5.0 # Apache-2.0

View File

@ -0,0 +1 @@
__author__ = 'stack'

View File

@ -0,0 +1,41 @@
# Copyright 2015 - Alcatel-Lucent
#
# 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.
class VertexConstants(object):
TYPE = 'TYPE'
SUB_TYPE = 'SUB_TYPE'
ID = 'ID'
IS_VERTEX_DELETED = 'IS_VERTEX_DELETED'
VERTEX_DELETION_TIMESTAMP = 'VERTEX_DELETION_TIMESTAMP'
STATE = 'STATE'
PROJECT = 'PROJECT'
UPDATE_TIMESTAMP = 'UPDATE_TIMESTAMP'
class EdgeConstants(object):
RELATION_NAME = 'RELATION_NAME'
IS_EDGE_DELETED = 'IS_EDGE_DELETED'
EDGE_DELETION_TIMESTAMP = 'EDGE_DELETION_TIMESTAMP'
class EdgeLabels(object):
ON = 'on'
CONTAINS = 'contains'
class SynchronizerMessageMode(object):
SNAPSHOT = 'snapshot'
INIT_SNAPSHOT = 'init_snapshot'
UPDATE = 'update'

View File

@ -1,30 +0,0 @@
# Copyright 2015 - Alcatel-Lucent
#
# 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.
# Vertex properties
TYPE = 'TYPE'
SUB_TYPE = 'SUB_TYPE'
ID = 'ID'
VERTEX_ID = 'VERTEX_ID'
EVENT_TYPE = 'EVENT_TYPE'
IS_VERTEX_DELETED = 'IS_VERTEX_DELETED'
VERTEX_DELETION_TIMESTAMP = 'VERTEX_DELETION_TIMESTAMP'
STATE = 'STATE'
PROJECT = 'PROJECT'
TIMESTAMP = 'TIMESTAMP'
# Edge properties
RELATION_NAME = 'RELATION_NAME'
IS_EDGE_DELETED = 'IS_EDGE_DELETED'
EDGE_DELETION_TIMESTAMP = 'EDGE_DELETION_TIMESTAMP'

View File

@ -13,12 +13,19 @@
# under the License. # under the License.
import abc import abc
import six
from collections import namedtuple
from oslo_log import log as logging from oslo_log import log as logging
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
EntityWrapper = \
namedtuple('EntityWrapper', ['entity_vertex', 'neighbors'], 'action')
@six.add_metaclass(abc.ABCMeta)
class Transformer(object): class Transformer(object):
@abc.abstractmethod @abc.abstractmethod
@ -28,13 +35,14 @@ class Transformer(object):
:return: An EntityWrapper. EntityWrapper is namedTuple that contains :return: An EntityWrapper. EntityWrapper is namedTuple that contains
an entity vertex and a list of vertex and an edge pair that describe an entity vertex and a list of vertex and an edge pair that describe
the entity's neighbors. the entity's neighbors.
:rtype: EntityWrapper
""" """
pass pass
@abc.abstractmethod @abc.abstractmethod
def get_key_fields(self): def key_fields(self):
pass pass
@abc.abstractmethod @abc.abstractmethod
def get_key(self, entity_event): def extract_key(self, entity_event):
pass pass

View File

@ -0,0 +1,47 @@
# Copyright 2015 - Alcatel-Lucent
#
# 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_log import log as logging
from vitrage.common.constants import VertexConstants as vertex_cons
from vitrage.entity_graph.transformer import base
LOG = logging.getLogger(__name__)
KEY_SEPARATOR = ':'
ENTITY_TYPE = 'RESOURCE'
INSTANCE_SUB_TYPE = 'nova.instance'
HOST_SUB_TYPE = 'nova.host'
class InstanceTransformer(base.Transformer):
# # Fields returned from Nova Instance snapshot
ENTITY_ID_DICT = {'snapshot': 'id',
'init_snapshot': 'id',
'update': 'instance_id'}
def transform(self, entity_event):
pass
def key_fields(self):
return [vertex_cons.TYPE, vertex_cons.SUB_TYPE, vertex_cons.ID]
def extract_key(self, entity_event):
sync_mode = entity_event['sync_mode']
return KEY_SEPARATOR.join(
[ENTITY_TYPE,
INSTANCE_SUB_TYPE,
entity_event[self.ENTITY_ID_DICT[sync_mode]]])

View File

@ -0,0 +1,29 @@
# Copyright 2014 - Mirantis, Inc.
# Copyright 2014 - StackStorm, Inc.
#
# 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_log import log as logging
LOG = logging.getLogger(__name__)
class TransformerManager(object):
def __init__(self):
self.transformers = self.register_transformer_classes()
@staticmethod
def register_transformer_classes():
pass

View File

@ -67,7 +67,7 @@ class Edge(object):
:param source_id: source vertex id :param source_id: source vertex id
:type source_id: str :type source_id: str
:param target_id: target vertex id :param target_id: target vertex id`
:type target_id: str :type target_id: str
:param label: :param label:

View File

@ -25,14 +25,14 @@ multiple instances of the same entity type.
from os.path import curdir from os.path import curdir
from os import walk from os import walk
from random import randint import random
from entity_model import CommonEntityModel as cem from entity_model import CommonEntityModel as cem
def _get_filename_path(filename): def _get_filename_path(filename):
base_dir = None base_dir = None
for i in walk("../%s" % curdir): for i in walk("../../%s" % curdir):
if i[0].find('resources') != -1 and filename in i[2]: if i[0].find('resources') != -1 and filename in i[2]:
base_dir = i[0] base_dir = i[0]
break break
@ -46,8 +46,6 @@ class MockEventGenerator(object):
"""Represents a single generator. """Represents a single generator.
A generator can generate events for several instances of the same type A generator can generate events for several instances of the same type
file is expected to be in the ../resources folder
""" """
def __init__(self, filename, instance_num, generator_name='generator'): def __init__(self, filename, instance_num, generator_name='generator'):
@ -75,7 +73,7 @@ class MockEventGenerator(object):
param_type = line_params[1].lower() param_type = line_params[1].lower()
params_dict[param_type][line_params[0]] = line_params[2] params_dict[param_type][line_params[0]] = line_params[2]
except KeyError as ke: except KeyError as ke:
print("Syntax error: {0}".format(ke.message)) print("Syntax error ({0}): {1}".format(ke.errno, ke.strerror))
def prepare_instance_models(self): def prepare_instance_models(self):
"""Create the models for all the instances """ """Create the models for all the instances """
@ -97,7 +95,7 @@ class MockEventGenerator(object):
data_stream = [] data_stream = []
for _ in xrange(event_num): for _ in xrange(event_num):
model = self.models[randint(0, self.instance_num - 1)] model = self.models[random.randint(0, self.instance_num - 1)]
model.generate_dynamic_params() model.generate_dynamic_params()
data_stream.append(model.params) data_stream.append(model.params)
return data_stream return data_stream

View File

@ -20,7 +20,7 @@ of what can be returned
usage example: usage example:
test_entity_spec_list = [ test_entity_spec_list = [
{'filename': '../resources/mock_nova_instance_config_1.txt', {'filename': '../resources/mock_nova_inst_snapshot.txt',
'#instances': 10, '#instances': 10,
'name': 'Instance (vm) generator' 'name': 'Instance (vm) generator'
} }

View File

@ -1,4 +1,4 @@
event_type S snapshot sync_mode D snapshot|init_snapshot
OS-DCF:diskConfig S AUTO OS-DCF:diskConfig S AUTO
OS-EXT-AZ:availability_zone S nova OS-EXT-AZ:availability_zone S nova
OS-EXT-SRV-ATTR:host D [a-z]{4}-devstack OS-EXT-SRV-ATTR:host D [a-z]{4}-devstack

View File

@ -1,4 +1,4 @@
event_type S update sync_mode S update
u-OS-DCF:diskConfig S AUTO u-OS-DCF:diskConfig S AUTO
u-OS-EXT-AZ:availability_zone S nova u-OS-EXT-AZ:availability_zone S nova
u-OS-EXT-SRV-ATTR:host D [a-z]{4}-devstack u-OS-EXT-SRV-ATTR:host D [a-z]{4}-devstack
@ -22,7 +22,7 @@ u-config_drive D True|False
u-created D \d{2}:\d{2}:\d{2} \d{2}:\d{2}:\d{2} u-created D \d{2}:\d{2}:\d{2} \d{2}:\d{2}:\d{2}
u-flavor D m1.nano u-flavor D m1.nano
u-hostId D [0-9a-f]{56} u-hostId D [0-9a-f]{56}
u-id S [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} instance_id S [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
u-image D cirros-[a-z]+ ([a-z0-9]+) u-image D cirros-[a-z]+ ([a-z0-9]+)
u-key_name D - u-key_name D -
u-metadata D \{\} u-metadata D \{\}

View File

@ -13,8 +13,16 @@
# under the License. # under the License.
from oslotest import base from oslotest import base
import sys
class BaseTest(base.BaseTestCase): class BaseTest(base.BaseTestCase):
"""Test case base class for all unit tests.""" """Test case base class for all unit tests."""
def assert_list_equal(self, l1, l2):
if tuple(sys.version_info)[0:2] < (2, 7):
# for python 2.6 compatibility
self.assertEqual(l1, l2)
else:
super(BaseTest, self).assertListEqual(l1, l2)

View File

@ -1,26 +0,0 @@
# Copyright 2015 - Alcatel-Lucent
#
# 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_log import log as logging
from vitrage.tests.unit import base
LOG = logging.getLogger(__name__)
class TransformNovaInstanceTest(base.BaseTest):
def test_transform_instance(self):
LOG.debug('Test transform Nova instance into entity wrapper')
self.assertTrue(True)

View File

@ -0,0 +1,83 @@
# Copyright 2015 - Alcatel-Lucent
#
# 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_log import log as logging
from vitrage.common.constants import VertexConstants as vertexCons
from vitrage.entity_graph.transformer import nova_transformer as nt
from vitrage.tests.mocks import mock_syncronizer as mock_sync
from vitrage.tests.unit import base
LOG = logging.getLogger(__name__)
def get_nova_instance_transformer():
return nt.InstanceTransformer()
def get_instance_entity_spec_list(config_file_path, number_of_instances):
"""Returns a list of nova instance specifications by
given specific configuration file.
:rtype : list
"""
return {
'filename': config_file_path,
'#instances': number_of_instances,
'name': 'Instance generator'
}
class NovaInstanceTransformerTest(base.BaseTest):
def test_key_fields(self):
LOG.debug('Test get key fields from nova instance transformer')
transformer = get_nova_instance_transformer()
expected_key_fields = [vertexCons.TYPE,
vertexCons.SUB_TYPE,
vertexCons.ID]
observed_key_fields = transformer.key_fields()
self.assert_list_equal(expected_key_fields, observed_key_fields)
def test_extract_key(self):
LOG.debug('Test get key from nova instance transformer')
transformer = get_nova_instance_transformer()
instance_specifications = [
get_instance_entity_spec_list('mock_nova_inst_snapshot.txt', 1),
get_instance_entity_spec_list('mock_nova_inst_update.txt', 1)
]
spec_list = mock_sync.get_mock_generators(instance_specifications)
instance_events = mock_sync.generate_random_events_list(spec_list)
for event in instance_events:
observed_key = transformer.extract_key(event)
observed_key_fields = observed_key.split(nt.KEY_SEPARATOR)
self.assertEqual(nt.ENTITY_TYPE, observed_key_fields[0])
self.assertEqual(nt.INSTANCE_SUB_TYPE, observed_key_fields[1])
event_id = event[transformer.ENTITY_ID_DICT[event['sync_mode']]]
self.assertEqual(event_id, observed_key_fields[2])
expected_key = nt.KEY_SEPARATOR.join(
[nt.ENTITY_TYPE,
nt.INSTANCE_SUB_TYPE,
event_id])
self.assertEqual(expected_key, observed_key)

View File

@ -0,0 +1 @@
__author__ = 'stack'