421 lines
18 KiB
Python
421 lines
18 KiB
Python
# 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_config import cfg
|
|
|
|
from vitrage.common.constants import DatasourceAction as DSAction
|
|
from vitrage.common.constants import DatasourceProperties as DSProps
|
|
from vitrage.common.constants import EdgeProperties as EProps
|
|
from vitrage.common.constants import GraphAction
|
|
from vitrage.common.constants import VertexProperties as VProps
|
|
from vitrage.datasources.transformer_base import Neighbor
|
|
from vitrage.entity_graph.mappings.operational_resource_state import \
|
|
OperationalResourceState
|
|
from vitrage.entity_graph.processor import processor_utils as PUtils
|
|
import vitrage.graph.utils as graph_utils
|
|
from vitrage.tests.unit.entity_graph.base import TestEntityGraphUnitBase
|
|
from vitrage.utils.datetime import utcnow
|
|
|
|
|
|
class TestProcessor(TestEntityGraphUnitBase):
|
|
|
|
ZONE_SPEC = 'ZONE_SPEC'
|
|
HOST_SPEC = 'HOST_SPEC'
|
|
INSTANCE_SPEC = 'INSTANCE_SPEC'
|
|
NUM_VERTICES_AFTER_CREATION = 2
|
|
NUM_EDGES_AFTER_CREATION = 1
|
|
NUM_VERTICES_AFTER_DELETION = 1
|
|
NUM_EDGES_AFTER_DELETION = 0
|
|
|
|
# noinspection PyAttributeOutsideInit,PyPep8Naming
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestProcessor, cls).setUpClass()
|
|
cls.conf = cfg.ConfigOpts()
|
|
cls.conf.register_opts(cls.PROCESSOR_OPTS, group='entity_graph')
|
|
cls.conf.register_opts(cls.DATASOURCES_OPTS, group='datasources')
|
|
cls.load_datasources(cls.conf)
|
|
|
|
def test_process_event(self):
|
|
# check create instance event
|
|
processor = self.create_processor_and_graph(self.conf, uuid=True)
|
|
event = self._create_event(spec_type=self.INSTANCE_SPEC,
|
|
datasource_action=DSAction.INIT_SNAPSHOT)
|
|
processor.process_event(event)
|
|
self._check_graph(processor, self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION)
|
|
|
|
# check update instance even
|
|
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)
|
|
|
|
# check delete instance event
|
|
event[DSProps.DATASOURCE_ACTION] = DSAction.UPDATE
|
|
event[DSProps.EVENT_TYPE] = 'compute.instance.delete.end'
|
|
processor.process_event(event)
|
|
self._check_graph(processor, self.NUM_VERTICES_AFTER_DELETION,
|
|
self.NUM_EDGES_AFTER_DELETION)
|
|
|
|
def test_create_entity_with_placeholder_neighbor(self):
|
|
# create instance event with host neighbor and check validity
|
|
self._create_and_check_entity()
|
|
|
|
def test_update_entity_state(self):
|
|
# create instance event with host neighbor and check validity
|
|
(vertex, neighbors, processor) =\
|
|
self._create_and_check_entity(status='STARTING')
|
|
|
|
# check added entity
|
|
vertex = processor.entity_graph.get_vertex(vertex.vertex_id)
|
|
self.assertEqual('STARTING', vertex.properties[VProps.STATE])
|
|
|
|
# update instance event with state running
|
|
vertex.properties[VProps.STATE] = 'RUNNING'
|
|
vertex.properties[VProps.VITRAGE_SAMPLE_TIMESTAMP] = str(utcnow())
|
|
processor.update_entity(vertex, neighbors)
|
|
|
|
# check state
|
|
self._check_graph(processor, self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION)
|
|
vertex = processor.entity_graph.get_vertex(vertex.vertex_id)
|
|
self.assertEqual('RUNNING', vertex.properties[VProps.STATE])
|
|
|
|
def test_change_parent(self):
|
|
# create instance event with host neighbor and check validity
|
|
(vertex, neighbors, processor) = self._create_and_check_entity()
|
|
|
|
# update instance event with state running
|
|
(neighbor_vertex, neighbor_edge) = neighbors[0]
|
|
old_neighbor_id = neighbor_vertex.vertex_id
|
|
neighbor_vertex.properties[VProps.ID] = 'newhost-2'
|
|
neighbor_vertex.vertex_id = 'RESOURCE_HOST_newhost-2'
|
|
neighbor_edge.source_id = 'RESOURCE_HOST_newhost-2'
|
|
processor.update_entity(vertex, neighbors)
|
|
|
|
# check state
|
|
self._check_graph(processor, self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION)
|
|
neighbor_vertex = \
|
|
processor.entity_graph.get_vertex(old_neighbor_id)
|
|
self.assertIsNone(neighbor_vertex)
|
|
|
|
def test_delete_entity(self):
|
|
# create instance event with host neighbor and check validity
|
|
(vertex, neighbors, processor) = self._create_and_check_entity()
|
|
|
|
# delete entity
|
|
processor.delete_entity(vertex, neighbors)
|
|
|
|
# check deleted entity
|
|
self._check_graph(processor, self.NUM_VERTICES_AFTER_DELETION,
|
|
self.NUM_EDGES_AFTER_DELETION)
|
|
self.assertTrue(PUtils.is_deleted(vertex))
|
|
|
|
def test_update_relationship(self):
|
|
# setup
|
|
vertex1, neighbors1, processor = self._create_entity(
|
|
spec_type=self.INSTANCE_SPEC,
|
|
datasource_action=DSAction.INIT_SNAPSHOT)
|
|
vertex2, neighbors2, processor = self._create_entity(
|
|
spec_type=self.INSTANCE_SPEC,
|
|
datasource_action=DSAction.INIT_SNAPSHOT,
|
|
processor=processor)
|
|
self.assertEqual(2, processor.entity_graph.num_edges())
|
|
|
|
new_edge = graph_utils.create_edge(vertex1.vertex_id,
|
|
vertex2.vertex_id,
|
|
'backup')
|
|
mock_neighbor = graph_utils.create_vertex(
|
|
"asdjashdkahsdashdalksjhd",
|
|
vitrage_category="RESOURCE",
|
|
vitrage_type="nova.instance",
|
|
entity_id="wtw64768476",
|
|
entity_state="AVAILABLE",
|
|
)
|
|
new_neighbors = [Neighbor(mock_neighbor, new_edge)]
|
|
|
|
# action
|
|
processor.update_relationship(vertex1, new_neighbors)
|
|
|
|
# test assertions
|
|
self.assertEqual(3, processor.entity_graph.num_edges())
|
|
|
|
def test_delete_relationship(self):
|
|
# setup
|
|
vertex1, neighbors1, processor = self._create_entity(
|
|
spec_type=self.INSTANCE_SPEC,
|
|
datasource_action=DSAction.INIT_SNAPSHOT)
|
|
vertex2, neighbors2, processor = self._create_entity(
|
|
spec_type=self.INSTANCE_SPEC,
|
|
datasource_action=DSAction.INIT_SNAPSHOT,
|
|
processor=processor)
|
|
self.assertEqual(2, processor.entity_graph.num_edges())
|
|
|
|
new_edge = graph_utils.create_edge(vertex1.vertex_id,
|
|
vertex2.vertex_id,
|
|
'backup')
|
|
processor.entity_graph.add_edge(new_edge)
|
|
self.assertEqual(3, processor.entity_graph.num_edges())
|
|
new_neighbors = [Neighbor(vertex1, new_edge)]
|
|
|
|
# action
|
|
processor.delete_relationship(vertex2, new_neighbors)
|
|
|
|
# test assertions
|
|
edge_from_graph = processor.entity_graph.get_edge(vertex1.vertex_id,
|
|
vertex2.vertex_id,
|
|
'backup')
|
|
self.assertEqual(3, processor.entity_graph.num_edges())
|
|
self.assertEqual(True, edge_from_graph[EProps.VITRAGE_IS_DELETED])
|
|
|
|
def test_remove_deleted_entity(self):
|
|
# setup
|
|
vertex, neighbors, processor = self._create_entity(
|
|
spec_type=self.INSTANCE_SPEC,
|
|
datasource_action=DSAction.INIT_SNAPSHOT)
|
|
self.assertEqual(1, processor.entity_graph.num_edges())
|
|
vertex[VProps.VITRAGE_IS_DELETED] = True
|
|
processor.entity_graph.update_vertex(vertex)
|
|
|
|
# action
|
|
processor.remove_deleted_entity(vertex, None)
|
|
|
|
# test assertions
|
|
self.assertEqual(0, processor.entity_graph.num_edges())
|
|
|
|
def test_update_neighbors(self):
|
|
# create instance event with host neighbor and check validity
|
|
(vertex, neighbors, processor) = self._create_and_check_entity()
|
|
|
|
# update instance event with state running
|
|
(neighbor_vertex, neighbor_edge) = neighbors[0]
|
|
old_neighbor_id = neighbor_vertex.vertex_id
|
|
neighbor_vertex.properties[VProps.ID] = 'newhost-2'
|
|
neighbor_vertex.vertex_id = 'RESOURCE_HOST_newhost-2'
|
|
neighbor_edge.source_id = 'RESOURCE_HOST_newhost-2'
|
|
processor._update_neighbors(vertex, neighbors)
|
|
|
|
# check state
|
|
self._check_graph(processor, self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION)
|
|
self.assertIsNone(processor.entity_graph.get_vertex(old_neighbor_id))
|
|
|
|
# update instance with the same neighbor
|
|
processor._update_neighbors(vertex, neighbors)
|
|
|
|
# check state
|
|
self._check_graph(processor, self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION)
|
|
|
|
def test_update_neighbors_by_changing_label(self):
|
|
# create instance event with host neighbor and check validity
|
|
(vertex, neighbors, processor) = self._create_and_check_entity()
|
|
|
|
# update instance event with state running
|
|
(neighbor_vertex, neighbor_edge) = neighbors[0]
|
|
old_label = neighbor_edge.label
|
|
neighbor_vertex[VProps.VITRAGE_IS_PLACEHOLDER] = False
|
|
processor.entity_graph.update_vertex(neighbor_vertex)
|
|
neighbor_vertex[VProps.VITRAGE_IS_PLACEHOLDER] = True
|
|
neighbor_edge.label = 'new label'
|
|
|
|
processor._update_neighbors(vertex, neighbors)
|
|
|
|
# check state
|
|
self._check_graph(processor,
|
|
self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION,
|
|
vertex_id=vertex.vertex_id)
|
|
old_edge = processor.entity_graph.get_edge(neighbor_vertex.vertex_id,
|
|
vertex.vertex_id,
|
|
old_label)
|
|
new_edge = processor.entity_graph.get_edge(neighbor_vertex.vertex_id,
|
|
vertex.vertex_id,
|
|
neighbor_edge.label)
|
|
self.assertIsNotNone(old_edge)
|
|
self.assertEqual(old_edge[EProps.VITRAGE_IS_DELETED], True)
|
|
self.assertIsNotNone(new_edge)
|
|
self.assertEqual(new_edge[EProps.VITRAGE_IS_DELETED], False)
|
|
|
|
# update instance with the same neighbor
|
|
processor._update_neighbors(vertex, neighbors)
|
|
|
|
# check state
|
|
self._check_graph(processor,
|
|
self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION,
|
|
vertex_id=vertex.vertex_id)
|
|
|
|
def test_update_neighbors_by_changing_label_with_placeholder(self):
|
|
# create instance event with host neighbor and check validity
|
|
(vertex, neighbors, processor) = self._create_and_check_entity()
|
|
|
|
# update instance event with state running
|
|
(neighbor_vertex, neighbor_edge) = neighbors[0]
|
|
old_label = neighbor_edge.label
|
|
neighbor_edge.label = 'new label'
|
|
|
|
processor._update_neighbors(vertex, neighbors)
|
|
|
|
# check state
|
|
self._check_graph(processor,
|
|
self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION,
|
|
vertex_id=vertex.vertex_id)
|
|
old_edge = processor.entity_graph.get_edge(neighbor_vertex.vertex_id,
|
|
vertex.vertex_id,
|
|
old_label)
|
|
new_edge = processor.entity_graph.get_edge(neighbor_vertex.vertex_id,
|
|
vertex.vertex_id,
|
|
neighbor_edge.label)
|
|
self.assertIsNone(old_edge)
|
|
self.assertIsNotNone(new_edge)
|
|
self.assertEqual(new_edge[EProps.VITRAGE_IS_DELETED], False)
|
|
|
|
# update instance with the same neighbor
|
|
processor._update_neighbors(vertex, neighbors)
|
|
|
|
# check state
|
|
self._check_graph(processor,
|
|
self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION,
|
|
vertex_id=vertex.vertex_id)
|
|
|
|
def test_delete_old_connections(self):
|
|
# create instance event with host neighbor and check validity
|
|
(vertex, neighbors, processor) = self._create_and_check_entity()
|
|
|
|
# delete entity
|
|
processor._delete_old_connections(vertex, [neighbors[0][1]])
|
|
|
|
# check deleted entity
|
|
self._check_graph(processor,
|
|
self.NUM_VERTICES_AFTER_DELETION,
|
|
self.NUM_EDGES_AFTER_DELETION)
|
|
|
|
def test_calculate_vitrage_aggregated_state(self):
|
|
# setup
|
|
instances = []
|
|
for i in range(6):
|
|
(vertex, neighbors, processor) = self._create_and_check_entity()
|
|
instances.append((vertex, processor))
|
|
|
|
# action
|
|
# state already exists and its updated
|
|
instances[0][0][VProps.STATE] = 'SUSPENDED'
|
|
instances[0][1]._calculate_vitrage_aggregated_state(
|
|
instances[0][0], GraphAction.UPDATE_ENTITY)
|
|
|
|
# vitrage state doesn't exist and its updated
|
|
instances[1][0][VProps.STATE] = None
|
|
instances[1][1].entity_graph.update_vertex(instances[1][0])
|
|
instances[1][0][VProps.VITRAGE_STATE] = \
|
|
OperationalResourceState.SUBOPTIMAL
|
|
instances[1][1]._calculate_vitrage_aggregated_state(
|
|
instances[1][0], GraphAction.UPDATE_ENTITY)
|
|
|
|
# state exists and vitrage state changes
|
|
instances[2][0][VProps.VITRAGE_STATE] = \
|
|
OperationalResourceState.SUBOPTIMAL
|
|
instances[2][1]._calculate_vitrage_aggregated_state(
|
|
instances[2][0], GraphAction.UPDATE_ENTITY)
|
|
|
|
# vitrage state exists and state changes
|
|
instances[3][0][VProps.STATE] = None
|
|
instances[3][0][VProps.VITRAGE_STATE] = \
|
|
OperationalResourceState.SUBOPTIMAL
|
|
instances[3][1].entity_graph.update_vertex(instances[3][0])
|
|
instances[3][0][VProps.STATE] = 'SUSPENDED'
|
|
instances[3][1]._calculate_vitrage_aggregated_state(
|
|
instances[3][0], GraphAction.UPDATE_ENTITY)
|
|
|
|
# state and vitrage state exists and state changes
|
|
instances[4][0][VProps.VITRAGE_STATE] = \
|
|
OperationalResourceState.SUBOPTIMAL
|
|
instances[4][1].entity_graph.update_vertex(instances[4][0])
|
|
instances[4][0][VProps.STATE] = 'SUSPENDED'
|
|
instances[4][1]._calculate_vitrage_aggregated_state(
|
|
instances[4][0], GraphAction.UPDATE_ENTITY)
|
|
|
|
# state and vitrage state exists and vitrage state changes
|
|
instances[5][0][VProps.VITRAGE_STATE] = \
|
|
OperationalResourceState.SUBOPTIMAL
|
|
instances[5][1].entity_graph.update_vertex(instances[5][0])
|
|
instances[5][1]._calculate_vitrage_aggregated_state(
|
|
instances[5][0], GraphAction.UPDATE_ENTITY)
|
|
|
|
# test assertions
|
|
self.assertEqual('SUSPENDED',
|
|
instances[0][0][VProps.VITRAGE_AGGREGATED_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[0][0][VProps.VITRAGE_OPERATIONAL_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[1][0][VProps.VITRAGE_AGGREGATED_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[1][0][VProps.VITRAGE_OPERATIONAL_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[2][0][VProps.VITRAGE_AGGREGATED_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[2][0][VProps.VITRAGE_OPERATIONAL_STATE])
|
|
self.assertEqual('SUSPENDED',
|
|
instances[3][0][VProps.VITRAGE_AGGREGATED_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[3][0][VProps.VITRAGE_OPERATIONAL_STATE])
|
|
self.assertEqual('SUSPENDED',
|
|
instances[4][0][VProps.VITRAGE_AGGREGATED_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[4][0][VProps.VITRAGE_OPERATIONAL_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[5][0][VProps.VITRAGE_AGGREGATED_STATE])
|
|
self.assertEqual(OperationalResourceState.SUBOPTIMAL,
|
|
instances[5][0][VProps.VITRAGE_OPERATIONAL_STATE])
|
|
|
|
def _create_and_check_entity(self, processor=None, **kwargs):
|
|
# create instance event with host neighbor
|
|
(vertex, neighbors, processor) = self._create_entity(
|
|
spec_type=self.INSTANCE_SPEC,
|
|
datasource_action=DSAction.INIT_SNAPSHOT,
|
|
properties=kwargs,
|
|
processor=processor)
|
|
|
|
# check the data in the graph is correct
|
|
self._check_graph(processor,
|
|
self.NUM_VERTICES_AFTER_CREATION,
|
|
self.NUM_EDGES_AFTER_CREATION)
|
|
|
|
return vertex, neighbors, processor
|
|
|
|
def _check_graph(self,
|
|
processor,
|
|
num_vertices,
|
|
num_edges,
|
|
vertex_id=None):
|
|
self.assertEqual(num_vertices, len(processor.entity_graph))
|
|
|
|
if not vertex_id:
|
|
self.assertEqual(num_edges, processor.entity_graph.num_edges())
|
|
else:
|
|
deleted_edges = processor.entity_graph.get_edges(
|
|
vertex_id,
|
|
attr_filter={VProps.VITRAGE_IS_DELETED: True})
|
|
self.assertEqual(num_edges + len(deleted_edges),
|
|
processor.entity_graph.num_edges())
|