diff --git a/vitrage/api/app.py b/vitrage/api/app.py index 6035f84cf..685b6be65 100644 --- a/vitrage/api/app.py +++ b/vitrage/api/app.py @@ -38,6 +38,7 @@ APPCONFIGS = {} def setup_app(root, conf=None): app_hooks = [hooks.ConfigHook(conf), hooks.TranslationHook(), + hooks.GCHook(), hooks.RPCHook(conf), hooks.ContextHook(), hooks.DBHook(conf)] diff --git a/vitrage/api/controllers/v1/alarm_base.py b/vitrage/api/controllers/v1/alarm_base.py index 3eed14c78..453ccafe1 100644 --- a/vitrage/api/controllers/v1/alarm_base.py +++ b/vitrage/api/controllers/v1/alarm_base.py @@ -13,7 +13,6 @@ # under the License. -import json from oslo_log import log from oslo_utils.strutils import bool_from_string import pecan @@ -23,7 +22,7 @@ from vitrage.api.controllers.rest import RootRestController from vitrage.api.policy import enforce from vitrage.common.constants import TenantProps from vitrage.common.constants import VertexProperties as Vprops - +from vitrage.common.utils import decompress_obj LOG = log.getLogger(__name__) @@ -53,7 +52,7 @@ class BaseAlarmsController(RootRestController): enforce("list alarms", pecan.request.headers, pecan.request.enforcer, {}) - alarms_json = \ + alarms = \ pecan.request.client.call(pecan.request.context, 'get_alarms', vitrage_id=vitrage_id, @@ -71,7 +70,7 @@ class BaseAlarmsController(RootRestController): ) try: - alarms_list = json.loads(alarms_json)['alarms'] + alarms_list = decompress_obj(alarms)['alarms'] return alarms_list except Exception: diff --git a/vitrage/api/controllers/v1/resource.py b/vitrage/api/controllers/v1/resource.py index 87110b219..a6aca30b6 100644 --- a/vitrage/api/controllers/v1/resource.py +++ b/vitrage/api/controllers/v1/resource.py @@ -19,7 +19,7 @@ from pecan.core import abort from vitrage.api.controllers.rest import RootRestController from vitrage.api.policy import enforce - +from vitrage.common.utils import decompress_obj LOG = log.getLogger(__name__) @@ -55,13 +55,12 @@ class ResourcesController(RootRestController): LOG.info('get_resources with type: %s, all_tenants: %s', resource_type, all_tenants) try: - resources_json = \ + resources = \ pecan.request.client.call(pecan.request.context, 'get_resources', resource_type=resource_type, all_tenants=all_tenants) - LOG.info(resources_json) - resources = json.loads(resources_json)['resources'] + resources = decompress_obj(resources)['resources'] return resources except Exception: LOG.exception('Failed to get resources.') diff --git a/vitrage/api/controllers/v1/root.py b/vitrage/api/controllers/v1/root.py index be94fcc5b..138228124 100644 --- a/vitrage/api/controllers/v1/root.py +++ b/vitrage/api/controllers/v1/root.py @@ -9,6 +9,7 @@ # 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 gc from vitrage.api.controllers.v1 import alarm from vitrage.api.controllers.v1 import event @@ -20,6 +21,9 @@ from vitrage.api.controllers.v1 import webhook class V1Controller(object): + + gc.set_threshold(1, 1, 1) + topology = topology.TopologyController() resources = resource.ResourcesController() alarm = alarm.AlarmsController() diff --git a/vitrage/api/controllers/v1/topology.py b/vitrage/api/controllers/v1/topology.py index a0de9361d..d3771942e 100644 --- a/vitrage/api/controllers/v1/topology.py +++ b/vitrage/api/controllers/v1/topology.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. - import json import networkx as nx @@ -27,6 +26,7 @@ from vitrage.api.policy import enforce from vitrage.common.constants import VertexProperties as VProps # noinspection PyProtectedMember +from vitrage.common.utils import decompress_obj from vitrage.datasources.transformer_base import CLUSTER_ID @@ -77,8 +77,7 @@ class TopologyController(RootRestController): query=query, root=root, all_tenants=all_tenants) - LOG.debug(graph_data) - graph = json.loads(graph_data) + graph = decompress_obj(graph_data) if graph_type == 'graph': return graph if graph_type == 'tree': diff --git a/vitrage/api/hooks.py b/vitrage/api/hooks.py index c8a6c9d48..389fe092f 100644 --- a/vitrage/api/hooks.py +++ b/vitrage/api/hooks.py @@ -9,7 +9,7 @@ # 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 gc import oslo_messaging from oslo_context import context @@ -89,3 +89,9 @@ class DBHook(hooks.PecanHook): def before(self, state): state.request.storage = self.storage + + +class GCHook(hooks.PecanHook): + + def after(self, state): + gc.collect() diff --git a/vitrage/api_handler/apis/alarm.py b/vitrage/api_handler/apis/alarm.py index 787df9bc3..6ace84c20 100755 --- a/vitrage/api_handler/apis/alarm.py +++ b/vitrage/api_handler/apis/alarm.py @@ -22,6 +22,7 @@ from vitrage.common.constants import EntityCategory as ECategory from vitrage.common.constants import HistoryProps as HProps from vitrage.common.constants import TenantProps from vitrage.common.constants import VertexProperties as VProps +from vitrage.common.utils import compress_obj from vitrage.datasources.alarm_properties import AlarmProperties as AProps from vitrage.entity_graph.mappings.operational_alarm_severity import \ OperationalAlarmSeverity @@ -54,7 +55,8 @@ class AlarmApis(EntityGraphApisBase): kwargs.get('filter_vals', []).append(vitrage_id) alarms = self._get_alarms(*args, **kwargs) - return json.dumps({'alarms': [v.payload for v in alarms]}) + data = {'alarms': [v.payload for v in alarms]} + return compress_obj(data, level=1) # TODO(annarez): add db support def show_alarm(self, ctx, vitrage_id): diff --git a/vitrage/api_handler/apis/resource.py b/vitrage/api_handler/apis/resource.py index d7a3d92f6..5f1cc9a59 100644 --- a/vitrage/api_handler/apis/resource.py +++ b/vitrage/api_handler/apis/resource.py @@ -21,7 +21,8 @@ from vitrage.api_handler.apis.base import RESOURCES_ALL_QUERY from vitrage.common.constants import EntityCategory from vitrage.common.constants import TenantProps from vitrage.common.constants import VertexProperties as VProps - +from vitrage.common.utils import compress_obj +from vitrage.common.utils import timed_method LOG = log.getLogger(__name__) @@ -34,6 +35,7 @@ class ResourceApis(EntityGraphApisBase): self.entity_graph = entity_graph self.conf = conf + @timed_method(log_results=True) def get_resources(self, ctx, resource_type=None, all_tenants=False): LOG.debug('ResourceApis get_resources - resource_type: %s,' 'all_tenants: %s', str(resource_type), all_tenants) @@ -55,8 +57,8 @@ class ResourceApis(EntityGraphApisBase): query['and'].append(type_query) resources = self.entity_graph.get_vertices(query_dict=query) - return json.dumps({'resources': [resource.properties - for resource in resources]}) + data = {'resources': [r.properties for r in resources]} + return compress_obj(data, level=1) def show_resource(self, ctx, vitrage_id): diff --git a/vitrage/api_handler/apis/topology.py b/vitrage/api_handler/apis/topology.py index f4e891fb4..ed4214fcb 100644 --- a/vitrage/api_handler/apis/topology.py +++ b/vitrage/api_handler/apis/topology.py @@ -11,6 +11,7 @@ # 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 networkx.algorithms.shortest_paths.generic import shortest_path from oslo_log import log from osprofiler import profiler @@ -24,7 +25,8 @@ from vitrage.common.constants import EntityCategory from vitrage.common.constants import TenantProps from vitrage.common.constants import VertexProperties as VProps from vitrage.common.exception import VitrageError -from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE +from vitrage.common.utils import compress_obj +from vitrage.common.utils import timed_method from vitrage.datasources import OPENSTACK_CLUSTER LOG = log.getLogger(__name__) @@ -38,13 +40,13 @@ class TopologyApis(EntityGraphApisBase): self.entity_graph = entity_graph self.conf = conf + @timed_method(log_results=True) def get_topology(self, ctx, graph_type, depth, query, root, all_tenants): LOG.debug("TopologyApis get_topology - root: %s, all_tenants=%s", str(root), all_tenants) project_id = ctx.get(TenantProps.TENANT, None) is_admin_project = ctx.get(TenantProps.IS_ADMIN, False) - ga = self.entity_graph.algo LOG.debug('project_id = %s, is_admin_project %s', project_id, is_admin_project) @@ -63,29 +65,30 @@ class TopologyApis(EntityGraphApisBase): {'==': {VProps.PROJECT_ID: None}}]} current_query = {'and': [query, project_query]} - graph = ga.graph_query_vertices(root_id, - query_dict=current_query, - depth=depth, - edge_query_dict=EDGE_QUERY) + graph = self.entity_graph.algo.graph_query_vertices( + root_id, + query_dict=current_query, + depth=depth, + edge_query_dict=EDGE_QUERY) # By default the graph_type is 'graph' else: if all_tenants: q = query if query else TOPOLOGY_AND_ALARMS_QUERY - graph = ga.create_graph_from_matching_vertices( - query_dict=q, - edge_attr_filter={VProps.VITRAGE_IS_DELETED: False}) + graph = \ + self.entity_graph.algo.create_graph_from_matching_vertices( + query_dict=q, + edge_attr_filter={VProps.VITRAGE_IS_DELETED: False}) else: graph = self._get_topology_for_specific_project( - ga, query, project_id, is_admin_project, root_id) - return graph.json_output_graph() + data = graph.json_output_graph(raw=True) + return compress_obj(data, level=1) def _get_topology_for_specific_project(self, - ga, query, project_id, is_admin_project, @@ -95,7 +98,6 @@ class TopologyApis(EntityGraphApisBase): Finds all the entities which has project_id. In case the tenant is admin then project_id can also be None. - :type ga: NXAlgorithm :type query: dictionary :type project_id: string :type is_admin_project: boolean @@ -118,25 +120,24 @@ class TopologyApis(EntityGraphApisBase): default_query = {'or': [resource_query, alarm_query]} q = default_query - tmp_graph = ga.create_graph_from_matching_vertices(query_dict=q) - graph = self._create_graph_of_connected_components(ga, tmp_graph, root) + vertices_ids = self.entity_graph.get_vertices_ids(query_dict=q) + vertices_ids = self._all_paths_from_node(self.entity_graph, + source_node=root, + targets=vertices_ids) + graph = self.entity_graph.algo.subgraph(vertices_ids).copy() edge_query = {EProps.VITRAGE_IS_DELETED: False} - self._remove_unnecessary_elements(ga, - graph, - project_id, - is_admin_project, - edge_attr_filter=edge_query) + self._remove_unnecessary_elements( + graph, project_id, is_admin_project, edge_attr_filter=edge_query) return graph def _remove_unnecessary_elements(self, - ga, graph, project_id, is_admin_project, edge_attr_filter): # delete non matching edges - ga._apply_edge_attr_filter(graph, edge_attr_filter) + self.entity_graph.algo.apply_edge_attr_filter(graph, edge_attr_filter) self._remove_alarms_of_other_projects(graph, project_id, @@ -173,44 +174,19 @@ class TopologyApis(EntityGraphApisBase): if cond1 or cond2: graph.remove_vertex(alarm) - def _create_graph_of_connected_components(self, ga, tmp_graph, root): - return ga.subgraph(self._topology_for_unrooted_graph(ga, - tmp_graph, - root)).copy() + @staticmethod + def _all_paths_from_node(graph, source_node, targets): + """Find all nodes on a (shortest) path from source to targets - def _topology_for_unrooted_graph(self, ga, subgraph, root): - """Finds topology for unrooted subgraph + Return all the node ids that are either in targets + or are in a path from source node to any of targets - 1. Finds all the connected component subgraphs in subgraph. - 2. For each component, finds the path from one of the VMs (if exists) - to the root entity. - 3. Unify all the entities found and return them - - :type ga: NXAlgorithm - :type subgraph: networkx graph - :type root: string :rtype: list """ - - entities = [] - - root_vertex = \ - self.entity_graph.get_vertex(root) - - local_connected_component_subgraphs = \ - ga.connected_component_subgraphs(subgraph) - - for component_subgraph in local_connected_component_subgraphs: - entities += list(component_subgraph.nodes()) - instance_in_component_subgraph = \ - self._find_instance_in_graph(component_subgraph) - if instance_in_component_subgraph: - paths = ga.all_simple_paths(root_vertex.vertex_id, - instance_in_component_subgraph) - for path in paths: - entities += path - - return set(entities) + vertices_ids = targets + paths = shortest_path(graph._g, source=source_node) + vertices_ids.update(*[set(paths.get(n, [])) for n in targets]) + return vertices_ids def _default_root_id(self): tmp_vertices = self.entity_graph.get_vertices( @@ -221,13 +197,3 @@ class TopologyApis(EntityGraphApisBase): if len(tmp_vertices) > 1: raise VitrageError("Multiple root vertices found") return tmp_vertices[0].vertex_id - - @staticmethod - def _find_instance_in_graph(graph): - for node, node_data in graph.nodes(data=True): - if node_data[VProps.VITRAGE_CATEGORY] == \ - EntityCategory.RESOURCE \ - and node_data[VProps.VITRAGE_TYPE] == \ - NOVA_INSTANCE_DATASOURCE: - return node - return None diff --git a/vitrage/common/utils.py b/vitrage/common/utils.py index 648c55095..bfb7381ff 100644 --- a/vitrage/common/utils.py +++ b/vitrage/common/utils.py @@ -16,18 +16,25 @@ # 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 base64 from collections import defaultdict import copy import hashlib import itertools import random import six +from six.moves import cPickle import threading +import time +import zlib from oslo_config import cfg +from oslo_log import log import cProfile +LOG = log.getLogger(__name__) + def recursive_keypairs(d, separator='.'): # taken from ceilometer and gnocchi @@ -116,3 +123,35 @@ def fmt(docstr): docstr = docstr.strip() return docstr + + +def timed_method(log_results=False, warn_above_sec=-1): + def _decorator(function): + def wrapper(*args, **kwargs): + t1 = time.time() + result = function(*args, **kwargs) + t2 = time.time() + if warn_above_sec > 0 and warn_above_sec < t2 - t1: + LOG.warning( + 'Function %s runtime crossed limit %s seconds.', + function.__name__, t2 - t1) + elif log_results: + LOG.info('Function %s timed %s', function.__name__, t2 - t1) + return result + return wrapper + return _decorator + + +def compress_obj(obj, level=9): + str_data = cPickle.dumps(obj) + data = base64.b64encode(zlib.compress(str_data, level)) + return data + + +def decompress_obj(blob): + decoded_blob = base64.standard_b64decode(blob) + str_data = zlib.decompress(decoded_blob) + obj = cPickle.loads(str_data) + del decoded_blob + del str_data + return obj diff --git a/vitrage/graph/algo_driver/algorithm.py b/vitrage/graph/algo_driver/algorithm.py index 7b4cc7859..a411e7f39 100644 --- a/vitrage/graph/algo_driver/algorithm.py +++ b/vitrage/graph/algo_driver/algorithm.py @@ -79,29 +79,8 @@ class GraphAlgorithm(object): """ pass - @staticmethod - def connected_component_subgraphs(subgraph): - """Generate connected components as subgraphs. - - :type subgraph: NetworkX graph. - :rtype: list of NXGraphs - """ - pass - - def all_simple_paths(self, source, target): - """Generate all simple paths in the graph G from source to target. - - A simple path is a path with no repeated nodes. - - :type source: Starting node for path - :type target: Ending node for path - :rtype: lists of simple paths - """ - pass - @abc.abstractmethod def create_graph_from_matching_vertices(self, - vertex_attr_filter=None, query_dict=None, edge_attr_filter=None): """Generate graph using the query @@ -109,7 +88,6 @@ class GraphAlgorithm(object): Finds all the vertices in the graph matching the query, and returns a subgraph consisted from the vertices - :type vertex_attr_filter: dictionary :type query_dict: dictionary :type edge_attr_filter: dictionary :rtype: NXGraph diff --git a/vitrage/graph/algo_driver/networkx_algorithm.py b/vitrage/graph/algo_driver/networkx_algorithm.py index f331c4ee1..d9591442a 100644 --- a/vitrage/graph/algo_driver/networkx_algorithm.py +++ b/vitrage/graph/algo_driver/networkx_algorithm.py @@ -12,9 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -from networkx.algorithms import components -from networkx.algorithms import simple_paths - from oslo_log import log as logging from vitrage.common.constants import EdgeProperties as EProps @@ -23,8 +20,6 @@ from vitrage.graph.algo_driver.algorithm import Mapping from vitrage.graph.algo_driver.sub_graph_matching import NEG_CONDITION from vitrage.graph.algo_driver.sub_graph_matching import subgraph_matching from vitrage.graph.driver import Direction -from vitrage.graph.driver import Edge -from vitrage.graph.driver import Vertex from vitrage.graph.filter import check_filter from vitrage.graph.query import create_predicate @@ -84,10 +79,11 @@ class NXAlgorithm(GraphAlgorithm): e_result.extend(e_list) nodes_q.extend([(v_id, curr_depth + 1) for v_id, data in n_list]) - graph = self._create_new_graph( - graph.name, - vertices=self._vertex_result_to_list(n_result), - edges=self._edge_result_to_list(e_result)) + graph = self._create_new_graph(graph.name) + for v_id, data in n_result: + graph._g.add_node(v_id, **data) + for source_id, target_id, label, data in e_result: + graph._g.add_edge(source_id, target_id, label, **data) return graph @@ -128,32 +124,15 @@ class NXAlgorithm(GraphAlgorithm): validate) def create_graph_from_matching_vertices(self, - vertex_attr_filter=None, query_dict=None, edge_attr_filter=None): - if query_dict: - vertices = self.graph.get_vertices(query_dict=query_dict) - elif vertex_attr_filter: - vertices = self.graph.get_vertices( - vertex_attr_filter=vertex_attr_filter) - else: - vertices = self.graph.get_vertices() - - vertices_ids = [vertex.vertex_id for vertex in vertices] - + vertices_ids = self.graph.get_vertices_ids(query_dict=query_dict) graph = self._create_new_graph('graph') - graph._g = self.graph._g.subgraph(vertices_ids).copy() + graph._g = self.graph._g.subgraph(vertices_ids) # delete non matching edges if edge_attr_filter: - self._apply_edge_attr_filter(graph, edge_attr_filter) - - LOG.debug('match query, find graph: nodes %s, edges %s', - str(list(graph._g.nodes(data=True))), - str(list(graph._g.edges(data=True)))) - LOG.debug('match query, real graph: nodes %s, edges %s', - str(list(self.graph._g.nodes(data=True))), - str(list(self.graph._g.edges(data=True)))) + self.apply_edge_attr_filter(graph, edge_attr_filter) return graph @@ -162,15 +141,6 @@ class NXAlgorithm(GraphAlgorithm): subgraph._g = self.graph._g.subgraph(entities) return subgraph - def connected_component_subgraphs(self, subgraph): - return components.connected_component_subgraphs( - subgraph._g.to_undirected(), copy=False) - - def all_simple_paths(self, source, target): - return simple_paths.all_simple_paths(self.graph._g, - source=source, - target=target) - def _filtered_subgraph_matching(self, ge_v_id, sge_v_id, @@ -191,21 +161,6 @@ class NXAlgorithm(GraphAlgorithm): return [] - @staticmethod - def _edge_result_to_list(edge_result): - d = dict() - for source_id, target_id, label, data in edge_result: - d[(source_id, target_id, label)] = \ - Edge(source_id, target_id, label, properties=data) - return d.values() - - @staticmethod - def _vertex_result_to_list(vertex_result): - d = dict() - for v_id, data in vertex_result: - d[v_id] = Vertex(vertex_id=v_id, properties=data) - return d.values() - @staticmethod def _list_union(list_1, list_2): """Union of list that aren't hashable @@ -223,7 +178,7 @@ class NXAlgorithm(GraphAlgorithm): return list_1 @staticmethod - def _apply_edge_attr_filter(graph, edge_attr_filter): + def apply_edge_attr_filter(graph, edge_attr_filter): edges = graph._g.edges(data=True, keys=True) edges_to_remove = [(u, v, k) for (u, v, k, d) in edges if not check_filter(d, edge_attr_filter)] diff --git a/vitrage/graph/driver/networkx_graph.py b/vitrage/graph/driver/networkx_graph.py index b3551fa02..eabb3ce8b 100644 --- a/vitrage/graph/driver/networkx_graph.py +++ b/vitrage/graph/driver/networkx_graph.py @@ -250,6 +250,17 @@ class NXGraph(Graph): else: return [] + def get_vertices_ids(self, query_dict): + if not query_dict: + return list(self._g.nodes()) + + vertices_ids = set() + match_func = create_predicate(query_dict) + for node, node_data in self._g.nodes(data=True): + if match_func(node_data): + vertices_ids.add(node) + return vertices_ids + def get_vertices_by_key(self, key_values_hash): if key_values_hash in self.key_to_vertex_ids: diff --git a/vitrage/tests/functional/api/v1/test_noauth.py b/vitrage/tests/functional/api/v1/test_noauth.py index a4f7ba578..960e5695c 100755 --- a/vitrage/tests/functional/api/v1/test_noauth.py +++ b/vitrage/tests/functional/api/v1/test_noauth.py @@ -18,6 +18,7 @@ from datetime import datetime # noinspection PyPackageRequirements from mock import mock +from vitrage.common.utils import compress_obj from vitrage.storage.sqlalchemy import models from vitrage.tests.functional.api.v1 import FunctionalTest @@ -54,7 +55,7 @@ class NoAuthTest(FunctionalTest): def test_noauth_mode_get_topology(self): with mock.patch('pecan.request') as request: - request.client.call.return_value = '{}' + request.client.call.return_value = compress_obj({}) params = dict(depth=None, graph_type='graph', query=None, root=None, all_tenants=False) @@ -66,7 +67,7 @@ class NoAuthTest(FunctionalTest): def test_noauth_mode_list_alarms(self): with mock.patch('pecan.request') as request: - request.client.call.return_value = '{"alarms": []}' + request.client.call.return_value = compress_obj({"alarms": []}) params = dict(vitrage_id='all', all_tenants=False) data = self.get_json('/alarm/', params=params) @@ -95,7 +96,7 @@ class NoAuthTest(FunctionalTest): def test_noauth_mode_list_resources(self): with mock.patch('pecan.request') as request: - request.client.call.return_value = '{"resources": []}' + request.client.call.return_value = compress_obj({"resources": []}) params = dict(resource_type='all', all_tenants=False) data = self.get_json('/resources/', params=params) diff --git a/vitrage/tests/functional/api_handler/test_apis.py b/vitrage/tests/functional/api_handler/test_apis.py index 8650bf5d3..b29f131ed 100755 --- a/vitrage/tests/functional/api_handler/test_apis.py +++ b/vitrage/tests/functional/api_handler/test_apis.py @@ -26,6 +26,7 @@ from vitrage.common.constants import EdgeLabel from vitrage.common.constants import EdgeProperties from vitrage.common.constants import EntityCategory from vitrage.common.constants import VertexProperties as VProps +from vitrage.common.utils import decompress_obj from vitrage.datasources import NOVA_HOST_DATASOURCE from vitrage.datasources import NOVA_INSTANCE_DATASOURCE from vitrage.datasources import NOVA_ZONE_DATASOURCE @@ -60,7 +61,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): # Action alarms = apis.get_alarms(ctx, vitrage_id='all', all_tenants=False) - alarms = json.loads(alarms)['alarms'] + alarms = decompress_obj(alarms)['alarms'] # Test assertions self.assertThat(alarms, matchers.HasLength(3)) @@ -74,7 +75,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): # Action alarms = apis.get_alarms(ctx, vitrage_id='all', all_tenants=False) - alarms = json.loads(alarms)['alarms'] + alarms = decompress_obj(alarms)['alarms'] # Test assertions self.assertThat(alarms, matchers.HasLength(2)) @@ -105,7 +106,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): # Action alarms = apis.get_alarms(ctx, vitrage_id='all', all_tenants=True) - alarms = json.loads(alarms)['alarms'] + alarms = decompress_obj(alarms)['alarms'] # Test assertions self.assertThat(alarms, matchers.HasLength(5)) @@ -200,7 +201,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): query=None, root=None, all_tenants=False) - graph_topology = json.loads(graph_topology) + graph_topology = decompress_obj(graph_topology) # Test assertions self.assertThat(graph_topology['nodes'], matchers.HasLength(8)) @@ -222,7 +223,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): query=None, root=None, all_tenants=False) - graph_topology = json.loads(graph_topology) + graph_topology = decompress_obj(graph_topology) # Test assertions self.assertThat(graph_topology['nodes'], matchers.HasLength(7)) @@ -244,7 +245,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): query=None, root=None, all_tenants=True) - graph_topology = json.loads(graph_topology) + graph_topology = decompress_obj(graph_topology) # Test assertions self.assertThat(graph_topology['nodes'], matchers.HasLength(12)) @@ -260,7 +261,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): ctx, resource_type=None, all_tenants=False) - resources = json.loads(resources)['resources'] + resources = decompress_obj(resources)['resources'] # Test assertions self.assertThat(resources, matchers.HasLength(5)) @@ -276,7 +277,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): ctx, resource_type=None, all_tenants=False) - resources = json.loads(resources)['resources'] + resources = decompress_obj(resources)['resources'] # Test assertions self.assertThat(resources, matchers.HasLength(2)) @@ -292,7 +293,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): ctx, resource_type=NOVA_HOST_DATASOURCE, all_tenants=False) - resources = json.loads(resources)['resources'] + resources = decompress_obj(resources)['resources'] # Test assertions self.assertThat(resources, IsEmpty()) @@ -308,7 +309,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): ctx, resource_type=NOVA_INSTANCE_DATASOURCE, all_tenants=False) - resources = json.loads(resources)['resources'] + resources = decompress_obj(resources)['resources'] # Test assertions self.assertThat(resources, matchers.HasLength(2)) @@ -324,7 +325,7 @@ class TestApis(TestEntityGraphUnitBase, TestConfiguration): ctx, resource_type=None, all_tenants=True) - resources = json.loads(resources)['resources'] + resources = decompress_obj(resources)['resources'] # Test assertions self.assertThat(resources, matchers.HasLength(7))