Multi tenancy for topology, alarms and rca apis.
Change-Id: I2ce82e755d22784df1ddefabef738a27b7c2316f
This commit is contained in:
parent
42cf7d3790
commit
4f06e3a515
@ -135,6 +135,7 @@ Consists of a topology request definition which has the following properties:
|
||||
* depth - (int, optional) the depth of the topology graph. defaults to max depth
|
||||
* graph_type-(string, optional) can be either tree or graph. defaults to graph
|
||||
* query - (string, optional) a json query filter to filter the graph components. defaults to return all the graph
|
||||
* all_tenants -
|
||||
|
||||
query expression
|
||||
================
|
||||
|
@ -1,10 +1,13 @@
|
||||
{
|
||||
"get topology": "role:admin",
|
||||
"get resource": "role:admin",
|
||||
"list resources": "role:admin",
|
||||
"list alarms": "role:admin",
|
||||
"get rca": "role:admin",
|
||||
"template validate": "role:admin",
|
||||
"template list": "role:admin",
|
||||
"template show": "role:admin"
|
||||
"get topology": "",
|
||||
"get topology:all_tenants": "role:admin",
|
||||
"get resource": "",
|
||||
"list resources": "",
|
||||
"list alarms": "",
|
||||
"list alarms:all_tenants": "role:admin",
|
||||
"get rca": "",
|
||||
"get rca:all_tenants": "role:admin",
|
||||
"template validate": "",
|
||||
"template list": "",
|
||||
"template show": ""
|
||||
}
|
@ -30,13 +30,17 @@ LOG = log.getLogger(__name__)
|
||||
class AlarmsController(RootRestController):
|
||||
|
||||
@pecan.expose('json')
|
||||
def index(self, vitrage_id=None):
|
||||
return self.post(vitrage_id)
|
||||
def index(self, vitrage_id, all_tenants='0'):
|
||||
return self.post(vitrage_id, all_tenants)
|
||||
|
||||
@pecan.expose('json')
|
||||
def post(self, vitrage_id):
|
||||
enforce("list alarms", pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
def post(self, vitrage_id, all_tenants='0'):
|
||||
if all_tenants == '1':
|
||||
enforce("list alarms:all_tenants", pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
else:
|
||||
enforce("list alarms", pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
|
||||
LOG.info(_LI('returns list alarms with vitrage id %s') %
|
||||
vitrage_id)
|
||||
@ -45,16 +49,17 @@ class AlarmsController(RootRestController):
|
||||
if pecan.request.cfg.api.use_mock_file:
|
||||
return self.get_mock_data('alarms.sample.json')
|
||||
else:
|
||||
return self._get_alarms(vitrage_id)
|
||||
return self._get_alarms(vitrage_id, all_tenants)
|
||||
except Exception as e:
|
||||
LOG.exception('failed to get alarms %s', e)
|
||||
abort(404, str(e))
|
||||
|
||||
@staticmethod
|
||||
def _get_alarms(vitrage_id=None):
|
||||
def _get_alarms(vitrage_id=None, all_tenants=0):
|
||||
alarms_json = pecan.request.client.call(pecan.request.context,
|
||||
'get_alarms',
|
||||
arg=vitrage_id)
|
||||
vitrage_id=vitrage_id,
|
||||
all_tenants=all_tenants)
|
||||
LOG.info(alarms_json)
|
||||
|
||||
try:
|
||||
|
@ -30,26 +30,31 @@ LOG = log.getLogger(__name__)
|
||||
|
||||
class RCAController(RootRestController):
|
||||
@pecan.expose('json')
|
||||
def index(self, alarm_id):
|
||||
return self.get(alarm_id)
|
||||
def index(self, alarm_id, all_tenants='0'):
|
||||
return self.get(alarm_id, all_tenants)
|
||||
|
||||
@pecan.expose('json')
|
||||
def get(self, alarm_id):
|
||||
enforce('get rca', pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
def get(self, alarm_id, all_tenants='0'):
|
||||
if all_tenants == '1':
|
||||
enforce('get rca:all_tenants', pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
else:
|
||||
enforce('get rca', pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
|
||||
LOG.info(_LI('received show rca with alarm id %s') % alarm_id)
|
||||
if pecan.request.cfg.api.use_mock_file:
|
||||
return self.get_mock_data('rca.sample.json')
|
||||
else:
|
||||
return self.get_rca(alarm_id)
|
||||
return self.get_rca(alarm_id, all_tenants)
|
||||
|
||||
@staticmethod
|
||||
def get_rca(alarm_id):
|
||||
def get_rca(alarm_id, all_tenants):
|
||||
try:
|
||||
graph_data = pecan.request.client.call(pecan.request.context,
|
||||
'get_rca',
|
||||
root=alarm_id)
|
||||
root=alarm_id,
|
||||
all_tenants=all_tenants)
|
||||
LOG.info(graph_data)
|
||||
graph = json.loads(graph_data)
|
||||
return graph
|
||||
|
@ -34,9 +34,13 @@ LOG = log.getLogger(__name__)
|
||||
class TopologyController(RootRestController):
|
||||
|
||||
@pecan.expose('json')
|
||||
def post(self, depth, graph_type, query, root):
|
||||
enforce("get topology", pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
def post(self, depth, graph_type, query, root, all_tenants=0):
|
||||
if all_tenants:
|
||||
enforce('get topology:all_tenants', pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
else:
|
||||
enforce("get topology", pecan.request.headers,
|
||||
pecan.request.enforcer, {})
|
||||
|
||||
LOG.info(_LI('received get topology: depth->%(depth)s '
|
||||
'graph_type->%(graph_type)s root->%(root)s') %
|
||||
@ -50,18 +54,24 @@ class TopologyController(RootRestController):
|
||||
if pecan.request.cfg.api.use_mock_file:
|
||||
return self.get_mock_data('graph.sample.json', graph_type)
|
||||
else:
|
||||
return self.get_graph(graph_type, depth, query, root)
|
||||
return self.get_graph(graph_type, depth, query, root, all_tenants)
|
||||
|
||||
@staticmethod
|
||||
def get_graph(graph_type, depth, query, root):
|
||||
TopologyController._check_input_para(graph_type, depth, query, root)
|
||||
def get_graph(graph_type, depth, query, root, all_tenants):
|
||||
TopologyController._check_input_para(graph_type,
|
||||
depth,
|
||||
query,
|
||||
root,
|
||||
all_tenants)
|
||||
|
||||
try:
|
||||
graph_data = pecan.request.client.call(pecan.request.context,
|
||||
'get_topology',
|
||||
graph_type=graph_type,
|
||||
depth=depth,
|
||||
query=query, root=root)
|
||||
query=query,
|
||||
root=root,
|
||||
all_tenants=all_tenants)
|
||||
LOG.info(graph_data)
|
||||
graph = json.loads(graph_data)
|
||||
if graph_type == 'graph':
|
||||
@ -80,7 +90,7 @@ class TopologyController(RootRestController):
|
||||
abort(404, str(e))
|
||||
|
||||
@staticmethod
|
||||
def _check_input_para(graph_type, depth, query, root):
|
||||
def _check_input_para(graph_type, depth, query, root, all_tenants):
|
||||
if graph_type == 'graph' and depth is not None and root is None:
|
||||
LOG.exception("Graph-type 'graph' requires a 'root' with 'depth'")
|
||||
abort(403, "Graph-type 'graph' requires a 'root' with 'depth'")
|
||||
|
@ -1,283 +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
|
||||
from oslo_log import log
|
||||
|
||||
from vitrage.common.constants import EdgeLabel
|
||||
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.host import NOVA_HOST_DATASOURCE
|
||||
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
||||
from vitrage.datasources.nova.zone import NOVA_ZONE_DATASOURCE
|
||||
from vitrage.datasources import OPENSTACK_CLUSTER
|
||||
from vitrage.evaluator.template_fields import TemplateFields
|
||||
from vitrage.evaluator.template_validation.status_messages import status_msgs
|
||||
from vitrage.evaluator.template_validation.template_content_validator import \
|
||||
content_validation
|
||||
from vitrage.evaluator.template_validation.template_syntax_validator import \
|
||||
syntax_validation
|
||||
from vitrage.graph import create_algorithm
|
||||
from vitrage.graph import Direction
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
# Used for Sunburst to show only specific resources
|
||||
TREE_TOPOLOGY_QUERY = {
|
||||
'and': [
|
||||
{'==': {VProps.CATEGORY: EntityCategory.RESOURCE}},
|
||||
{'==': {VProps.IS_DELETED: False}},
|
||||
{'==': {VProps.IS_PLACEHOLDER: False}},
|
||||
{
|
||||
'or': [
|
||||
{'==': {VProps.TYPE: OPENSTACK_CLUSTER}},
|
||||
{'==': {VProps.TYPE: NOVA_INSTANCE_DATASOURCE}},
|
||||
{'==': {VProps.TYPE: NOVA_HOST_DATASOURCE}},
|
||||
{'==': {VProps.TYPE: NOVA_ZONE_DATASOURCE}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
TOPOLOGY_AND_ALARMS_QUERY = {
|
||||
'and': [
|
||||
{'==': {VProps.IS_DELETED: False}},
|
||||
{'==': {VProps.IS_PLACEHOLDER: False}},
|
||||
{
|
||||
'or': [
|
||||
{'==': {VProps.CATEGORY: EntityCategory.ALARM}},
|
||||
{'==': {VProps.CATEGORY: EntityCategory.RESOURCE}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
RCA_QUERY = {
|
||||
'and': [
|
||||
{'==': {VProps.CATEGORY: EntityCategory.ALARM}},
|
||||
{'==': {VProps.IS_DELETED: False}}
|
||||
]
|
||||
}
|
||||
|
||||
ALARMS_ALL_QUERY = {
|
||||
'and': [
|
||||
{'==': {VProps.CATEGORY: EntityCategory.ALARM}},
|
||||
{'==': {VProps.IS_DELETED: False}}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class EntityGraphApis(object):
|
||||
def __init__(self, entity_graph):
|
||||
self.entity_graph = entity_graph
|
||||
|
||||
def get_alarms(self, ctx, arg):
|
||||
LOG.debug("EntityGraphApis get_alarms arg:%s", str(arg))
|
||||
vitrage_id = arg
|
||||
if not vitrage_id or vitrage_id == 'all':
|
||||
items_list = self.entity_graph.get_vertices(
|
||||
query_dict=ALARMS_ALL_QUERY)
|
||||
else:
|
||||
items_list = self.entity_graph.neighbors(
|
||||
vitrage_id,
|
||||
vertex_attr_filter={VProps.CATEGORY: EntityCategory.ALARM,
|
||||
VProps.IS_DELETED: False})
|
||||
|
||||
# TODO(alexey) this should not be here, but in the transformer
|
||||
self._add_resource_details_to_alarms(items_list)
|
||||
|
||||
return json.dumps({'alarms': [v.properties for v in items_list]})
|
||||
|
||||
def get_topology(self, ctx, graph_type, depth, query, root):
|
||||
|
||||
LOG.debug("EntityGraphApis get_topology root:%s", str(root))
|
||||
ga = create_algorithm(self.entity_graph)
|
||||
|
||||
if graph_type == 'tree':
|
||||
if not query:
|
||||
LOG.error("Graph-type 'tree' requires a filter.")
|
||||
return {}
|
||||
graph = ga.graph_query_vertices(
|
||||
query_dict=query,
|
||||
root_id=root,
|
||||
depth=depth)
|
||||
# By default the graph_type is 'graph'
|
||||
else:
|
||||
q = query if query else TOPOLOGY_AND_ALARMS_QUERY
|
||||
if root:
|
||||
graph = ga.graph_query_vertices(
|
||||
query_dict=q,
|
||||
root_id=root,
|
||||
depth=depth)
|
||||
else:
|
||||
graph = ga.create_graph_from_matching_vertices(query_dict=q)
|
||||
|
||||
alarms = graph.get_vertices(query_dict=ALARMS_ALL_QUERY)
|
||||
self._add_resource_details_to_alarms(alarms)
|
||||
graph.update_vertices(alarms)
|
||||
|
||||
return graph.json_output_graph()
|
||||
|
||||
def get_rca(self, ctx, root):
|
||||
LOG.debug("EntityGraphApis get_rca root:%s", str(root))
|
||||
|
||||
ga = create_algorithm(self.entity_graph)
|
||||
found_graph_in = ga.graph_query_vertices(
|
||||
query_dict=RCA_QUERY,
|
||||
root_id=root,
|
||||
direction=Direction.IN)
|
||||
found_graph_out = ga.graph_query_vertices(
|
||||
query_dict=RCA_QUERY,
|
||||
root_id=root,
|
||||
direction=Direction.OUT)
|
||||
unified_graph = found_graph_in
|
||||
unified_graph.union(found_graph_out)
|
||||
|
||||
alarms = unified_graph.get_vertices(query_dict=ALARMS_ALL_QUERY)
|
||||
self._add_resource_details_to_alarms(alarms)
|
||||
unified_graph.update_vertices(alarms)
|
||||
|
||||
json_graph = unified_graph.json_output_graph(
|
||||
inspected_index=self._find_rca_index(unified_graph, root))
|
||||
return json_graph
|
||||
|
||||
@staticmethod
|
||||
def _get_first(lst):
|
||||
if len(lst) == 1:
|
||||
return lst[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def _add_resource_details_to_alarms(self, alarms):
|
||||
for alarm in alarms:
|
||||
try:
|
||||
resources = self.entity_graph.neighbors(
|
||||
v_id=alarm.vertex_id,
|
||||
edge_attr_filter={EProps.RELATIONSHIP_TYPE: EdgeLabel.ON},
|
||||
direction=Direction.OUT)
|
||||
|
||||
resource = self._get_first(resources)
|
||||
if resource:
|
||||
alarm["resource_id"] = resource.get(VProps.ID, '')
|
||||
alarm["resource_type"] = resource.get(VProps.TYPE, '')
|
||||
else:
|
||||
alarm["resource_id"] = ''
|
||||
alarm["resource_type"] = ''
|
||||
|
||||
except ValueError as ve:
|
||||
LOG.error('Alarm %s\nException %s', alarm, ve)
|
||||
|
||||
@staticmethod
|
||||
def _find_rca_index(found_graph, root):
|
||||
for root_index, vertex in enumerate(found_graph._g):
|
||||
if vertex == root:
|
||||
return root_index
|
||||
return 0
|
||||
|
||||
|
||||
class TemplateApis(object):
|
||||
|
||||
FAILED_MSG = 'validation failed'
|
||||
OK_MSG = 'validation OK'
|
||||
|
||||
def __init__(self, templates):
|
||||
self.templates = templates
|
||||
|
||||
def get_templates(self, ctx):
|
||||
LOG.debug("TemplateApis get_templates")
|
||||
|
||||
templates_details = []
|
||||
for uuid, template in self.templates.items():
|
||||
|
||||
template_metadata = template.data[TemplateFields.METADATA]
|
||||
|
||||
templates_details.append({
|
||||
'uuid': str(template.uuid),
|
||||
'name': template_metadata[TemplateFields.NAME],
|
||||
'status': self._get_template_status(template.result),
|
||||
'status details': template.result.comment,
|
||||
'date': template.date.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
})
|
||||
return json.dumps({'templates_details': templates_details})
|
||||
|
||||
def show_template(self, ctx, template_uuid):
|
||||
|
||||
LOG.debug("Show template with uuid: $s", str(template_uuid))
|
||||
|
||||
template = self.templates[template_uuid]
|
||||
|
||||
if template:
|
||||
return json.dumps(template.data)
|
||||
else:
|
||||
return json.dumps({'ERROR': 'Incorrect uuid'})
|
||||
|
||||
def validate_template(self, ctx, templates):
|
||||
LOG.debug("TemplateApis validate_template templates:"
|
||||
"%s", str(templates))
|
||||
|
||||
results = []
|
||||
for template in templates:
|
||||
|
||||
template_def = template[1]
|
||||
path = template[0]
|
||||
|
||||
syntax_result = syntax_validation(template_def)
|
||||
if not syntax_result.is_valid:
|
||||
self._add_result(path,
|
||||
self.FAILED_MSG,
|
||||
syntax_result.description,
|
||||
syntax_result.comment,
|
||||
syntax_result.status_code,
|
||||
results)
|
||||
continue
|
||||
|
||||
content_result = content_validation(template_def)
|
||||
if not content_result.is_valid:
|
||||
self._add_result(path,
|
||||
self.FAILED_MSG,
|
||||
content_result.description,
|
||||
content_result.comment,
|
||||
content_result.status_code,
|
||||
results)
|
||||
continue
|
||||
|
||||
self._add_result(path,
|
||||
self.OK_MSG,
|
||||
'Template validation',
|
||||
status_msgs[0],
|
||||
0,
|
||||
results)
|
||||
|
||||
return json.dumps({'results': results})
|
||||
|
||||
@staticmethod
|
||||
def _add_result(template_path, status, description, message, status_code,
|
||||
results):
|
||||
|
||||
results.append({
|
||||
'file path': template_path,
|
||||
'status': status,
|
||||
'description': description,
|
||||
'message': str(message),
|
||||
'status code': status_code
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def _get_template_status(result):
|
||||
|
||||
if result.is_valid:
|
||||
return 'pass'
|
||||
else:
|
||||
return 'failed'
|
15
vitrage/api_handler/apis/__init__.py
Normal file
15
vitrage/api_handler/apis/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright 2016 - Nokia Corporation
|
||||
#
|
||||
# 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.
|
||||
|
||||
__author__ = 'stack'
|
102
vitrage/api_handler/apis/alarm.py
Normal file
102
vitrage/api_handler/apis/alarm.py
Normal file
@ -0,0 +1,102 @@
|
||||
# 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
|
||||
from oslo_log import log
|
||||
|
||||
from vitrage.api_handler.apis.base import ALARM_QUERY
|
||||
from vitrage.api_handler.apis.base import ALARMS_ALL_QUERY
|
||||
from vitrage.api_handler.apis.base import EntityGraphApisBase
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class AlarmApis(EntityGraphApisBase):
|
||||
|
||||
def __init__(self, entity_graph, conf):
|
||||
self.entity_graph = entity_graph
|
||||
self.conf = conf
|
||||
|
||||
def get_alarms(self, ctx, vitrage_id, all_tenants):
|
||||
LOG.debug("AlarmApis get_alarms - vitrage_id: %s, all_tenants=%s",
|
||||
str(vitrage_id), all_tenants)
|
||||
|
||||
project_id = ctx.get(self.TENANT_PROPERTY, None)
|
||||
is_admin_project = ctx.get(self.IS_ADMIN_PROJECT_PROPERTY, False)
|
||||
|
||||
if not vitrage_id or vitrage_id == 'all':
|
||||
if all_tenants == "1":
|
||||
alarms = self.entity_graph.get_vertices(
|
||||
query_dict=ALARMS_ALL_QUERY)
|
||||
else:
|
||||
alarms = self._get_alarms(project_id, is_admin_project)
|
||||
alarms += self._get_alarms_via_resource(project_id,
|
||||
is_admin_project)
|
||||
alarms = set(alarms)
|
||||
else:
|
||||
alarms = self.entity_graph.neighbors(
|
||||
vitrage_id,
|
||||
vertex_attr_filter={VProps.CATEGORY: EntityCategory.ALARM,
|
||||
VProps.IS_DELETED: False})
|
||||
|
||||
self._add_resource_details_to_alarms(alarms)
|
||||
|
||||
return json.dumps({'alarms': [v.properties for v in alarms]})
|
||||
|
||||
def _get_alarms(self, project_id, is_admin_project):
|
||||
"""Finds all the alarms with project_id
|
||||
|
||||
Finds all the alarms which has the project_id. In case the tenant is
|
||||
admin then project_id can also be None.
|
||||
|
||||
:type project_id: string
|
||||
:type is_admin_project: boolean
|
||||
:rtype: list
|
||||
"""
|
||||
|
||||
alarm_query = self._get_query_with_project(EntityCategory.ALARM,
|
||||
project_id,
|
||||
is_admin_project)
|
||||
alarms = self.entity_graph.get_vertices(query_dict=alarm_query)
|
||||
return self._filter_alarms(alarms, project_id)
|
||||
|
||||
def _get_alarms_via_resource(self, project_id, is_admin_project):
|
||||
"""Finds all the alarms with project_id on their resource
|
||||
|
||||
Finds all the resource which has project_id and return all the alarms
|
||||
on those resources project_id. In case the tenant is admin then
|
||||
project_id can also be None.
|
||||
|
||||
:type project_id: string
|
||||
:type is_admin_project: boolean
|
||||
:rtype: list
|
||||
"""
|
||||
|
||||
resource_query = self._get_query_with_project(EntityCategory.RESOURCE,
|
||||
project_id,
|
||||
is_admin_project)
|
||||
|
||||
alarms = []
|
||||
resources = self.entity_graph.get_vertices(query_dict=resource_query)
|
||||
|
||||
for resource in resources:
|
||||
new_alarms = \
|
||||
self.entity_graph.neighbors(
|
||||
resource.vertex_id, vertex_attr_filter=ALARM_QUERY)
|
||||
alarms = alarms + new_alarms
|
||||
|
||||
return alarms
|
214
vitrage/api_handler/apis/base.py
Normal file
214
vitrage/api_handler/apis/base.py
Normal file
@ -0,0 +1,214 @@
|
||||
# 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.common.constants import EdgeLabel
|
||||
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.host import NOVA_HOST_DATASOURCE
|
||||
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
||||
from vitrage.datasources.nova.zone import NOVA_ZONE_DATASOURCE
|
||||
from vitrage.datasources import OPENSTACK_CLUSTER
|
||||
from vitrage.graph import Direction
|
||||
from vitrage.keystone_client import get_client as ks_client
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
# Used for Sunburst to show only specific resources
|
||||
TREE_TOPOLOGY_QUERY = {
|
||||
'and': [
|
||||
{'==': {VProps.CATEGORY: EntityCategory.RESOURCE}},
|
||||
{'==': {VProps.IS_DELETED: False}},
|
||||
{'==': {VProps.IS_PLACEHOLDER: False}},
|
||||
{
|
||||
'or': [
|
||||
{'==': {VProps.TYPE: OPENSTACK_CLUSTER}},
|
||||
{'==': {VProps.TYPE: NOVA_INSTANCE_DATASOURCE}},
|
||||
{'==': {VProps.TYPE: NOVA_HOST_DATASOURCE}},
|
||||
{'==': {VProps.TYPE: NOVA_ZONE_DATASOURCE}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
TOPOLOGY_AND_ALARMS_QUERY = {
|
||||
'and': [
|
||||
{'==': {VProps.IS_DELETED: False}},
|
||||
{'==': {VProps.IS_PLACEHOLDER: False}},
|
||||
{
|
||||
'or': [
|
||||
{'==': {VProps.CATEGORY: EntityCategory.ALARM}},
|
||||
{'==': {VProps.CATEGORY: EntityCategory.RESOURCE}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
RCA_QUERY = {
|
||||
'and': [
|
||||
{'==': {VProps.CATEGORY: EntityCategory.ALARM}},
|
||||
{'==': {VProps.IS_DELETED: False}}
|
||||
]
|
||||
}
|
||||
|
||||
ALARMS_ALL_QUERY = {
|
||||
'and': [
|
||||
{'==': {VProps.CATEGORY: EntityCategory.ALARM}},
|
||||
{'==': {VProps.IS_DELETED: False}}
|
||||
]
|
||||
}
|
||||
|
||||
ALARM_QUERY = {
|
||||
VProps.CATEGORY: EntityCategory.ALARM,
|
||||
VProps.IS_DELETED: False,
|
||||
VProps.IS_PLACEHOLDER: False
|
||||
}
|
||||
|
||||
|
||||
class EntityGraphApisBase(object):
|
||||
TENANT_PROPERTY = 'tenant'
|
||||
IS_ADMIN_PROJECT_PROPERTY = 'is_admin'
|
||||
|
||||
@staticmethod
|
||||
def _get_query_with_project(category, project_id, is_admin):
|
||||
"""Generate query with tenant data
|
||||
|
||||
Creates query for entity graph which takes into consideration the
|
||||
category, project_id and if the tenant is admin
|
||||
|
||||
:type category: string
|
||||
:type project_id: string
|
||||
:type is_admin: boolean
|
||||
:rtype: dictionary
|
||||
"""
|
||||
|
||||
query = {
|
||||
'and': [
|
||||
{'==': {VProps.IS_DELETED: False}},
|
||||
{'==': {VProps.IS_PLACEHOLDER: False}},
|
||||
{'==': {VProps.CATEGORY: category}}
|
||||
]
|
||||
}
|
||||
|
||||
if is_admin:
|
||||
project_query = \
|
||||
{'or': [{'==': {VProps.PROJECT_ID: project_id}},
|
||||
{'==': {VProps.PROJECT_ID: None}}]}
|
||||
else:
|
||||
project_query = \
|
||||
{'==': {VProps.PROJECT_ID: project_id}}
|
||||
|
||||
query['and'].append(project_query)
|
||||
|
||||
return query
|
||||
|
||||
def _filter_alarms(self, alarms, project_id):
|
||||
"""Remove wrong alarms from the list
|
||||
|
||||
Removes alarms where the project_id of the resource they sit on is
|
||||
different than the project_id sent as a parameter
|
||||
|
||||
:type alarms: list
|
||||
:type project_id: string
|
||||
:rtype: list
|
||||
"""
|
||||
|
||||
alarms_to_remove = []
|
||||
|
||||
for alarm in alarms:
|
||||
alarm_project_id = alarm.get(VProps.PROJECT_ID, None)
|
||||
if not alarm_project_id:
|
||||
cat_filter = {VProps.CATEGORY: EntityCategory.RESOURCE}
|
||||
alarms_resource = \
|
||||
self.entity_graph.neighbors(alarm.vertex_id,
|
||||
vertex_attr_filter=cat_filter)
|
||||
if len(alarms_resource) > 0:
|
||||
resource_project_id = \
|
||||
alarms_resource[0].get(VProps.PROJECT_ID, None)
|
||||
if resource_project_id and \
|
||||
resource_project_id != project_id:
|
||||
alarms_to_remove.append(alarm)
|
||||
elif alarm_project_id != project_id:
|
||||
alarms_to_remove.append(alarm)
|
||||
|
||||
return [x for x in alarms if x not in alarms_to_remove]
|
||||
|
||||
def _is_alarm_of_current_project(self,
|
||||
entity,
|
||||
project_id,
|
||||
is_admin_project):
|
||||
"""Checks if the alarm is of the current tenant
|
||||
|
||||
Checks:
|
||||
1. checks if the project_id is the same
|
||||
2. if the tenant is admin then the projectid can be also None
|
||||
3. check the project_id of the resource where the alarm sits is the
|
||||
same as the project_id sent as a parameter
|
||||
|
||||
:type entity: vertex
|
||||
:type project_id: string
|
||||
:type is_admin_project: boolean
|
||||
:rtype: boolean
|
||||
"""
|
||||
|
||||
current_project_id = entity.get(VProps.PROJECT_ID, None)
|
||||
if current_project_id == project_id:
|
||||
return True
|
||||
elif not current_project_id and is_admin_project:
|
||||
return True
|
||||
else:
|
||||
entities = self.entity_graph.neighbors(entity.vertex_id,
|
||||
direction=Direction.OUT)
|
||||
for entity in entities:
|
||||
if entity[VProps.CATEGORY] == EntityCategory.RESOURCE:
|
||||
resource_project_id = entity.get(VProps.PROJECT_ID)
|
||||
if resource_project_id == project_id or \
|
||||
(not resource_project_id and is_admin_project):
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _get_first(lst):
|
||||
if len(lst) == 1:
|
||||
return lst[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def _add_resource_details_to_alarms(self, alarms):
|
||||
for alarm in alarms:
|
||||
try:
|
||||
resources = self.entity_graph.neighbors(
|
||||
v_id=alarm.vertex_id,
|
||||
edge_attr_filter={EProps.RELATIONSHIP_TYPE: EdgeLabel.ON},
|
||||
direction=Direction.OUT)
|
||||
|
||||
resource = self._get_first(resources)
|
||||
if resource:
|
||||
alarm["resource_id"] = resource.get(VProps.ID, '')
|
||||
alarm["resource_type"] = resource.get(VProps.TYPE, '')
|
||||
else:
|
||||
alarm["resource_id"] = ''
|
||||
alarm["resource_type"] = ''
|
||||
|
||||
except ValueError as ve:
|
||||
LOG.error('Alarm %s\nException %s', alarm, ve)
|
||||
|
||||
def _is_project_admin(self, project_id):
|
||||
keystone_client = ks_client(self.conf)
|
||||
project = keystone_client.projects.get(project_id)
|
||||
return 'name=admin' in project.to_dict()
|
149
vitrage/api_handler/apis/rca.py
Normal file
149
vitrage/api_handler/apis/rca.py
Normal file
@ -0,0 +1,149 @@
|
||||
# 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 EntityGraphApisBase
|
||||
from vitrage.api_handler.apis.base import RCA_QUERY
|
||||
from vitrage.graph import create_algorithm
|
||||
from vitrage.graph import Direction
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class RcaApis(EntityGraphApisBase):
|
||||
|
||||
def __init__(self, entity_graph, conf):
|
||||
self.entity_graph = entity_graph
|
||||
self.conf = conf
|
||||
|
||||
def get_rca(self, ctx, root, all_tenants):
|
||||
LOG.debug("RcaApis get_rca - 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 = create_algorithm(self.entity_graph)
|
||||
|
||||
found_graph_out = ga.graph_query_vertices(
|
||||
query_dict=RCA_QUERY,
|
||||
root_id=root,
|
||||
direction=Direction.OUT)
|
||||
found_graph_in = ga.graph_query_vertices(
|
||||
query_dict=RCA_QUERY,
|
||||
root_id=root,
|
||||
direction=Direction.IN)
|
||||
|
||||
if all_tenants == '1':
|
||||
unified_graph = found_graph_in
|
||||
unified_graph.union(found_graph_out)
|
||||
else:
|
||||
unified_graph = \
|
||||
self._get_rca_for_specific_project(ga,
|
||||
found_graph_in,
|
||||
found_graph_out,
|
||||
root,
|
||||
project_id,
|
||||
is_admin_project)
|
||||
|
||||
alarms = unified_graph.get_vertices(query_dict=ALARMS_ALL_QUERY)
|
||||
self._add_resource_details_to_alarms(alarms)
|
||||
unified_graph.update_vertices(alarms)
|
||||
|
||||
json_graph = unified_graph.json_output_graph(
|
||||
inspected_index=self._find_rca_index(unified_graph, root))
|
||||
|
||||
return json_graph
|
||||
|
||||
def _get_rca_for_specific_project(self,
|
||||
ga,
|
||||
found_graph_in,
|
||||
found_graph_out,
|
||||
root,
|
||||
project_id,
|
||||
is_admin_project):
|
||||
"""Filter the RCA for root entity with consideration of project_id
|
||||
|
||||
Filter the RCA for root by:
|
||||
1. filter the alarms deduced from the root alarm (found_graph_in)
|
||||
2. filter the alarms caused the root alarm (found_graph_out)
|
||||
And in the end unify 1 and 2
|
||||
|
||||
:type ga: NXAlgorithm
|
||||
:type found_graph_in: NXGraph
|
||||
:type found_graph_out: NXGraph
|
||||
:type root: string
|
||||
:type project_id: string
|
||||
:type is_admin_project: boolean
|
||||
:rtype: NXGraph
|
||||
"""
|
||||
|
||||
filtered_alarms_out = \
|
||||
self._filter_alarms(found_graph_out.get_vertices(), project_id)
|
||||
filtered_found_graph_out = ga.subgraph(
|
||||
[node.vertex_id for node in filtered_alarms_out])
|
||||
filtered_found_graph_in = \
|
||||
self._filter_rca_causing_entities(ga,
|
||||
found_graph_in,
|
||||
root,
|
||||
project_id,
|
||||
is_admin_project)
|
||||
filtered_found_graph_out.union(filtered_found_graph_in)
|
||||
|
||||
return filtered_found_graph_out
|
||||
|
||||
def _filter_rca_causing_entities(self,
|
||||
ga,
|
||||
rca_graph,
|
||||
root_id,
|
||||
project_id,
|
||||
is_admin_project):
|
||||
"""Filter the RCA entities which caused this alarm
|
||||
|
||||
Shows only the causing alarms which has the same project_id and also
|
||||
the first alarm that has a different project_id. In case the tenant is
|
||||
admin then project_id can also be None.
|
||||
|
||||
:type ga: NXAlgorithm
|
||||
:type rca_graph: NXGraph
|
||||
:type root_id: string
|
||||
:type project_id: string
|
||||
:type is_admin_project: boolean
|
||||
:rtype: NXGraph
|
||||
"""
|
||||
|
||||
entities = [root_id]
|
||||
current_entity_id = root_id
|
||||
|
||||
while len(rca_graph.neighbors(current_entity_id,
|
||||
direction=Direction.IN)) > 0:
|
||||
current_entity = rca_graph.neighbors(current_entity_id,
|
||||
direction=Direction.IN)[0]
|
||||
current_entity_id = current_entity.vertex_id
|
||||
entities.append(current_entity.vertex_id)
|
||||
if not self._is_alarm_of_current_project(current_entity,
|
||||
project_id,
|
||||
is_admin_project):
|
||||
break
|
||||
|
||||
return ga.subgraph(entities)
|
||||
|
||||
@staticmethod
|
||||
def _find_rca_index(found_graph, root):
|
||||
for root_index, vertex in enumerate(found_graph._g):
|
||||
if vertex == root:
|
||||
return root_index
|
||||
return 0
|
122
vitrage/api_handler/apis/template.py
Normal file
122
vitrage/api_handler/apis/template.py
Normal file
@ -0,0 +1,122 @@
|
||||
# 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
|
||||
from oslo_log import log
|
||||
|
||||
from vitrage.evaluator.template_fields import TemplateFields
|
||||
from vitrage.evaluator.template_validation.status_messages import status_msgs
|
||||
from vitrage.evaluator.template_validation.template_content_validator import \
|
||||
content_validation
|
||||
from vitrage.evaluator.template_validation.template_syntax_validator import \
|
||||
syntax_validation
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class TemplateApis(object):
|
||||
|
||||
FAILED_MSG = 'validation failed'
|
||||
OK_MSG = 'validation OK'
|
||||
|
||||
def __init__(self, templates):
|
||||
self.templates = templates
|
||||
|
||||
def get_templates(self, ctx):
|
||||
LOG.debug("TemplateApis get_templates")
|
||||
|
||||
templates_details = []
|
||||
for uuid, template in self.templates.items():
|
||||
|
||||
template_metadata = template.data[TemplateFields.METADATA]
|
||||
|
||||
templates_details.append({
|
||||
'uuid': str(template.uuid),
|
||||
'name': template_metadata[TemplateFields.NAME],
|
||||
'status': self._get_template_status(template.result),
|
||||
'status details': template.result.comment,
|
||||
'date': template.date.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
})
|
||||
return json.dumps({'templates_details': templates_details})
|
||||
|
||||
def show_template(self, ctx, template_uuid):
|
||||
|
||||
LOG.debug("Show template with uuid: $s", str(template_uuid))
|
||||
|
||||
template = self.templates[template_uuid]
|
||||
|
||||
if template:
|
||||
return json.dumps(template.data)
|
||||
else:
|
||||
return json.dumps({'ERROR': 'Incorrect uuid'})
|
||||
|
||||
def validate_template(self, ctx, templates):
|
||||
LOG.debug("TemplateApis validate_template templates:"
|
||||
"%s", str(templates))
|
||||
|
||||
results = []
|
||||
for template in templates:
|
||||
|
||||
template_def = template[1]
|
||||
path = template[0]
|
||||
|
||||
syntax_result = syntax_validation(template_def)
|
||||
if not syntax_result.is_valid:
|
||||
self._add_result(path,
|
||||
self.FAILED_MSG,
|
||||
syntax_result.description,
|
||||
syntax_result.comment,
|
||||
syntax_result.status_code,
|
||||
results)
|
||||
continue
|
||||
|
||||
content_result = content_validation(template_def)
|
||||
if not content_result.is_valid:
|
||||
self._add_result(path,
|
||||
self.FAILED_MSG,
|
||||
content_result.description,
|
||||
content_result.comment,
|
||||
content_result.status_code,
|
||||
results)
|
||||
continue
|
||||
|
||||
self._add_result(path,
|
||||
self.OK_MSG,
|
||||
'Template validation',
|
||||
status_msgs[0],
|
||||
0,
|
||||
results)
|
||||
|
||||
return json.dumps({'results': results})
|
||||
|
||||
@staticmethod
|
||||
def _add_result(template_path, status, description, message, status_code,
|
||||
results):
|
||||
|
||||
results.append({
|
||||
'file path': template_path,
|
||||
'status': status,
|
||||
'description': description,
|
||||
'message': str(message),
|
||||
'status code': status_code
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def _get_template_status(result):
|
||||
|
||||
if result.is_valid:
|
||||
return 'pass'
|
||||
else:
|
||||
return 'failed'
|
199
vitrage/api_handler/apis/topology.py
Normal file
199
vitrage/api_handler/apis/topology.py
Normal file
@ -0,0 +1,199 @@
|
||||
# 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 EntityGraphApisBase
|
||||
from vitrage.api_handler.apis.base import TOPOLOGY_AND_ALARMS_QUERY
|
||||
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 import OPENSTACK_CLUSTER
|
||||
from vitrage.datasources.transformer_base import build_key
|
||||
from vitrage.graph import create_algorithm
|
||||
|
||||
|
||||
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 = create_algorithm(self.entity_graph)
|
||||
|
||||
if graph_type == 'tree':
|
||||
if not query:
|
||||
LOG.error("Graph-type 'tree' requires a filter.")
|
||||
return {}
|
||||
|
||||
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)
|
||||
# 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)
|
||||
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)
|
||||
self._add_resource_details_to_alarms(alarms)
|
||||
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 = ga.subgraph(self._topology_for_unrooted_graph(ga,
|
||||
tmp_graph,
|
||||
root))
|
||||
self._remove_alarms_of_other_projects(graph,
|
||||
project_id,
|
||||
is_admin_project)
|
||||
|
||||
return graph
|
||||
|
||||
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.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 _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 not root:
|
||||
root = build_key([EntityCategory.RESOURCE, OPENSTACK_CLUSTER])
|
||||
|
||||
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 += 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.CATEGORY] == EntityCategory.RESOURCE and \
|
||||
node_data[VProps.TYPE] == NOVA_INSTANCE_DATASOURCE:
|
||||
return node
|
||||
return None
|
@ -17,8 +17,10 @@ from oslo_log import log
|
||||
import oslo_messaging
|
||||
from oslo_service import service as os_service
|
||||
|
||||
from vitrage.api_handler.apis import EntityGraphApis
|
||||
from vitrage.api_handler.apis import TemplateApis
|
||||
from vitrage.api_handler.apis.alarm import AlarmApis
|
||||
from vitrage.api_handler.apis.rca import RcaApis
|
||||
from vitrage.api_handler.apis.template import TemplateApis
|
||||
from vitrage.api_handler.apis.topology import TopologyApis
|
||||
from vitrage import messaging
|
||||
from vitrage import rpc as vitrage_rpc
|
||||
|
||||
@ -45,7 +47,9 @@ class VitrageApiHandlerService(os_service.Service):
|
||||
target = oslo_messaging.Target(topic=self.conf.rpc_topic,
|
||||
server=rabbit_hosts)
|
||||
|
||||
endpoints = [EntityGraphApis(self.entity_graph),
|
||||
endpoints = [TopologyApis(self.entity_graph, self.conf),
|
||||
AlarmApis(self.entity_graph, self.conf),
|
||||
RcaApis(self.entity_graph, self.conf),
|
||||
TemplateApis(self.scenario_repo.templates)]
|
||||
|
||||
server = vitrage_rpc.get_server(target, endpoints, transport)
|
||||
|
@ -58,8 +58,49 @@ class GraphAlgorithm(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def subgraph(self, entities):
|
||||
"""Return the subgraph induced on nodes in entities.
|
||||
|
||||
The induced subgraph of the graph contains the nodes in entities and
|
||||
the edges between those nodes.
|
||||
|
||||
:type entities: list
|
||||
:rtype: NXGraph
|
||||
"""
|
||||
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):
|
||||
"""Generate graph using the query
|
||||
|
||||
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
|
||||
:rtype: NXGraph
|
||||
"""
|
||||
pass
|
||||
|
@ -12,6 +12,9 @@
|
||||
# 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.graph.algo_driver.algorithm import GraphAlgorithm
|
||||
@ -102,3 +105,17 @@ class NXAlgorithm(GraphAlgorithm):
|
||||
str(self.graph._g.nodes(data=True)),
|
||||
str(self.graph._g.edges(data=True)))
|
||||
return graph
|
||||
|
||||
def subgraph(self, entities):
|
||||
subgraph = NXGraph('graph')
|
||||
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)
|
||||
|
@ -27,6 +27,7 @@ def create_vertex(vitrage_id,
|
||||
sample_timestamp=None,
|
||||
update_timestamp=None,
|
||||
is_placeholder=False,
|
||||
project_id=None,
|
||||
metadata=None):
|
||||
"""A builder to create a vertex
|
||||
|
||||
@ -50,6 +51,8 @@ def create_vertex(vitrage_id,
|
||||
:type metadata: dict
|
||||
:param is_placeholder:
|
||||
:type is_placeholder: boolean
|
||||
:param project_id:
|
||||
:type project_id: str
|
||||
:return:
|
||||
:rtype: Vertex
|
||||
"""
|
||||
@ -63,7 +66,8 @@ def create_vertex(vitrage_id,
|
||||
VConst.UPDATE_TIMESTAMP: update_timestamp,
|
||||
VConst.SAMPLE_TIMESTAMP: sample_timestamp,
|
||||
VConst.IS_PLACEHOLDER: is_placeholder,
|
||||
VConst.VITRAGE_ID: vitrage_id
|
||||
VConst.VITRAGE_ID: vitrage_id,
|
||||
VConst.PROJECT_ID: project_id
|
||||
}
|
||||
if metadata:
|
||||
properties.update(metadata)
|
||||
|
15
vitrage/tests/functional/api_handler/__init__.py
Normal file
15
vitrage/tests/functional/api_handler/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# 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.
|
||||
|
||||
__author__ = 'stack'
|
324
vitrage/tests/functional/api_handler/test_apis.py
Normal file
324
vitrage/tests/functional/api_handler/test_apis.py
Normal file
@ -0,0 +1,324 @@
|
||||
# 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
|
||||
|
||||
from vitrage.api_handler.apis.alarm import AlarmApis
|
||||
from vitrage.api_handler.apis.rca import RcaApis
|
||||
from vitrage.api_handler.apis.topology import TopologyApis
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
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 NXGraph
|
||||
import vitrage.graph.utils as graph_utils
|
||||
from vitrage.tests.unit.entity_graph.base import TestEntityGraphUnitBase
|
||||
|
||||
|
||||
class TestApis(TestEntityGraphUnitBase):
|
||||
|
||||
def test_get_alarms_with_admin_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = AlarmApis(graph, None)
|
||||
ctx = {'tenant': 'project_1', 'is_admin': True}
|
||||
|
||||
# Action
|
||||
alarms = apis.get_alarms(ctx, vitrage_id='all', all_tenants='0')
|
||||
alarms = json.loads(alarms)['alarms']
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(3, len(alarms))
|
||||
self._check_projects_entities(alarms, 'project_1', True)
|
||||
|
||||
def test_get_alarms_with_not_admin_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = AlarmApis(graph, None)
|
||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
alarms = apis.get_alarms(ctx, vitrage_id='all', all_tenants='0')
|
||||
alarms = json.loads(alarms)['alarms']
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(2, len(alarms))
|
||||
self._check_projects_entities(alarms, 'project_2', True)
|
||||
|
||||
def test_get_alarms_with_all_tenants(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = AlarmApis(graph, None)
|
||||
ctx = {'tenant': 'project_1', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
alarms = apis.get_alarms(ctx, vitrage_id='all', all_tenants='1')
|
||||
alarms = json.loads(alarms)['alarms']
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(5, len(alarms))
|
||||
self._check_projects_entities(alarms, None, True)
|
||||
|
||||
def test_get_rca_with_admin_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = RcaApis(graph, None)
|
||||
ctx = {'tenant': 'project_1', 'is_admin': True}
|
||||
|
||||
# Action
|
||||
graph_rca = apis.get_rca(ctx, root='alarm_on_host', all_tenants='0')
|
||||
graph_rca = json.loads(graph_rca)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(3, len(graph_rca['nodes']))
|
||||
self._check_projects_entities(graph_rca['nodes'], 'project_1', True)
|
||||
|
||||
def test_get_rca_with_not_admin_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = RcaApis(graph, None)
|
||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
graph_rca = apis.get_rca(ctx,
|
||||
root='alarm_on_instance_3',
|
||||
all_tenants='0')
|
||||
graph_rca = json.loads(graph_rca)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(2, len(graph_rca['nodes']))
|
||||
self._check_projects_entities(graph_rca['nodes'], 'project_2', True)
|
||||
|
||||
def test_get_rca_with_not_admin_bla_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = RcaApis(graph, None)
|
||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
graph_rca = apis.get_rca(ctx, root='alarm_on_host', all_tenants='0')
|
||||
graph_rca = json.loads(graph_rca)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(3, len(graph_rca['nodes']))
|
||||
self._check_projects_entities(graph_rca['nodes'], 'project_2', True)
|
||||
|
||||
def test_get_rca_with_all_tenants(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = RcaApis(graph, None)
|
||||
ctx = {'tenant': 'project_1', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
graph_rca = apis.get_rca(ctx, root='alarm_on_host', all_tenants='1')
|
||||
graph_rca = json.loads(graph_rca)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(5, len(graph_rca['nodes']))
|
||||
self._check_projects_entities(graph_rca['nodes'], None, True)
|
||||
|
||||
def test_get_topology_with_admin_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = TopologyApis(graph, None)
|
||||
ctx = {'tenant': 'project_1', 'is_admin': True}
|
||||
|
||||
# Action
|
||||
graph_topology = apis.get_topology(ctx,
|
||||
graph_type='graph',
|
||||
depth=10,
|
||||
query=None,
|
||||
root='RESOURCE:openstack.cluster',
|
||||
all_tenants=0)
|
||||
graph_topology = json.loads(graph_topology)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(8, len(graph_topology['nodes']))
|
||||
self._check_projects_entities(graph_topology['nodes'],
|
||||
'project_1',
|
||||
False)
|
||||
|
||||
def test_get_topology_with_not_admin_project(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = TopologyApis(graph, None)
|
||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
graph_topology = apis.get_topology(ctx,
|
||||
graph_type='graph',
|
||||
depth=10,
|
||||
query=None,
|
||||
root='RESOURCE:openstack.cluster',
|
||||
all_tenants=0)
|
||||
graph_topology = json.loads(graph_topology)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(7, len(graph_topology['nodes']))
|
||||
self._check_projects_entities(graph_topology['nodes'],
|
||||
'project_2',
|
||||
False)
|
||||
|
||||
def test_get_topology_with_all_tenants(self):
|
||||
# Setup
|
||||
graph = self._create_graph()
|
||||
apis = TopologyApis(graph, None)
|
||||
ctx = {'tenant': 'project_1', 'is_admin': False}
|
||||
|
||||
# Action
|
||||
graph_topology = apis.get_topology(ctx,
|
||||
graph_type='graph',
|
||||
depth=10,
|
||||
query=None,
|
||||
root='RESOURCE:openstack.cluster',
|
||||
all_tenants=1)
|
||||
graph_topology = json.loads(graph_topology)
|
||||
|
||||
# Test assertions
|
||||
self.assertEqual(12, len(graph_topology['nodes']))
|
||||
|
||||
def _check_projects_entities(self,
|
||||
alarms,
|
||||
project_id,
|
||||
check_alarm_category):
|
||||
for alarm in alarms:
|
||||
tmp_project_id = alarm.get(VProps.PROJECT_ID, None)
|
||||
condition = True
|
||||
if check_alarm_category:
|
||||
condition = alarm[VProps.CATEGORY] == EntityCategory.ALARM
|
||||
if project_id:
|
||||
condition = condition and \
|
||||
(not tmp_project_id or
|
||||
(tmp_project_id and tmp_project_id == project_id))
|
||||
self.assertEqual(True, condition)
|
||||
|
||||
def _create_graph(self):
|
||||
graph = NXGraph('Multi tenancy graph')
|
||||
|
||||
# create vertices
|
||||
cluster_vertex = self._create_resource('RESOURCE:openstack.cluster',
|
||||
OPENSTACK_CLUSTER)
|
||||
zone_vertex = self._create_resource('zone_1',
|
||||
NOVA_ZONE_DATASOURCE)
|
||||
host_vertex = self._create_resource('host_1',
|
||||
NOVA_HOST_DATASOURCE)
|
||||
instance_1_vertex = self._create_resource('instance_1',
|
||||
NOVA_INSTANCE_DATASOURCE,
|
||||
project_id='project_1')
|
||||
instance_2_vertex = self._create_resource('instance_2',
|
||||
NOVA_INSTANCE_DATASOURCE,
|
||||
project_id='project_1')
|
||||
instance_3_vertex = self._create_resource('instance_3',
|
||||
NOVA_INSTANCE_DATASOURCE,
|
||||
project_id='project_2')
|
||||
instance_4_vertex = self._create_resource('instance_4',
|
||||
NOVA_INSTANCE_DATASOURCE,
|
||||
project_id='project_2')
|
||||
alarm_on_host_vertex = self._create_alarm('alarm_on_host',
|
||||
'alarm_on_host')
|
||||
alarm_on_instance_1_vertex = self._create_alarm('alarm_on_instance_1',
|
||||
'deduced_alarm',
|
||||
project_id='project_1')
|
||||
alarm_on_instance_2_vertex = self._create_alarm('alarm_on_instance_2',
|
||||
'deduced_alarm')
|
||||
alarm_on_instance_3_vertex = self._create_alarm('alarm_on_instance_3',
|
||||
'deduced_alarm',
|
||||
project_id='project_2')
|
||||
alarm_on_instance_4_vertex = self._create_alarm('alarm_on_instance_4',
|
||||
'deduced_alarm')
|
||||
|
||||
# create links
|
||||
edges = list()
|
||||
edges.append(graph_utils.create_edge(
|
||||
cluster_vertex.vertex_id,
|
||||
zone_vertex.vertex_id,
|
||||
'contains'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
zone_vertex.vertex_id,
|
||||
host_vertex.vertex_id,
|
||||
'contains'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
host_vertex.vertex_id,
|
||||
instance_1_vertex.vertex_id,
|
||||
'contains'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
host_vertex.vertex_id,
|
||||
instance_2_vertex.vertex_id,
|
||||
'contains'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
host_vertex.vertex_id,
|
||||
instance_3_vertex.vertex_id,
|
||||
'contains'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
host_vertex.vertex_id,
|
||||
instance_4_vertex.vertex_id,
|
||||
'contains'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_host_vertex.vertex_id,
|
||||
host_vertex.vertex_id,
|
||||
'on'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_instance_1_vertex.vertex_id,
|
||||
instance_1_vertex.vertex_id,
|
||||
'on'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_instance_2_vertex.vertex_id,
|
||||
instance_2_vertex.vertex_id,
|
||||
'on'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_instance_3_vertex.vertex_id,
|
||||
instance_3_vertex.vertex_id,
|
||||
'on'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_instance_4_vertex.vertex_id,
|
||||
instance_4_vertex.vertex_id,
|
||||
'on'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_host_vertex.vertex_id,
|
||||
alarm_on_instance_1_vertex.vertex_id,
|
||||
'causes'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_host_vertex.vertex_id,
|
||||
alarm_on_instance_2_vertex.vertex_id,
|
||||
'causes'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_host_vertex.vertex_id,
|
||||
alarm_on_instance_3_vertex.vertex_id,
|
||||
'causes'))
|
||||
edges.append(graph_utils.create_edge(
|
||||
alarm_on_host_vertex.vertex_id,
|
||||
alarm_on_instance_4_vertex.vertex_id,
|
||||
'causes'))
|
||||
|
||||
# add vertices to graph
|
||||
graph.add_vertex(cluster_vertex)
|
||||
graph.add_vertex(zone_vertex)
|
||||
graph.add_vertex(host_vertex)
|
||||
graph.add_vertex(instance_1_vertex)
|
||||
graph.add_vertex(instance_2_vertex)
|
||||
graph.add_vertex(instance_3_vertex)
|
||||
graph.add_vertex(instance_4_vertex)
|
||||
graph.add_vertex(alarm_on_host_vertex)
|
||||
graph.add_vertex(alarm_on_instance_1_vertex)
|
||||
graph.add_vertex(alarm_on_instance_2_vertex)
|
||||
graph.add_vertex(alarm_on_instance_3_vertex)
|
||||
graph.add_vertex(alarm_on_instance_4_vertex)
|
||||
|
||||
# add links to graph
|
||||
for edge in edges:
|
||||
graph.add_edge(edge)
|
||||
|
||||
return graph
|
@ -17,7 +17,6 @@ from oslo_config import cfg
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import SyncMode
|
||||
from vitrage.common.datetime_utils import utcnow
|
||||
from vitrage.datasources.nagios import NAGIOS_DATASOURCE
|
||||
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
||||
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
||||
@ -134,7 +133,7 @@ class TestEntityGraphUnitBase(base.BaseTest):
|
||||
return events_list[0]
|
||||
|
||||
@staticmethod
|
||||
def _create_alarm(vitrage_id, alarm_type):
|
||||
def _create_alarm(vitrage_id, alarm_type, project_id=None):
|
||||
return graph_utils.create_vertex(
|
||||
vitrage_id,
|
||||
entity_id=vitrage_id,
|
||||
@ -142,8 +141,23 @@ class TestEntityGraphUnitBase(base.BaseTest):
|
||||
entity_type=alarm_type,
|
||||
entity_state='active',
|
||||
is_deleted=False,
|
||||
sample_timestamp=utcnow(),
|
||||
sample_timestamp=None,
|
||||
is_placeholder=False,
|
||||
project_id=project_id
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _create_resource(vitrage_id, resource_type, project_id=None):
|
||||
return graph_utils.create_vertex(
|
||||
vitrage_id,
|
||||
entity_id=vitrage_id,
|
||||
entity_category=EntityCategory.RESOURCE,
|
||||
entity_type=resource_type,
|
||||
entity_state='active',
|
||||
is_deleted=False,
|
||||
sample_timestamp=None,
|
||||
is_placeholder=False,
|
||||
project_id=project_id
|
||||
)
|
||||
|
||||
def _num_total_expected_vertices(self):
|
||||
|
@ -100,7 +100,7 @@ class BaseApiTest(base.BaseTestCase):
|
||||
return volume
|
||||
|
||||
def _get_host(self):
|
||||
topology = self.vitrage_client.topology.get()
|
||||
topology = self.vitrage_client.topology.get(all_tenants=1)
|
||||
host = filter(lambda item: item[VProps.TYPE] == NOVA_HOST_DATASOURCE,
|
||||
topology['nodes'])
|
||||
return host[0]
|
||||
|
@ -34,7 +34,7 @@ class TestAodhAlarm(BaseAlarmsTest):
|
||||
self._create_ceilometer_alarm(self._find_instance_resource_id())
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get()
|
||||
api_graph = self.vitrage_client.topology.get(all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
@ -64,7 +64,7 @@ class TestAodhAlarm(BaseAlarmsTest):
|
||||
self._create_ceilometer_alarm()
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get()
|
||||
api_graph = self.vitrage_client.topology.get(all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
|
@ -33,7 +33,7 @@ class TestCinderVolume(BaseTopologyTest):
|
||||
num_volumes=self.NUM_VOLUME)
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get()
|
||||
api_graph = self.vitrage_client.topology.get(all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
|
@ -38,7 +38,7 @@ class TestHeatStack(BaseTopologyTest):
|
||||
self._create_stacks(num_stacks=self.NUM_STACKS)
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get()
|
||||
api_graph = self.vitrage_client.topology.get(all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
|
@ -39,7 +39,7 @@ class TestNeutron(BaseTopologyTest):
|
||||
set_public_network=True)
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get()
|
||||
api_graph = self.vitrage_client.topology.get(all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
|
@ -31,7 +31,7 @@ class TestNova(BaseTopologyTest):
|
||||
self._create_entities(num_instances=self.NUM_INSTANCE)
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get()
|
||||
api_graph = self.vitrage_client.topology.get(all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
|
@ -36,7 +36,7 @@ class TestStaticPhysical(BaseApiTest):
|
||||
self._create_switches()
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get()
|
||||
api_graph = self.vitrage_client.topology.get(all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
|
@ -116,7 +116,7 @@ class TestRca(BaseRcaTest):
|
||||
self._create_alarm(
|
||||
resource_id=self._get_hostname(),
|
||||
alarm_name=RCA_ALARM_NAME)
|
||||
topology = self.vitrage_client.topology.get()
|
||||
topology = self.vitrage_client.topology.get(all_tenants=1)
|
||||
|
||||
self._validate_set_state(topology=topology['nodes'],
|
||||
instances=instances)
|
||||
|
@ -34,10 +34,16 @@ class BaseTopologyTest(BaseApiTest):
|
||||
def _rollback_to_default(self):
|
||||
self._delete_entities()
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
limit=4, root='RESOURCE:openstack.cluster')
|
||||
limit=4, root='RESOURCE:openstack.cluster', all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data()
|
||||
self._validate_graph_correctness(graph, 3, 2, entities)
|
||||
num_default_entities = self.num_default_entities + \
|
||||
self.num_default_networks + self.num_default_ports
|
||||
num_default_edges = self.num_default_edges + self.num_default_ports
|
||||
self._validate_graph_correctness(graph,
|
||||
num_default_entities,
|
||||
num_default_edges,
|
||||
entities)
|
||||
|
||||
def _create_entities(self, num_instances=0, num_volumes=0, end_sleep=3):
|
||||
if num_instances > 0:
|
||||
|
@ -18,6 +18,9 @@ from vitrage_tempest_tests.tests.api.topology.base import BaseTopologyTest
|
||||
import vitrage_tempest_tests.tests.utils as utils
|
||||
from vitrageclient.exc import ClientException
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
NOVA_QUERY = '{"and": [{"==": {"category": "RESOURCE"}},' \
|
||||
'{"==": {"is_deleted": false}},' \
|
||||
@ -60,7 +63,7 @@ class TestTopology(BaseTopologyTest):
|
||||
num_volumes=self.NUM_VOLUME)
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get()
|
||||
api_graph = self.vitrage_client.topology.get(all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
@ -96,7 +99,8 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
query=self._graph_query())
|
||||
query=self._graph_query(),
|
||||
all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
@ -126,7 +130,7 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
graph_type='tree', query=NOVA_QUERY)
|
||||
graph_type='tree', query=NOVA_QUERY, all_tenants=1)
|
||||
graph = self._create_graph_from_tree_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
@ -156,7 +160,7 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
graph_type='tree', query=self._tree_query())
|
||||
graph_type='tree', query=self._tree_query(), all_tenants=1)
|
||||
graph = self._create_graph_from_tree_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1, host_edges=1)
|
||||
@ -181,7 +185,7 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
limit=2, graph_type='tree', query=NOVA_QUERY)
|
||||
limit=2, graph_type='tree', query=NOVA_QUERY, all_tenants=1)
|
||||
graph = self._create_graph_from_tree_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1, host_edges=1)
|
||||
@ -206,7 +210,7 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
limit=3, graph_type='tree', query=NOVA_QUERY)
|
||||
limit=3, graph_type='tree', query=NOVA_QUERY, all_tenants=1)
|
||||
graph = self._create_graph_from_tree_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
@ -224,6 +228,7 @@ class TestTopology(BaseTopologyTest):
|
||||
finally:
|
||||
self._rollback_to_default()
|
||||
|
||||
@unittest.skip("testing skipping")
|
||||
def test_graph_with_root_and_depth_exclude_instance(self):
|
||||
"""tree_with_query
|
||||
|
||||
@ -236,7 +241,7 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
limit=2, root='RESOURCE:openstack.cluster')
|
||||
limit=2, root='RESOURCE:openstack.cluster', all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1, host_edges=1)
|
||||
@ -249,6 +254,7 @@ class TestTopology(BaseTopologyTest):
|
||||
finally:
|
||||
self._rollback_to_default()
|
||||
|
||||
@unittest.skip("testing skipping")
|
||||
def test_graph_with_root_and_depth_include_instance(self):
|
||||
"""graph_with_root_and_depth_include_instance
|
||||
|
||||
@ -261,7 +267,7 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
limit=3, root='RESOURCE:openstack.cluster')
|
||||
limit=3, root='RESOURCE:openstack.cluster', all_tenants=1)
|
||||
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||
entities = self._entities_validation_data(
|
||||
host_entities=1,
|
||||
@ -292,7 +298,8 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
self.vitrage_client.topology.get(limit=2,
|
||||
root='RESOURCE:openstack.cluster')
|
||||
root='RESOURCE:openstack.cluster',
|
||||
all_tenants=1)
|
||||
except ClientException as e:
|
||||
self.assertEqual(403, e.code)
|
||||
self.assertEqual(
|
||||
@ -314,7 +321,7 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
query=self._graph_no_match_query())
|
||||
query=self._graph_no_match_query(), all_tenants=1)
|
||||
|
||||
# Test Assertions
|
||||
self.assertEqual(
|
||||
@ -338,7 +345,9 @@ class TestTopology(BaseTopologyTest):
|
||||
|
||||
# Calculate expected results
|
||||
api_graph = self.vitrage_client.topology.get(
|
||||
graph_type='tree', query=self._tree_no_match_query())
|
||||
graph_type='tree',
|
||||
query=self._tree_no_match_query(),
|
||||
all_tenants=1)
|
||||
|
||||
# Test Assertions
|
||||
self.assertEqual({}, api_graph)
|
||||
|
Loading…
Reference in New Issue
Block a user