diff --git a/test-requirements.txt b/test-requirements.txt index 7a1e69841..dd61a7d3f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -23,6 +23,7 @@ oslo.messaging!=2.8.0,>2.6.1 # Apache-2.0 oslo.i18n>=2.1.0 oslo.policy>=0.3.0 pecan>=0.8.0 +tempest-lib>=0.14.0 # Apache-2.0 testrepository>=0.0.18 testscenarios>=0.4 testtools>=1.4.0 diff --git a/vitrage/api/controllers/rest.py b/vitrage/api/controllers/rest.py index 2dfdfde3f..c1eca381f 100644 --- a/vitrage/api/controllers/rest.py +++ b/vitrage/api/controllers/rest.py @@ -19,7 +19,6 @@ from oslo_log import log import pecan as pecan from pecan import abort from pecan import rest -from vitrage.common.constants import EntityCategory from vitrage.datasources import OPENSTACK_NODE @@ -29,9 +28,7 @@ LOG = log.getLogger(__name__) class RootRestController(rest.RestController): @staticmethod - def as_tree(graph, - root='%s:%s' % (EntityCategory.RESOURCE, OPENSTACK_NODE), - reverse=False): + def as_tree(graph, root=OPENSTACK_NODE, reverse=False): linked_graph = json_graph.node_link_graph(graph) if reverse: linked_graph = linked_graph.reverse() diff --git a/vitrage/datasources/static_physical/__init__.py b/vitrage/datasources/static_physical/__init__.py index 05afb1567..236e61943 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=30, - min=30, + default=5, + min=5, help='interval between checking changes in the configuration ' 'files of the physical topology data sources', required=True), diff --git a/vitrage/datasources/static_physical/driver.py b/vitrage/datasources/static_physical/driver.py index 09fb95942..113ea5f2b 100644 --- a/vitrage/datasources/static_physical/driver.py +++ b/vitrage/datasources/static_physical/driver.py @@ -100,12 +100,23 @@ class StaticPhysicalDriver(DriverBase): entities_updates += \ self._get_entities_from_file(file_, full_path) + # iterate over deleted files + deleted_files = set(self.cache.keys()) - set(files) + for file_ in deleted_files: + self._update_on_existing_entities( + self.cache[file_][self.ENTITIES_SECTION], + {}, + entities_updates) + del self.cache[file_] + return entities_updates - def _update_on_existing_entities(self, old_entities, - new_entities, updates): + def _update_on_existing_entities(self, + old_entities, + new_entities, + updates): for old_entity in old_entities: - if old_entity not in new_entities: + if not new_entities or old_entity not in new_entities: new_entity = self._find_entity(old_entity, new_entities) if not new_entity: self._set_event_type(old_entity, EventAction.DELETE_ENTITY) diff --git a/vitrage/datasources/static_physical/transformer.py b/vitrage/datasources/static_physical/transformer.py index 7e06ce7c0..929b01cbf 100644 --- a/vitrage/datasources/static_physical/transformer.py +++ b/vitrage/datasources/static_physical/transformer.py @@ -16,6 +16,7 @@ from oslo_log import log as logging from vitrage.common.constants import DatasourceProperties as DSProps from vitrage.common.constants import EntityCategory +from vitrage.common.constants import EventAction from vitrage.common.constants import VertexProperties as VProps from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE from vitrage.datasources.resource_transformer_base import \ @@ -33,6 +34,11 @@ class StaticPhysicalTransformer(ResourceTransformerBase): RELATION_TYPE = 'relation_type' RELATIONSHIPS_SECTION = 'relationships' + # Event types which need to refer them differently + UPDATE_EVENT_TYPES = { + EventAction.DELETE_ENTITY: EventAction.DELETE_ENTITY + } + def __init__(self, transformers): super(StaticPhysicalTransformer, self).__init__(transformers) self._register_relations_direction() diff --git a/vitrage/entity_graph/processor/entity_graph.py b/vitrage/entity_graph/processor/entity_graph.py index 92b12ce39..c7d0f5d49 100644 --- a/vitrage/entity_graph/processor/entity_graph.py +++ b/vitrage/entity_graph/processor/entity_graph.py @@ -114,6 +114,7 @@ class EntityGraph(NXGraph): def update_entity_graph_vertex(self, graph_vertex, updated_vertex): if updated_vertex[VProps.IS_PLACEHOLDER] and \ graph_vertex and not graph_vertex[VProps.IS_PLACEHOLDER]: + updated_vertex[VProps.IS_PLACEHOLDER] = False updated_vertex[VProps.IS_DELETED] = graph_vertex[VProps.IS_DELETED] diff --git a/vitrage_tempest_tests/tests/api/data_sources/__init__.py b/vitrage_tempest_tests/tests/api/datasources/__init__.py similarity index 100% rename from vitrage_tempest_tests/tests/api/data_sources/__init__.py rename to vitrage_tempest_tests/tests/api/datasources/__init__.py diff --git a/vitrage_tempest_tests/tests/api/datasources/test_aodh.py b/vitrage_tempest_tests/tests/api/datasources/test_aodh.py new file mode 100644 index 000000000..211da1fb8 --- /dev/null +++ b/vitrage_tempest_tests/tests/api/datasources/test_aodh.py @@ -0,0 +1,40 @@ +# Copyright 2016 - 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_log import log as logging + +from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest + +LOG = logging.getLogger(__name__) + + +class TestAodhAlarm(BaseTopologyTest): + + @classmethod + def setUpClass(cls): + super(TestAodhAlarm, cls).setUpClass() + + def test_alarm(self): + try: + # create entities + self._create_switches() + 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) + finally: + self._rollback_to_default() diff --git a/vitrage_tempest_tests/tests/api/datasources/test_cinder_volume.py b/vitrage_tempest_tests/tests/api/datasources/test_cinder_volume.py new file mode 100644 index 000000000..89c4fddeb --- /dev/null +++ b/vitrage_tempest_tests/tests/api/datasources/test_cinder_volume.py @@ -0,0 +1,40 @@ +# Copyright 2016 - 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_log import log as logging + +from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest + +LOG = logging.getLogger(__name__) + + +class TestCinderVolume(BaseTopologyTest): + + @classmethod + def setUpClass(cls): + super(TestCinderVolume, cls).setUpClass() + + def test_volume(self): + try: + # create entities + self._create_entities(num_instances=3, num_volumes=1) + 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) + finally: + self._rollback_to_default() diff --git a/vitrage_tempest_tests/tests/api/datasources/test_nova.py b/vitrage_tempest_tests/tests/api/datasources/test_nova.py new file mode 100644 index 000000000..bf4d3fb9c --- /dev/null +++ b/vitrage_tempest_tests/tests/api/datasources/test_nova.py @@ -0,0 +1,39 @@ +# Copyright 2016 - 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_log import log as logging + +from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest + +LOG = logging.getLogger(__name__) + + +class TestNova(BaseTopologyTest): + + @classmethod + def setUpClass(cls): + super(TestNova, cls).setUpClass() + + def test_nova_entities(self): + try: + # create entities + self._create_entities(num_instances=3, end_sleep=10) + 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=3) + self._validate_graph_correctness(graph, 6, 5, entities) + finally: + self._rollback_to_default() diff --git a/vitrage_tempest_tests/tests/api/datasources/test_static_physical.py b/vitrage_tempest_tests/tests/api/datasources/test_static_physical.py new file mode 100644 index 000000000..0f6744f3b --- /dev/null +++ b/vitrage_tempest_tests/tests/api/datasources/test_static_physical.py @@ -0,0 +1,62 @@ +# Copyright 2016 - 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 socket +import time + +from oslo_log import log as logging + +from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest + + +LOG = logging.getLogger(__name__) + + +class TestStaticPhysical(BaseTopologyTest): + + @classmethod + def setUpClass(cls): + super(TestStaticPhysical, cls).setUpClass() + + def test_switches(self): + try: + # create entities + self._create_switches() + 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=3, + switch_entities=2, switch_edges=2) + self._validate_graph_correctness(graph, 5, 4, entities) + finally: + self._rollback_to_default() + + @staticmethod + def _create_switches(): + hostname = socket.gethostname() + + # template file + file_path = '/opt/stack/vitrage/vitrage_tempest_tests/tests/' \ + 'resources/static_physical/tempest_configuration.yaml' + with open(file_path, 'rb') as f: + template_data = f.read() + template_data = template_data.replace('tmp-devstack', hostname) + + # new file + new_file = open( + '/etc/vitrage/static_datasources/tempest_configuration.yaml', 'wb') + new_file.write(template_data) + new_file.close() + + time.sleep(7) diff --git a/vitrage_tempest_tests/tests/api/topology/base.py b/vitrage_tempest_tests/tests/api/topology/base.py new file mode 100644 index 000000000..ec7346eed --- /dev/null +++ b/vitrage_tempest_tests/tests/api/topology/base.py @@ -0,0 +1,299 @@ +# Copyright 2016 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 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_NODE +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 + +LOG = logging.getLogger(__name__) + + +class BaseTopologyTest(test.BaseTestCase): + """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) + 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, + 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) + + # 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, node_entities=1, node_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_NODE, + self.NUM_ENTITIES_PER_TYPE: node_entities, + self.NUM_EDGES_PER_TYPE: node_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 """ + 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.node"}},' \ + '{"==": {"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.node"}},' \ + '{"==": {"type": "nova.host"}},' \ + '{"==": {"type": "nova.zone"}}]}]}' diff --git a/vitrage_tempest_tests/tests/api/topology/test_topology.py b/vitrage_tempest_tests/tests/api/topology/test_topology.py index 0fb1f665b..e5201ba14 100644 --- a/vitrage_tempest_tests/tests/api/topology/test_topology.py +++ b/vitrage_tempest_tests/tests/api/topology/test_topology.py @@ -17,7 +17,6 @@ import time from oslo_log import log as logging -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 @@ -28,15 +27,13 @@ from vitrage.datasources import OPENSTACK_NODE from vitrage.graph import Edge from vitrage.graph import NXGraph from vitrage.graph import Vertex -from vitrage import keystone_client -from vitrage_tempest_tests.tests.api.base import BaseVitrageTest +from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest import vitrage_tempest_tests.tests.utils as utils -from vitrageclient import client as v_client LOG = logging.getLogger(__name__) -class BaseTopologyTest(BaseVitrageTest): +class TestTopology(BaseTopologyTest): """Topology test class for Vitrage API tests.""" NUM_ENTITIES_PER_TYPE = 'num_vertices' @@ -44,12 +41,7 @@ class BaseTopologyTest(BaseVitrageTest): @classmethod def setUpClass(cls): - super(BaseTopologyTest, cls).setUpClass() - cls.conf = utils.get_conf() - 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) + super(TestTopology, cls).setUpClass() def test_compare_api_and_cli(self): """Wrapper that returns a test graph.""" diff --git a/vitrage_tempest_tests/tests/api/topology/utils.py b/vitrage_tempest_tests/tests/api/topology/utils.py deleted file mode 100644 index 74de4c11d..000000000 --- a/vitrage_tempest_tests/tests/api/topology/utils.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright 2016 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 json -import vitrage_tempest_tests.tests.utils as utils - -from oslo_log import log as logging - -from vitrage.api.controllers.v1.topology import TopologyController -from vitrage.common.constants import VertexProperties as VProps -from vitrage_tempest_tests.tests.api.base import BaseVitrageTest - -LOG = logging.getLogger(__name__) - - -class TopologyHelper(BaseVitrageTest): - """Topology test class for Vitrage API tests.""" - - def __init__(self): - super(TopologyHelper, self).__init__() - self.client = utils.get_client() - self.depth = '' - self.query = '' - self.root = '' - - def get_api_topology(self, graph_type): - """Get Graph objects returned by the v1 client """ - try: - api_graph = TopologyController().get_graph(graph_type=graph_type, - depth=self.depth, - query=self.query, - root=self.root) - except Exception as e: - LOG.exception("Failed to get topology (graph_type = " + - graph_type + ") %s ", e) - return None - - return api_graph - - def show_cli_topology(self): - """Get Graph objects returned by cli """ - LOG.debug("The command is : vitrage topology show") - - return utils.run_vitrage_command_with_user( - "vitrage topology show", self.conf.service_credentials.user) - - def create_volume(self): - flavor_id = self.get_flavor_id_from_list() - image_id = self.get_image_id_from_list() - - resources = ["vm_for_vol", "vol_for_vm"] - self.create_vm_with_exist_image(resources[0], flavor_id, image_id) - self.create_volume_with_exist_size(resources[1]) - self.attach_volume(resources[0], resources[1]) - return resources - - @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) - LOG.debug("The topology graph taken from terminal is : " + - json.dumps(parsed_topology)) - LOG.debug("The topology graph taken by api is : %s", - json.dumps(api_graph)) - - cli_items = sorted(parsed_topology.items()) - api_items = sorted(api_graph.items()) - - for item in cli_items[4][1]: - item.pop(VProps.UPDATE_TIMESTAMP, None) - - for item in api_items[4][1]: - item.pop(VProps.UPDATE_TIMESTAMP, None) - - return cli_items == api_items - - @staticmethod - def validate_graph_correctness(cli_graph, resources): - """Compare Graph object to graph form terminal """ - if not cli_graph: - LOG.error("The topology graph taken from terminal is empty") - return False - if not resources: - LOG.error("The resources list is empty") - return False - - parsed_topology = json.loads(cli_graph) - LOG.debug("The topology graph taken from terminal is : " + - json.dumps(parsed_topology)) - cli_items = sorted(parsed_topology.items()) - - for resource_name in resources: - for item in cli_items[4][1]: - item.pop(VProps.UPDATE_TIMESTAMP, None) - if resource_name not in cli_items[4][1]: - LOG.error("The resources " + resource_name + - "doesn't exist in the topology graph") - return False - return True diff --git a/vitrage_tempest_tests/tests/resources/static_physical/tempest_configuration.yaml b/vitrage_tempest_tests/tests/resources/static_physical/tempest_configuration.yaml new file mode 100644 index 000000000..800dc2fa9 --- /dev/null +++ b/vitrage_tempest_tests/tests/resources/static_physical/tempest_configuration.yaml @@ -0,0 +1,19 @@ +entities: + - type: switch + name: switch-1 + id: 12345 + state: available + relationships: + - type: nova.host + name: tmp-devstack + id: tmp-devstack + relation_type: attached + - type: switch + name: switch-2 + id: 23456 + state: available + relationships: + - type: nova.host + name: tmp-devstack + id: tmp-devstack + relation_type: attached \ No newline at end of file diff --git a/vitrage_tempest_tests/tests/utils.py b/vitrage_tempest_tests/tests/utils.py index 5faaf115b..29a794725 100644 --- a/vitrage_tempest_tests/tests/utils.py +++ b/vitrage_tempest_tests/tests/utils.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +import socket + from oslo_config import cfg from oslo_log import log as logging from vitrage import service @@ -55,7 +57,8 @@ def get_from_terminal(command): def run_vitrage_command(command): - auth_url = '--os-auth-url http://10.41.251.201:5000/v2.0' + local_ip = socket.gethostbyname(socket.gethostname()) + auth_url = '--os-auth-url http://%s:5000/v2.0' % local_ip user = '--os-user-name admin' password = '--os-password password' project_name = '--os-project-name admin'