vitrage/vitrage/api_handler/apis/topology.py

227 lines
9.1 KiB
Python

# 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
from vitrage.api_handler.apis.base import ALARMS_ALL_QUERY
from vitrage.api_handler.apis.base import EDGE_QUERY
from vitrage.api_handler.apis.base import EntityGraphApisBase
from vitrage.api_handler.apis.base import TOPOLOGY_AND_ALARMS_QUERY
from vitrage.common.constants import EdgeProperties as EProps
from vitrage.common.constants import EntityCategory
from vitrage.common.constants import VertexProperties as VProps
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
from vitrage.datasources.transformer_base\
import create_cluster_placeholder_vertex
from vitrage.entity_graph.processor import processor_utils
LOG = log.getLogger(__name__)
class TopologyApis(EntityGraphApisBase):
def __init__(self, entity_graph, conf):
self.entity_graph = entity_graph
self.conf = conf
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(self.TENANT_PROPERTY, None)
is_admin_project = ctx.get(self.IS_ADMIN_PROJECT_PROPERTY, False)
ga = self.entity_graph.algo
if graph_type == 'tree':
if not query:
LOG.error("Graph-type 'tree' requires a filter.")
raise Exception("Graph-type 'tree' requires a filter.")
current_query = query
if not all_tenants:
project_query = \
{'or': [{'==': {VProps.PROJECT_ID: project_id}},
{'==': {VProps.PROJECT_ID: None}}]}
current_query = {'and': [query, project_query]}
graph = ga.graph_query_vertices(query_dict=current_query,
root_id=root,
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})
else:
graph = self._get_topology_for_specific_project(
ga,
query,
project_id,
is_admin_project,
root)
alarms = graph.get_vertices(query_dict=ALARMS_ALL_QUERY)
graph.update_vertices(alarms)
return graph.json_output_graph()
def _get_topology_for_specific_project(self,
ga,
query,
project_id,
is_admin_project,
root):
"""Finds the topology in consideration with the project_id
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
:type root: string
:rtype: NXGraph
"""
if query:
q = query
else:
alarm_query = self._get_query_with_project(EntityCategory.ALARM,
project_id,
is_admin=True)
resource_query = \
self._get_query_with_project(EntityCategory.RESOURCE,
project_id,
is_admin_project)
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)
edge_query = {EProps.VITRAGE_IS_DELETED: False}
self._remove_unnecessary_elements(ga,
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._remove_alarms_of_other_projects(graph,
project_id,
is_admin_project)
def _remove_alarms_of_other_projects(self,
graph,
current_project_id,
is_admin_project):
"""Removes wrong alarms from the graph
Removes alarms of other tenants from the graph, In case the tenant is
admin then project_id can also be None.
:type graph: NXGraph
:type current_project_id: string
:type is_admin_project: boolean
"""
for alarm in graph.get_vertices(query_dict=ALARMS_ALL_QUERY):
if not alarm.get(VProps.PROJECT_ID, None):
cat_filter = {VProps.VITRAGE_CATEGORY: EntityCategory.RESOURCE}
resource_neighbors = \
self.entity_graph.neighbors(alarm.vertex_id,
vertex_attr_filter=cat_filter)
if len(resource_neighbors) > 0:
resource_proj_id = \
resource_neighbors[0].get(VProps.PROJECT_ID, None)
cond1 = is_admin_project and resource_proj_id and \
resource_proj_id != current_project_id
cond2 = not is_admin_project and \
(not resource_proj_id or
resource_proj_id != current_project_id)
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))
def _topology_for_unrooted_graph(self, ga, subgraph, root):
"""Finds topology for unrooted subgraph
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 = []
if root:
root_vertex = \
self.entity_graph.get_vertex(root)
else:
key_values_hash = processor_utils.get_defining_properties(
create_cluster_placeholder_vertex())
tmp_vertices = self.entity_graph.get_vertices_by_key(
key_values_hash)
if not tmp_vertices:
LOG.debug("No root vertex found")
return set(entities)
root_vertex = tmp_vertices[0]
local_connected_component_subgraphs = \
ga.connected_component_subgraphs(subgraph)
for component_subgraph in local_connected_component_subgraphs:
entities += 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)
@staticmethod
def _find_instance_in_graph(graph):
for node, node_data in graph.nodes_iter(data=True):
if node_data[VProps.VITRAGE_CATEGORY] == EntityCategory.RESOURCE and \
node_data[VProps.VITRAGE_TYPE] == NOVA_INSTANCE_DATASOURCE:
return node
return None