diff --git a/etc/vitrage/datasources_values/openstack.node.yaml b/etc/vitrage/datasources_values/openstack.cluster.yaml similarity index 100% rename from etc/vitrage/datasources_values/openstack.node.yaml rename to etc/vitrage/datasources_values/openstack.cluster.yaml diff --git a/vitrage/datasources/aodh/__init__.py b/vitrage/datasources/aodh/__init__.py index c5029ceb1..812e970c8 100644 --- a/vitrage/datasources/aodh/__init__.py +++ b/vitrage/datasources/aodh/__init__.py @@ -27,8 +27,8 @@ OPTS = [ help='Aodh driver class path', required=True), cfg.IntOpt('changes_interval', - default=30, - min=30, + default=20, + min=20, help='interval between checking changes in aodh data source', required=True), ] diff --git a/vitrage/datasources/cinder/volume/driver.py b/vitrage/datasources/cinder/volume/driver.py index 3f0325a7b..bc1b58582 100644 --- a/vitrage/datasources/cinder/volume/driver.py +++ b/vitrage/datasources/cinder/volume/driver.py @@ -50,7 +50,14 @@ class CinderVolumeDriver(DriverBase): @staticmethod def get_event_types(conf): - return ['volume.'] + return ['volume.create.start', + 'volume.create.end', + 'volume.attach.start', + 'volume.attach.end', + 'volume.detach.start', + 'volume.detach.end', + 'volume.delete.start', + 'volume.delete.end'] @staticmethod def get_topic(conf): diff --git a/vitrage/datasources/nova/instance/driver.py b/vitrage/datasources/nova/instance/driver.py index 91176a40b..6cbef7280 100644 --- a/vitrage/datasources/nova/instance/driver.py +++ b/vitrage/datasources/nova/instance/driver.py @@ -46,8 +46,11 @@ class InstanceDriver(NovaDriverBase): @staticmethod def get_event_types(conf): # Add event_types to receive notifications about - return ['compute.instance.create', - 'compute.instance.delete', + return ['compute.instance.create.start', + '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', diff --git a/vitrage/datasources/static_physical/__init__.py b/vitrage/datasources/static_physical/__init__.py index 236e61943..c814d6c6a 100644 --- a/vitrage/datasources/static_physical/__init__.py +++ b/vitrage/datasources/static_physical/__init__.py @@ -29,8 +29,8 @@ OPTS = [ help='Static physical driver class path', required=True), cfg.IntOpt('changes_interval', - default=5, - min=5, + default=20, + min=20, help='interval between checking changes in the configuration ' 'files of the physical topology data sources', required=True), diff --git a/vitrage/entity_graph/processor/processor.py b/vitrage/entity_graph/processor/processor.py index 03e2aeec6..4228b3243 100644 --- a/vitrage/entity_graph/processor/processor.py +++ b/vitrage/entity_graph/processor/processor.py @@ -276,20 +276,23 @@ class Processor(processor.ProcessorBase): def _calculate_aggregated_state(self, vertex, action): LOG.debug("calculate event state") - if action in [EventAction.UPDATE_ENTITY, - EventAction.DELETE_ENTITY, - EventAction.CREATE_ENTITY]: - graph_vertex = self.entity_graph.get_vertex(vertex.vertex_id) - elif action in [EventAction.END_MESSAGE, - EventAction.UPDATE_RELATIONSHIP, - EventAction.DELETE_RELATIONSHIP]: - return None - else: - LOG.error('unrecognized action: %s for vertex: %s', - action, vertex) - return None + try: + if action in [EventAction.UPDATE_ENTITY, + EventAction.DELETE_ENTITY, + EventAction.CREATE_ENTITY]: + graph_vertex = self.entity_graph.get_vertex(vertex.vertex_id) + elif action in [EventAction.END_MESSAGE, + EventAction.UPDATE_RELATIONSHIP, + EventAction.DELETE_RELATIONSHIP]: + return None + else: + LOG.error('unrecognized action: %s for vertex: %s', + action, vertex) + return None - self.state_manager.aggregated_state(vertex, graph_vertex) + self.state_manager.aggregated_state(vertex, graph_vertex) + except Exception as e: + LOG.exception("Calculate aggregated state failed - %s", e) def _enrich_event(self, event): attr = self.transformer_manager.get_enrich_query(event) diff --git a/vitrage_tempest_tests/tests/api/alarms/utils.py b/vitrage_tempest_tests/tests/api/alarms/utils.py index ca24a5836..2b8ead5cd 100644 --- a/vitrage_tempest_tests/tests/api/alarms/utils.py +++ b/vitrage_tempest_tests/tests/api/alarms/utils.py @@ -41,8 +41,7 @@ class AlarmsHelper(BaseVitrageTest): def get_all_alarms(self): """Get Alarms returned by the cli """ - return utils.run_vitrage_command_with_user( - 'vitrage alarms list', self.conf.service_credentials.user) + return utils.run_vitrage_command_as_admin('vitrage alarms list') @staticmethod def filter_alarms(alarms_list, component): diff --git a/vitrage_tempest_tests/tests/api/base.py b/vitrage_tempest_tests/tests/api/base.py index 0a3c1db74..ec1f1fd20 100644 --- a/vitrage_tempest_tests/tests/api/base.py +++ b/vitrage_tempest_tests/tests/api/base.py @@ -11,30 +11,251 @@ # 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 time + from oslo_log import log as logging from oslotest import base -from vitrage_tempest_tests.tests.base_mock import BaseMock +from vitrage import clients +from vitrage.common.constants import EntityCategory +from vitrage.common.constants import VertexProperties as VProps +from vitrage.datasources import AODH_DATASOURCE +from vitrage.datasources import CINDER_VOLUME_DATASOURCE +from vitrage.datasources import NOVA_HOST_DATASOURCE +from vitrage.datasources import NOVA_INSTANCE_DATASOURCE +from vitrage.datasources import NOVA_ZONE_DATASOURCE +from vitrage.datasources import OPENSTACK_CLUSTER +from vitrage.datasources.static_physical import SWITCH +from vitrage.graph import Edge +from vitrage.graph import NXGraph +from vitrage.graph import Vertex +from vitrage import keystone_client +from vitrage import service +from vitrage_tempest_tests.tests import OPTS import vitrage_tempest_tests.tests.utils as utils +from vitrageclient import client as v_client LOG = logging.getLogger(__name__) -class BaseVitrageTest(base.BaseTestCase): +class BaseApiTest(base.BaseTestCase): """Base test class for Vitrage API tests.""" + NUM_VERTICES_PER_TYPE = 'num_vertices' + NUM_EDGES_PER_TYPE = 'num_edges_per_type' + @classmethod def setUpClass(cls): - super(BaseVitrageTest, cls).setUpClass() - cls.conf = utils.get_conf() + super(BaseApiTest, cls).setUpClass() + cls.conf = service.prepare_service([]) + cls.conf.register_opts(list(OPTS), group='keystone_authtoken') + cls.vitrage_client = \ + v_client.Client('1', session=keystone_client.get_session(cls.conf)) + cls.nova_client = clients.nova_client(cls.conf) + cls.cinder_client = clients.cinder_client(cls.conf) - def _create_graph_by_mock(self): - """Create MOCK Graph and copied to the string """ - self.mock_client = BaseMock() - processor = self.mock_client.create_processor_with_graph() - entity_graph = processor.entity_graph - mock_graph_output = entity_graph.output_graph() - LOG.info("The mock graph is : " + mock_graph_output) + def _create_volume_and_attach(self, name, size, instance_id, mount_point): + volume = self.cinder_client.volumes.create(display_name=name, + size=size) + time.sleep(3) + self.cinder_client.volumes.attach(volume=volume, + instance_uuid=instance_id, + mountpoint=mount_point) + + self._wait_for_status(20, + self._check_num_volumes, + num_volumes=1) + + time.sleep(2) + + return volume + + def _create_instances(self, num_instances): + flavors_list = self.nova_client.flavors.list() + images_list = self.nova_client.images.list() + + resources = [self.nova_client.servers.create( + name='%s-%s' % ('vm', index), + flavor=flavors_list[0], + image=images_list[0]) for index in range(num_instances)] + + self._wait_for_status(20, + self._check_num_instances, + num_instances=num_instances) + time.sleep(2) + + return resources + + def _delete_instances(self): + instances = self.nova_client.servers.list() + for instance in instances: + try: + self.nova_client.servers.delete(instance) + except Exception: + pass + + self._wait_for_status(20, + self._check_num_instances, + num_instances=0) + + time.sleep(2) + + def _delete_volumes(self): + volumes = self.cinder_client.volumes.list() + for volume in volumes: + try: + self.cinder_client.volumes.detach(volume) + self.cinder_client.volumes.force_delete(volume) + except Exception: + self.cinder_client.volumes.force_delete(volume) + + self._wait_for_status(30, + self._check_num_volumes, + num_volumes=0) + + time.sleep(2) + + def _check_num_instances(self, num_instances=0): + return len(self.nova_client.servers.list()) == num_instances + + def _check_num_volumes(self, num_volumes=0): + return len(self.cinder_client.volumes.list()) == num_volumes + + @staticmethod + def _create_graph_from_graph_dictionary(api_graph): + graph = NXGraph() + + nodes = api_graph['nodes'] + for i in xrange(len(nodes)): + graph.add_vertex(Vertex(str(i), nodes[i])) + + edges = api_graph['links'] + for i in xrange(len(edges)): + graph.add_edge(Edge(str(edges[i]['source']), + str(edges[i]['target']), + edges[i]['relationship_type'])) + + return graph + + def _create_graph_from_tree_dictionary(self, + api_graph, + graph=None, + ancestor=None): + children = [] + graph = NXGraph() if not graph else graph + + if 'children' in api_graph: + children = api_graph.copy()['children'] + del api_graph['children'] + + vertex = Vertex(api_graph[VProps.VITRAGE_ID], api_graph) + graph.add_vertex(vertex) + if ancestor: + graph.add_edge(Edge(ancestor[VProps.VITRAGE_ID], + vertex[VProps.VITRAGE_ID], + 'label')) + + for entity in children: + self._create_graph_from_tree_dictionary(entity, graph, vertex) + + return graph + + @staticmethod + def _wait_for_status(max_waiting, func, **kwargs): + count = 0 + while count < max_waiting: + if func(**kwargs): + return True + count += 1 + time.sleep(2) + LOG.info("wait_for_status - False ") + return False + + def _entities_validation_data(self, **kwargs): + validation_data = [] + + # openstack.cluster + props = {VProps.CATEGORY: EntityCategory.RESOURCE, + VProps.TYPE: OPENSTACK_CLUSTER, + self.NUM_VERTICES_PER_TYPE: kwargs.get('cluster_entities', 1), + self.NUM_EDGES_PER_TYPE: kwargs.get('cluster_edges', 1)} + validation_data.append(props) + + # nova.zone + props = {VProps.CATEGORY: EntityCategory.RESOURCE, + VProps.TYPE: NOVA_ZONE_DATASOURCE, + self.NUM_VERTICES_PER_TYPE: kwargs.get('zone_entities', 1), + self.NUM_EDGES_PER_TYPE: kwargs.get('zone_edges', 2)} + validation_data.append(props) + + # nova.host + props = {VProps.CATEGORY: EntityCategory.RESOURCE, + VProps.TYPE: NOVA_HOST_DATASOURCE, + self.NUM_VERTICES_PER_TYPE: kwargs.get('host_entities', 1), + self.NUM_EDGES_PER_TYPE: kwargs.get('host_edges', 1)} + validation_data.append(props) + + # nova.instance + props = {VProps.CATEGORY: EntityCategory.RESOURCE, + VProps.TYPE: NOVA_INSTANCE_DATASOURCE, + self.NUM_VERTICES_PER_TYPE: kwargs.get('instance_entities', + 0), + self.NUM_EDGES_PER_TYPE: kwargs.get('instance_edges', 0)} + validation_data.append(props) + + # cinder.volume + props = {VProps.CATEGORY: EntityCategory.RESOURCE, + VProps.TYPE: CINDER_VOLUME_DATASOURCE, + self.NUM_VERTICES_PER_TYPE: kwargs.get('volume_entities', 0), + self.NUM_EDGES_PER_TYPE: kwargs.get('volume_edges', 0)} + validation_data.append(props) + + # switch + props = {VProps.CATEGORY: EntityCategory.RESOURCE, + VProps.TYPE: SWITCH, + self.NUM_VERTICES_PER_TYPE: kwargs.get('switch_entities', 0), + self.NUM_EDGES_PER_TYPE: kwargs.get('switch_edges', 0)} + validation_data.append(props) + + # aodh + props = {VProps.CATEGORY: EntityCategory.ALARM, + VProps.TYPE: AODH_DATASOURCE, + self.NUM_VERTICES_PER_TYPE: kwargs.get('aodh_entities', 0), + self.NUM_EDGES_PER_TYPE: kwargs.get('aodh_edges', 0)} + validation_data.append(props) + + return validation_data + + def _validate_graph_correctness(self, + graph, + num_entities, + num_edges, + entities): + self.assertIsNot(None, graph) + self.assertIsNot(None, entities) + self.assertEqual(num_entities, graph.num_vertices()) + self.assertEqual(num_edges, graph.num_edges()) + + for entity in entities: + query = { + VProps.CATEGORY: entity[VProps.CATEGORY], + VProps.TYPE: entity[VProps.TYPE], + VProps.IS_DELETED: False, + VProps.IS_PLACEHOLDER: False + } + vertices = graph.get_vertices(vertex_attr_filter=query) + self.assertEqual(entity[self.NUM_VERTICES_PER_TYPE], + len(vertices), + '%s%s' % ('Num vertices is incorrect for: %s', + entity[VProps.TYPE])) + + num_edges = sum([len(graph.get_edges(vertex.vertex_id)) + for vertex in vertices]) + self.assertEqual(entity[self.NUM_EDGES_PER_TYPE], + num_edges, + '%s%s' % ('Num edges is incorrect for: %s', + entity[VProps.TYPE])) @staticmethod def get_flavor_id_from_list(): diff --git a/vitrage_tempest_tests/tests/api/datasources/test_aodh.py b/vitrage_tempest_tests/tests/api/datasources/test_aodh.py index 211da1fb8..24010d7d5 100644 --- a/vitrage_tempest_tests/tests/api/datasources/test_aodh.py +++ b/vitrage_tempest_tests/tests/api/datasources/test_aodh.py @@ -12,29 +12,93 @@ # License for the specific language governing permissions and limitations # under the License. +import random +import time + from oslo_log import log as logging -from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest +from vitrage import clients +from vitrage_tempest_tests.tests.api.base import BaseApiTest LOG = logging.getLogger(__name__) -class TestAodhAlarm(BaseTopologyTest): +class TestAodhAlarm(BaseApiTest): @classmethod def setUpClass(cls): super(TestAodhAlarm, cls).setUpClass() + cls.ceilometer_client = clients.ceilometer_client(cls.conf) - def test_alarm(self): + def test_alarm_with_resource_id(self): try: # create entities - self._create_switches() + self._create_instances(num_instances=1) + self._create_ceilometer_alarm(self._find_instance_resource_id()) api_graph = self.vitrage_client.topology.get() graph = self._create_graph_from_graph_dictionary(api_graph) entities = self._entities_validation_data( - host_entities=1, host_edges=4, - instance_entities=3, instance_edges=4, - volume_entities=1, volume_edges=1) - self._validate_graph_correctness(graph, 7, 6, entities) + host_entities=1, host_edges=2, + instance_entities=1, instance_edges=2, + aodh_entities=1, aodh_edges=1) + self._validate_graph_correctness(graph, 5, 4, entities) finally: - self._rollback_to_default() + self._delete_ceilometer_alarms() + self._delete_instances() + + def test_alarm_without_resource_id(self): + try: + # create entities + self._create_ceilometer_alarm() + api_graph = self.vitrage_client.topology.get() + graph = self._create_graph_from_graph_dictionary(api_graph) + entities = self._entities_validation_data( + host_entities=1, host_edges=1, + aodh_entities=1, aodh_edges=0) + self._validate_graph_correctness(graph, 4, 2, entities) + finally: + self._delete_ceilometer_alarms() + + def _create_ceilometer_alarm(self, resource_id=None): + aodh_request = self._aodh_request(resource_id=resource_id) + self.ceilometer_client.alarms.create(**aodh_request) + self._wait_for_status(20, + self._check_num_alarms, + num_alarms=1) + time.sleep(25) + + def _delete_ceilometer_alarms(self): + alarms = self.ceilometer_client.alarms.list() + for alarm in alarms: + self.ceilometer_client.alarms.delete(alarm.alarm_id) + self._wait_for_status(20, + self._check_num_alarms, + num_alarms=0) + time.sleep(25) + + def _check_num_alarms(self, num_alarms=0): + return len(self.ceilometer_client.alarms.list()) == num_alarms + + def _aodh_request(self, resource_id=None): + query = [] + if resource_id: + query = [ + dict( + field=u'resource_id', + type='', + op=u'eq', + value=resource_id) + ] + + random_name = '%s-%s' % ('test', random.randrange(0, 100000, 1)) + return dict( + name=random_name, + description=u'test alarm', + event_rule=dict(query=query), + severity='low', + state='alarm', # ok/alarm/insufficient data + type=u'event') + + def _find_instance_resource_id(self): + servers = self.nova_client.servers.list() + return servers[0].id diff --git a/vitrage_tempest_tests/tests/api/datasources/test_cinder_volume.py b/vitrage_tempest_tests/tests/api/datasources/test_cinder_volume.py index 89c4fddeb..576038af0 100644 --- a/vitrage_tempest_tests/tests/api/datasources/test_cinder_volume.py +++ b/vitrage_tempest_tests/tests/api/datasources/test_cinder_volume.py @@ -13,7 +13,6 @@ # under the License. from oslo_log import log as logging - from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest LOG = logging.getLogger(__name__) diff --git a/vitrage_tempest_tests/tests/api/datasources/test_nova.py b/vitrage_tempest_tests/tests/api/datasources/test_nova.py index bf4d3fb9c..372157982 100644 --- a/vitrage_tempest_tests/tests/api/datasources/test_nova.py +++ b/vitrage_tempest_tests/tests/api/datasources/test_nova.py @@ -13,7 +13,6 @@ # under the License. from oslo_log import log as logging - from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest LOG = logging.getLogger(__name__) diff --git a/vitrage_tempest_tests/tests/api/datasources/test_static_physical.py b/vitrage_tempest_tests/tests/api/datasources/test_static_physical.py index 0f6744f3b..632b6430d 100644 --- a/vitrage_tempest_tests/tests/api/datasources/test_static_physical.py +++ b/vitrage_tempest_tests/tests/api/datasources/test_static_physical.py @@ -12,18 +12,18 @@ # License for the specific language governing permissions and limitations # under the License. +import os import socket import time from oslo_log import log as logging - -from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest +from vitrage_tempest_tests.tests.api.base import BaseApiTest LOG = logging.getLogger(__name__) -class TestStaticPhysical(BaseTopologyTest): +class TestStaticPhysical(BaseApiTest): @classmethod def setUpClass(cls): @@ -40,7 +40,7 @@ class TestStaticPhysical(BaseTopologyTest): switch_entities=2, switch_edges=2) self._validate_graph_correctness(graph, 5, 4, entities) finally: - self._rollback_to_default() + self._delete_switches() @staticmethod def _create_switches(): @@ -59,4 +59,12 @@ class TestStaticPhysical(BaseTopologyTest): new_file.write(template_data) new_file.close() - time.sleep(7) + time.sleep(25) + + @staticmethod + def _delete_switches(): + path = '/etc/vitrage/static_datasources/tempest_configuration.yaml' + if os.path.exists(path): + os.remove(path) + + time.sleep(25) diff --git a/vitrage_tempest_tests/tests/api/topology/base.py b/vitrage_tempest_tests/tests/api/topology/base.py index 056537319..00d162594 100644 --- a/vitrage_tempest_tests/tests/api/topology/base.py +++ b/vitrage_tempest_tests/tests/api/topology/base.py @@ -13,51 +13,25 @@ # under the License. import json -import os -import os.path import time from oslo_log import log as logging -from tempest import test -from vitrage import clients -from vitrage.common.constants import EntityCategory from vitrage.common.constants import VertexProperties as VProps -from vitrage.datasources import CINDER_VOLUME_DATASOURCE -from vitrage.datasources import NOVA_HOST_DATASOURCE -from vitrage.datasources import NOVA_INSTANCE_DATASOURCE -from vitrage.datasources import NOVA_ZONE_DATASOURCE -from vitrage.datasources import OPENSTACK_CLUSTER -from vitrage.datasources.static_physical import SWITCH -from vitrage.graph import Edge -from vitrage.graph import NXGraph -from vitrage.graph import Vertex -from vitrage import keystone_client -from vitrage import service -from vitrage_tempest_tests.tests import OPTS -from vitrageclient import client as v_client +from vitrage_tempest_tests.tests.api.base import BaseApiTest LOG = logging.getLogger(__name__) -class BaseTopologyTest(test.BaseTestCase): +class BaseTopologyTest(BaseApiTest): """Topology test class for Vitrage API tests.""" - NUM_ENTITIES_PER_TYPE = 'num_vertices' - NUM_EDGES_PER_TYPE = 'num_edges_per_type' - @classmethod def setUpClass(cls): super(BaseTopologyTest, cls).setUpClass() - cls.conf = service.prepare_service([]) - cls.conf.register_opts(list(OPTS), group='keystone_authtoken') - cls.vitrage_client = \ - v_client.Client('1', session=keystone_client.get_session(cls.conf)) - cls.nova_client = clients.nova_client(cls.conf) - cls.cinder_client = clients.cinder_client(cls.conf) def _rollback_to_default(self): - self._delete_entities(instance=True, volume=True, static_physical=True) + self._delete_entities() api_graph = self.vitrage_client.topology.get() graph = self._create_graph_from_graph_dictionary(api_graph) entities = self._entities_validation_data() @@ -66,157 +40,23 @@ class BaseTopologyTest(test.BaseTestCase): def _create_entities(self, num_instances=0, num_volumes=0, end_sleep=3): if num_instances > 0: resources = self._create_instances(num_instances) - self._wait_for_status(20, - self._check_num_instances, - num_instances=num_instances) - time.sleep(1) if num_volumes > 0: self._create_volume_and_attach('volume-1', 1, resources[0].__dict__['id'], '/tmp/vda') - self._wait_for_status(20, - self._check_num_volumes, - num_volumes=1) - # waiting until all the entities creation were processed by the # entity graph processor time.sleep(end_sleep) - def _delete_entities(self, - instance=False, - volume=False, - static_physical=False): - if volume: - self._delete_volumes() - self._wait_for_status(30, - self._check_num_volumes, - num_volumes=0) - - if instance: - self._delete_instances() - self._wait_for_status(20, - self._check_num_instances, - num_instances=0) - - if static_physical: - self._delete_switches() - time.sleep(2) + def _delete_entities(self): + self._delete_volumes() + self._delete_instances() # waiting until all the entities deletion were processed by the # entity graph processor time.sleep(5) - def _delete_switches(self): - path = '/etc/vitrage/static_datasources/tempest_configuration.yaml' - if os.path.exists(path): - os.remove(path) - - def _entities_validation_data(self, cluster_entities=1, cluster_edges=1, - zone_entities=1, zone_edges=2, - host_entities=1, host_edges=1, - instance_entities=0, instance_edges=0, - volume_entities=0, volume_edges=0, - switch_entities=0, switch_edges=0): - return [ - {VProps.TYPE: OPENSTACK_CLUSTER, - self.NUM_ENTITIES_PER_TYPE: cluster_entities, - self.NUM_EDGES_PER_TYPE: cluster_edges}, - {VProps.TYPE: NOVA_ZONE_DATASOURCE, - self.NUM_ENTITIES_PER_TYPE: zone_entities, - self.NUM_EDGES_PER_TYPE: zone_edges}, - {VProps.TYPE: NOVA_HOST_DATASOURCE, - self.NUM_ENTITIES_PER_TYPE: host_entities, - self.NUM_EDGES_PER_TYPE: host_edges}, - {VProps.TYPE: NOVA_INSTANCE_DATASOURCE, - self.NUM_ENTITIES_PER_TYPE: instance_entities, - self.NUM_EDGES_PER_TYPE: instance_edges}, - {VProps.TYPE: CINDER_VOLUME_DATASOURCE, - self.NUM_ENTITIES_PER_TYPE: volume_entities, - self.NUM_EDGES_PER_TYPE: volume_edges}, - {VProps.TYPE: SWITCH, - self.NUM_ENTITIES_PER_TYPE: switch_entities, - self.NUM_EDGES_PER_TYPE: switch_edges} - ] - - def _validate_graph_correctness(self, - graph, - num_entities, - num_edges, - entities): - self.assertIsNot(None, graph) - self.assertIsNot(None, entities) - self.assertEqual(num_entities, graph.num_vertices()) - self.assertEqual(num_edges, graph.num_edges()) - - for entity in entities: - query = { - VProps.CATEGORY: EntityCategory.RESOURCE, - VProps.TYPE: entity[VProps.TYPE], - VProps.IS_DELETED: False, - VProps.IS_PLACEHOLDER: False - } - vertices = graph.get_vertices(vertex_attr_filter=query) - self.assertEqual(entity[self.NUM_ENTITIES_PER_TYPE], len(vertices)) - - num_edges = sum([len(graph.get_edges(vertex.vertex_id)) - for vertex in vertices]) - self.assertEqual(entity[self.NUM_EDGES_PER_TYPE], num_edges) - - def _check_num_instances(self, num_instances=0): - return len(self.nova_client.servers.list()) == num_instances - - def _check_num_volumes(self, num_volumes=0): - return len(self.cinder_client.volumes.list()) == num_volumes - - def _create_volume_and_attach(self, name, size, instance_id, mount_point): - volume = self.cinder_client.volumes.create(display_name=name, - size=size) - time.sleep(3) - self.cinder_client.volumes.attach(volume=volume, - instance_uuid=instance_id, - mountpoint=mount_point) - return volume - - def _create_instances(self, num_machines): - flavors_list = self.nova_client.flavors.list() - images_list = self.nova_client.images.list() - - resources = [self.nova_client.servers.create( - name='%s-%s' % ('vm', index), - flavor=flavors_list[0], - image=images_list[0]) for index in range(num_machines)] - - return resources - - def _delete_instances(self): - instances = self.nova_client.servers.list() - for instance in instances: - try: - self.nova_client.servers.delete(instance) - except Exception: - pass - - def _delete_volumes(self): - volumes = self.cinder_client.volumes.list() - for volume in volumes: - try: - self.cinder_client.volumes.detach(volume) - self.cinder_client.volumes.force_delete(volume) - except Exception: - self.cinder_client.volumes.force_delete(volume) - - @staticmethod - def _wait_for_status(max_waiting, func, **kwargs): - count = 0 - while count < max_waiting: - if func(**kwargs): - return True - count += 1 - time.sleep(2) - LOG.info("wait_for_status - False ") - return False - @staticmethod def _compare_graphs(api_graph, cli_graph): """Compare Graph object to graph form terminal """ @@ -240,45 +80,6 @@ class BaseTopologyTest(test.BaseTestCase): return sorted_cli_graph == sorted_api_graph - @staticmethod - def _create_graph_from_graph_dictionary(api_graph): - graph = NXGraph() - - nodes = api_graph['nodes'] - for i in xrange(len(nodes)): - graph.add_vertex(Vertex(str(i), nodes[i])) - - edges = api_graph['links'] - for i in xrange(len(edges)): - graph.add_edge(Edge(str(edges[i]['source']), - str(edges[i]['target']), - edges[i]['relationship_type'])) - - return graph - - def _create_graph_from_tree_dictionary(self, - api_graph, - graph=None, - ancestor=None): - children = [] - graph = NXGraph() if not graph else graph - - if 'children' in api_graph: - children = api_graph.copy()['children'] - del api_graph['children'] - - vertex = Vertex(api_graph[VProps.VITRAGE_ID], api_graph) - graph.add_vertex(vertex) - if ancestor: - graph.add_edge(Edge(ancestor[VProps.VITRAGE_ID], - vertex[VProps.VITRAGE_ID], - 'label')) - - for entity in children: - self._create_graph_from_tree_dictionary(entity, graph, vertex) - - return graph - @staticmethod def _graph_query(): return '{"and": [{"==": {"category": "RESOURCE"}},' \ diff --git a/vitrage_tempest_tests/tests/api/topology/test_topology.py b/vitrage_tempest_tests/tests/api/topology/test_topology.py index d4f12ab83..20865d52a 100644 --- a/vitrage_tempest_tests/tests/api/topology/test_topology.py +++ b/vitrage_tempest_tests/tests/api/topology/test_topology.py @@ -12,21 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. -import json -import time - from oslo_log import log as logging -from vitrage.common.constants import EntityCategory -from vitrage.common.constants import VertexProperties as VProps -from vitrage.datasources import CINDER_VOLUME_DATASOURCE -from vitrage.datasources import NOVA_HOST_DATASOURCE -from vitrage.datasources import NOVA_INSTANCE_DATASOURCE -from vitrage.datasources import NOVA_ZONE_DATASOURCE -from vitrage.datasources import OPENSTACK_CLUSTER -from vitrage.graph import Edge -from vitrage.graph import NXGraph -from vitrage.graph import Vertex from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest import vitrage_tempest_tests.tests.utils as utils @@ -36,9 +23,6 @@ LOG = logging.getLogger(__name__) class TestTopology(BaseTopologyTest): """Topology test class for Vitrage API tests.""" - NUM_ENTITIES_PER_TYPE = 'num_vertices' - NUM_EDGES_PER_TYPE = 'num_edges_per_type' - @classmethod def setUpClass(cls): super(TestTopology, cls).setUpClass() @@ -103,229 +87,3 @@ class TestTopology(BaseTopologyTest): self._validate_graph_correctness(graph, 3, 2, entities) finally: self._rollback_to_default() - - def _rollback_to_default(self): - self._delete_entities(instance=True, volume=True) - api_graph = self.vitrage_client.topology.get() - graph = self._create_graph_from_graph_dictionary(api_graph) - entities = self._entities_validation_data() - self._validate_graph_correctness(graph, 3, 2, entities) - - def _create_entities(self, num_instances=0, num_volumes=0, end_sleep=3): - if num_instances > 0: - resources = self._create_instances(num_instances) - self._wait_for_status(20, - self._check_num_instances, - num_instances=num_instances) - time.sleep(1) - - if num_volumes > 0: - self._create_volume_and_attach('volume-1', 1, - resources[0].__dict__['id'], - '/tmp/vda') - self._wait_for_status(20, - self._check_num_volumes, - num_volumes=1) - - # waiting until all the entities creation were processed by the - # entity graph processor - time.sleep(end_sleep) - - def _delete_entities(self, instance=False, volume=False): - if volume: - self._delete_volumes() - self._wait_for_status(30, - self._check_num_volumes, - num_volumes=0) - - if instance: - self._delete_instances() - self._wait_for_status(20, - self._check_num_instances, - num_instances=0) - - # waiting until all the entities deletion were processed by the - # entity graph processor - time.sleep(5) - - def _entities_validation_data(self, cluster_entities=1, cluster_edges=1, - zone_entities=1, zone_edges=2, - host_entities=1, host_edges=1, - instance_entities=0, instance_edges=0, - volume_entities=0, volume_edges=0): - return [ - {VProps.TYPE: OPENSTACK_CLUSTER, - self.NUM_ENTITIES_PER_TYPE: cluster_entities, - self.NUM_EDGES_PER_TYPE: cluster_edges}, - {VProps.TYPE: NOVA_ZONE_DATASOURCE, - self.NUM_ENTITIES_PER_TYPE: zone_entities, - self.NUM_EDGES_PER_TYPE: zone_edges}, - {VProps.TYPE: NOVA_HOST_DATASOURCE, - self.NUM_ENTITIES_PER_TYPE: host_entities, - self.NUM_EDGES_PER_TYPE: host_edges}, - {VProps.TYPE: NOVA_INSTANCE_DATASOURCE, - self.NUM_ENTITIES_PER_TYPE: instance_entities, - self.NUM_EDGES_PER_TYPE: instance_edges}, - {VProps.TYPE: CINDER_VOLUME_DATASOURCE, - self.NUM_ENTITIES_PER_TYPE: volume_entities, - self.NUM_EDGES_PER_TYPE: volume_edges} - ] - - def _validate_graph_correctness(self, - graph, - num_entities, - num_edges, - entities): - self.assertIsNot(None, graph) - self.assertIsNot(None, entities) - self.assertEqual(num_entities, graph.num_vertices()) - self.assertEqual(num_edges, graph.num_edges()) - - for entity in entities: - query = { - VProps.CATEGORY: EntityCategory.RESOURCE, - VProps.TYPE: entity[VProps.TYPE], - VProps.IS_DELETED: False, - VProps.IS_PLACEHOLDER: False - } - vertices = graph.get_vertices(vertex_attr_filter=query) - self.assertEqual(entity[self.NUM_ENTITIES_PER_TYPE], len(vertices)) - - num_edges = sum([len(graph.get_edges(vertex.vertex_id)) - for vertex in vertices]) - self.assertEqual(entity[self.NUM_EDGES_PER_TYPE], num_edges) - - def _check_num_instances(self, num_instances=0): - return len(self.nova_client.servers.list()) == num_instances - - def _check_num_volumes(self, num_volumes=0): - return len(self.cinder_client.volumes.list()) == num_volumes - - def _create_volume_and_attach(self, name, size, instance_id, mount_point): - volume = self.cinder_client.volumes.create(display_name=name, - size=size) - time.sleep(3) - self.cinder_client.volumes.attach(volume=volume, - instance_uuid=instance_id, - mountpoint=mount_point) - return volume - - def _create_instances(self, num_machines): - flavors_list = self.nova_client.flavors.list() - images_list = self.nova_client.images.list() - - resources = [self.nova_client.servers.create( - name='%s-%s' % ('vm', index), - flavor=flavors_list[0], - image=images_list[0]) for index in range(num_machines)] - - return resources - - def _delete_instances(self): - instances = self.nova_client.servers.list() - for instance in instances: - try: - self.nova_client.servers.delete(instance) - except Exception: - pass - - def _delete_volumes(self): - volumes = self.cinder_client.volumes.list() - for volume in volumes: - try: - self.cinder_client.volumes.detach(volume) - self.cinder_client.volumes.force_delete(volume) - except Exception: - self.cinder_client.volumes.force_delete(volume) - - @staticmethod - def _wait_for_status(max_waiting, func, **kwargs): - count = 0 - while count < max_waiting: - if func(**kwargs): - return True - count += 1 - time.sleep(2) - LOG.info("wait_for_status - False ") - return False - - @staticmethod - def _compare_graphs(api_graph, cli_graph): - """Compare Graph object to graph form terminal """ - if not api_graph: - LOG.error("The topology graph taken from rest api is empty") - return False - if not cli_graph: - LOG.error("The topology graph taken from terminal is empty") - return False - - parsed_topology = json.loads(cli_graph) - - sorted_cli_graph = sorted(parsed_topology.items()) - sorted_api_graph = sorted(api_graph.items()) - - for item in sorted_cli_graph[4][1]: - item.pop(VProps.UPDATE_TIMESTAMP, None) - - for item in sorted_api_graph[4][1]: - item.pop(VProps.UPDATE_TIMESTAMP, None) - - return sorted_cli_graph == sorted_api_graph - - @staticmethod - def _create_graph_from_graph_dictionary(api_graph): - graph = NXGraph() - - nodes = api_graph['nodes'] - for i in xrange(len(nodes)): - graph.add_vertex(Vertex(str(i), nodes[i])) - - edges = api_graph['links'] - for i in xrange(len(edges)): - graph.add_edge(Edge(str(edges[i]['source']), - str(edges[i]['target']), - edges[i]['relationship_type'])) - - return graph - - def _create_graph_from_tree_dictionary(self, - api_graph, - graph=None, - ancestor=None): - children = [] - graph = NXGraph() if not graph else graph - - if 'children' in api_graph: - children = api_graph.copy()['children'] - del api_graph['children'] - - vertex = Vertex(api_graph[VProps.VITRAGE_ID], api_graph) - graph.add_vertex(vertex) - if ancestor: - graph.add_edge(Edge(ancestor[VProps.VITRAGE_ID], - vertex[VProps.VITRAGE_ID], - 'label')) - - for entity in children: - self._create_graph_from_tree_dictionary(entity, graph, vertex) - - return graph - - @staticmethod - def _graph_query(): - return '{"and": [{"==": {"category": "RESOURCE"}},' \ - '{"==": {"is_deleted": false}},' \ - '{"==": {"is_placeholder": false}},' \ - '{"or": [{"==": {"type": "openstack.cluster"}},' \ - '{"==": {"type": "nova.instance"}},' \ - '{"==": {"type": "nova.host"}},' \ - '{"==": {"type": "nova.zone"}}]}]}' - - @staticmethod - def _tree_query(): - return '{"and": [{"==": {"category": "RESOURCE"}},' \ - '{"==": {"is_deleted": false}},' \ - '{"==": {"is_placeholder": false}},' \ - '{"or": [{"==": {"type": "openstack.cluster"}},' \ - '{"==": {"type": "nova.host"}},' \ - '{"==": {"type": "nova.zone"}}]}]}' diff --git a/vitrage_tempest_tests/tests/utils.py b/vitrage_tempest_tests/tests/utils.py index 29a794725..73962d14c 100644 --- a/vitrage_tempest_tests/tests/utils.py +++ b/vitrage_tempest_tests/tests/utils.py @@ -79,11 +79,8 @@ def run_vitrage_command(command): return None -def run_vitrage_command_with_user(command, user): - run_vitrage_command( - "cd /openstack/devstack; . openrc " + - user + " " + user + - "; " + command) +def run_vitrage_command_as_admin(command): + run_vitrage_command(command) def run_from_terminal(command):