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
|
* 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
|
* 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
|
* query - (string, optional) a json query filter to filter the graph components. defaults to return all the graph
|
||||||
|
* all_tenants -
|
||||||
|
|
||||||
query expression
|
query expression
|
||||||
================
|
================
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
{
|
{
|
||||||
"get topology": "role:admin",
|
"get topology": "",
|
||||||
"get resource": "role:admin",
|
"get topology:all_tenants": "role:admin",
|
||||||
"list resources": "role:admin",
|
"get resource": "",
|
||||||
"list alarms": "role:admin",
|
"list resources": "",
|
||||||
"get rca": "role:admin",
|
"list alarms": "",
|
||||||
"template validate": "role:admin",
|
"list alarms:all_tenants": "role:admin",
|
||||||
"template list": "role:admin",
|
"get rca": "",
|
||||||
"template show": "role:admin"
|
"get rca:all_tenants": "role:admin",
|
||||||
|
"template validate": "",
|
||||||
|
"template list": "",
|
||||||
|
"template show": ""
|
||||||
}
|
}
|
@ -30,13 +30,17 @@ LOG = log.getLogger(__name__)
|
|||||||
class AlarmsController(RootRestController):
|
class AlarmsController(RootRestController):
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
def index(self, vitrage_id=None):
|
def index(self, vitrage_id, all_tenants='0'):
|
||||||
return self.post(vitrage_id)
|
return self.post(vitrage_id, all_tenants)
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
def post(self, vitrage_id):
|
def post(self, vitrage_id, all_tenants='0'):
|
||||||
enforce("list alarms", pecan.request.headers,
|
if all_tenants == '1':
|
||||||
pecan.request.enforcer, {})
|
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') %
|
LOG.info(_LI('returns list alarms with vitrage id %s') %
|
||||||
vitrage_id)
|
vitrage_id)
|
||||||
@ -45,16 +49,17 @@ class AlarmsController(RootRestController):
|
|||||||
if pecan.request.cfg.api.use_mock_file:
|
if pecan.request.cfg.api.use_mock_file:
|
||||||
return self.get_mock_data('alarms.sample.json')
|
return self.get_mock_data('alarms.sample.json')
|
||||||
else:
|
else:
|
||||||
return self._get_alarms(vitrage_id)
|
return self._get_alarms(vitrage_id, all_tenants)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception('failed to get alarms %s', e)
|
LOG.exception('failed to get alarms %s', e)
|
||||||
abort(404, str(e))
|
abort(404, str(e))
|
||||||
|
|
||||||
@staticmethod
|
@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,
|
alarms_json = pecan.request.client.call(pecan.request.context,
|
||||||
'get_alarms',
|
'get_alarms',
|
||||||
arg=vitrage_id)
|
vitrage_id=vitrage_id,
|
||||||
|
all_tenants=all_tenants)
|
||||||
LOG.info(alarms_json)
|
LOG.info(alarms_json)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -30,26 +30,31 @@ LOG = log.getLogger(__name__)
|
|||||||
|
|
||||||
class RCAController(RootRestController):
|
class RCAController(RootRestController):
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
def index(self, alarm_id):
|
def index(self, alarm_id, all_tenants='0'):
|
||||||
return self.get(alarm_id)
|
return self.get(alarm_id, all_tenants)
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
def get(self, alarm_id):
|
def get(self, alarm_id, all_tenants='0'):
|
||||||
enforce('get rca', pecan.request.headers,
|
if all_tenants == '1':
|
||||||
pecan.request.enforcer, {})
|
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)
|
LOG.info(_LI('received show rca with alarm id %s') % alarm_id)
|
||||||
if pecan.request.cfg.api.use_mock_file:
|
if pecan.request.cfg.api.use_mock_file:
|
||||||
return self.get_mock_data('rca.sample.json')
|
return self.get_mock_data('rca.sample.json')
|
||||||
else:
|
else:
|
||||||
return self.get_rca(alarm_id)
|
return self.get_rca(alarm_id, all_tenants)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_rca(alarm_id):
|
def get_rca(alarm_id, all_tenants):
|
||||||
try:
|
try:
|
||||||
graph_data = pecan.request.client.call(pecan.request.context,
|
graph_data = pecan.request.client.call(pecan.request.context,
|
||||||
'get_rca',
|
'get_rca',
|
||||||
root=alarm_id)
|
root=alarm_id,
|
||||||
|
all_tenants=all_tenants)
|
||||||
LOG.info(graph_data)
|
LOG.info(graph_data)
|
||||||
graph = json.loads(graph_data)
|
graph = json.loads(graph_data)
|
||||||
return graph
|
return graph
|
||||||
|
@ -34,9 +34,13 @@ LOG = log.getLogger(__name__)
|
|||||||
class TopologyController(RootRestController):
|
class TopologyController(RootRestController):
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
def post(self, depth, graph_type, query, root):
|
def post(self, depth, graph_type, query, root, all_tenants=0):
|
||||||
enforce("get topology", pecan.request.headers,
|
if all_tenants:
|
||||||
pecan.request.enforcer, {})
|
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 '
|
LOG.info(_LI('received get topology: depth->%(depth)s '
|
||||||
'graph_type->%(graph_type)s root->%(root)s') %
|
'graph_type->%(graph_type)s root->%(root)s') %
|
||||||
@ -50,18 +54,24 @@ class TopologyController(RootRestController):
|
|||||||
if pecan.request.cfg.api.use_mock_file:
|
if pecan.request.cfg.api.use_mock_file:
|
||||||
return self.get_mock_data('graph.sample.json', graph_type)
|
return self.get_mock_data('graph.sample.json', graph_type)
|
||||||
else:
|
else:
|
||||||
return self.get_graph(graph_type, depth, query, root)
|
return self.get_graph(graph_type, depth, query, root, all_tenants)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_graph(graph_type, depth, query, root):
|
def get_graph(graph_type, depth, query, root, all_tenants):
|
||||||
TopologyController._check_input_para(graph_type, depth, query, root)
|
TopologyController._check_input_para(graph_type,
|
||||||
|
depth,
|
||||||
|
query,
|
||||||
|
root,
|
||||||
|
all_tenants)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
graph_data = pecan.request.client.call(pecan.request.context,
|
graph_data = pecan.request.client.call(pecan.request.context,
|
||||||
'get_topology',
|
'get_topology',
|
||||||
graph_type=graph_type,
|
graph_type=graph_type,
|
||||||
depth=depth,
|
depth=depth,
|
||||||
query=query, root=root)
|
query=query,
|
||||||
|
root=root,
|
||||||
|
all_tenants=all_tenants)
|
||||||
LOG.info(graph_data)
|
LOG.info(graph_data)
|
||||||
graph = json.loads(graph_data)
|
graph = json.loads(graph_data)
|
||||||
if graph_type == 'graph':
|
if graph_type == 'graph':
|
||||||
@ -80,7 +90,7 @@ class TopologyController(RootRestController):
|
|||||||
abort(404, str(e))
|
abort(404, str(e))
|
||||||
|
|
||||||
@staticmethod
|
@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:
|
if graph_type == 'graph' and depth is not None and root is None:
|
||||||
LOG.exception("Graph-type 'graph' requires a 'root' with 'depth'")
|
LOG.exception("Graph-type 'graph' requires a 'root' with 'depth'")
|
||||||
abort(403, "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
|
import oslo_messaging
|
||||||
from oslo_service import service as os_service
|
from oslo_service import service as os_service
|
||||||
|
|
||||||
from vitrage.api_handler.apis import EntityGraphApis
|
from vitrage.api_handler.apis.alarm import AlarmApis
|
||||||
from vitrage.api_handler.apis import TemplateApis
|
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 messaging
|
||||||
from vitrage import rpc as vitrage_rpc
|
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,
|
target = oslo_messaging.Target(topic=self.conf.rpc_topic,
|
||||||
server=rabbit_hosts)
|
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)]
|
TemplateApis(self.scenario_repo.templates)]
|
||||||
|
|
||||||
server = vitrage_rpc.get_server(target, endpoints, transport)
|
server = vitrage_rpc.get_server(target, endpoints, transport)
|
||||||
|
@ -58,8 +58,49 @@ class GraphAlgorithm(object):
|
|||||||
"""
|
"""
|
||||||
pass
|
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
|
@abc.abstractmethod
|
||||||
def create_graph_from_matching_vertices(self,
|
def create_graph_from_matching_vertices(self,
|
||||||
vertex_attr_filter=None,
|
vertex_attr_filter=None,
|
||||||
query_dict=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
|
pass
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from networkx.algorithms import components
|
||||||
|
from networkx.algorithms import simple_paths
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from vitrage.graph.algo_driver.algorithm import GraphAlgorithm
|
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.nodes(data=True)),
|
||||||
str(self.graph._g.edges(data=True)))
|
str(self.graph._g.edges(data=True)))
|
||||||
return graph
|
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,
|
sample_timestamp=None,
|
||||||
update_timestamp=None,
|
update_timestamp=None,
|
||||||
is_placeholder=False,
|
is_placeholder=False,
|
||||||
|
project_id=None,
|
||||||
metadata=None):
|
metadata=None):
|
||||||
"""A builder to create a vertex
|
"""A builder to create a vertex
|
||||||
|
|
||||||
@ -50,6 +51,8 @@ def create_vertex(vitrage_id,
|
|||||||
:type metadata: dict
|
:type metadata: dict
|
||||||
:param is_placeholder:
|
:param is_placeholder:
|
||||||
:type is_placeholder: boolean
|
:type is_placeholder: boolean
|
||||||
|
:param project_id:
|
||||||
|
:type project_id: str
|
||||||
:return:
|
:return:
|
||||||
:rtype: Vertex
|
:rtype: Vertex
|
||||||
"""
|
"""
|
||||||
@ -63,7 +66,8 @@ def create_vertex(vitrage_id,
|
|||||||
VConst.UPDATE_TIMESTAMP: update_timestamp,
|
VConst.UPDATE_TIMESTAMP: update_timestamp,
|
||||||
VConst.SAMPLE_TIMESTAMP: sample_timestamp,
|
VConst.SAMPLE_TIMESTAMP: sample_timestamp,
|
||||||
VConst.IS_PLACEHOLDER: is_placeholder,
|
VConst.IS_PLACEHOLDER: is_placeholder,
|
||||||
VConst.VITRAGE_ID: vitrage_id
|
VConst.VITRAGE_ID: vitrage_id,
|
||||||
|
VConst.PROJECT_ID: project_id
|
||||||
}
|
}
|
||||||
if metadata:
|
if metadata:
|
||||||
properties.update(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 DatasourceProperties as DSProps
|
||||||
from vitrage.common.constants import EntityCategory
|
from vitrage.common.constants import EntityCategory
|
||||||
from vitrage.common.constants import SyncMode
|
from vitrage.common.constants import SyncMode
|
||||||
from vitrage.common.datetime_utils import utcnow
|
|
||||||
from vitrage.datasources.nagios import NAGIOS_DATASOURCE
|
from vitrage.datasources.nagios import NAGIOS_DATASOURCE
|
||||||
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
||||||
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
||||||
@ -134,7 +133,7 @@ class TestEntityGraphUnitBase(base.BaseTest):
|
|||||||
return events_list[0]
|
return events_list[0]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_alarm(vitrage_id, alarm_type):
|
def _create_alarm(vitrage_id, alarm_type, project_id=None):
|
||||||
return graph_utils.create_vertex(
|
return graph_utils.create_vertex(
|
||||||
vitrage_id,
|
vitrage_id,
|
||||||
entity_id=vitrage_id,
|
entity_id=vitrage_id,
|
||||||
@ -142,8 +141,23 @@ class TestEntityGraphUnitBase(base.BaseTest):
|
|||||||
entity_type=alarm_type,
|
entity_type=alarm_type,
|
||||||
entity_state='active',
|
entity_state='active',
|
||||||
is_deleted=False,
|
is_deleted=False,
|
||||||
sample_timestamp=utcnow(),
|
sample_timestamp=None,
|
||||||
is_placeholder=False,
|
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):
|
def _num_total_expected_vertices(self):
|
||||||
|
@ -100,7 +100,7 @@ class BaseApiTest(base.BaseTestCase):
|
|||||||
return volume
|
return volume
|
||||||
|
|
||||||
def _get_host(self):
|
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,
|
host = filter(lambda item: item[VProps.TYPE] == NOVA_HOST_DATASOURCE,
|
||||||
topology['nodes'])
|
topology['nodes'])
|
||||||
return host[0]
|
return host[0]
|
||||||
|
@ -34,7 +34,7 @@ class TestAodhAlarm(BaseAlarmsTest):
|
|||||||
self._create_ceilometer_alarm(self._find_instance_resource_id())
|
self._create_ceilometer_alarm(self._find_instance_resource_id())
|
||||||
|
|
||||||
# Calculate expected results
|
# 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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
@ -64,7 +64,7 @@ class TestAodhAlarm(BaseAlarmsTest):
|
|||||||
self._create_ceilometer_alarm()
|
self._create_ceilometer_alarm()
|
||||||
|
|
||||||
# Calculate expected results
|
# 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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
|
@ -33,7 +33,7 @@ class TestCinderVolume(BaseTopologyTest):
|
|||||||
num_volumes=self.NUM_VOLUME)
|
num_volumes=self.NUM_VOLUME)
|
||||||
|
|
||||||
# Calculate expected results
|
# 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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
|
@ -38,7 +38,7 @@ class TestHeatStack(BaseTopologyTest):
|
|||||||
self._create_stacks(num_stacks=self.NUM_STACKS)
|
self._create_stacks(num_stacks=self.NUM_STACKS)
|
||||||
|
|
||||||
# Calculate expected results
|
# 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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
|
@ -39,7 +39,7 @@ class TestNeutron(BaseTopologyTest):
|
|||||||
set_public_network=True)
|
set_public_network=True)
|
||||||
|
|
||||||
# Calculate expected results
|
# 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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
|
@ -31,7 +31,7 @@ class TestNova(BaseTopologyTest):
|
|||||||
self._create_entities(num_instances=self.NUM_INSTANCE)
|
self._create_entities(num_instances=self.NUM_INSTANCE)
|
||||||
|
|
||||||
# Calculate expected results
|
# 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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
|
@ -36,7 +36,7 @@ class TestStaticPhysical(BaseApiTest):
|
|||||||
self._create_switches()
|
self._create_switches()
|
||||||
|
|
||||||
# Calculate expected results
|
# 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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
|
@ -116,7 +116,7 @@ class TestRca(BaseRcaTest):
|
|||||||
self._create_alarm(
|
self._create_alarm(
|
||||||
resource_id=self._get_hostname(),
|
resource_id=self._get_hostname(),
|
||||||
alarm_name=RCA_ALARM_NAME)
|
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'],
|
self._validate_set_state(topology=topology['nodes'],
|
||||||
instances=instances)
|
instances=instances)
|
||||||
|
@ -34,10 +34,16 @@ class BaseTopologyTest(BaseApiTest):
|
|||||||
def _rollback_to_default(self):
|
def _rollback_to_default(self):
|
||||||
self._delete_entities()
|
self._delete_entities()
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data()
|
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):
|
def _create_entities(self, num_instances=0, num_volumes=0, end_sleep=3):
|
||||||
if num_instances > 0:
|
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
|
import vitrage_tempest_tests.tests.utils as utils
|
||||||
from vitrageclient.exc import ClientException
|
from vitrageclient.exc import ClientException
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
NOVA_QUERY = '{"and": [{"==": {"category": "RESOURCE"}},' \
|
NOVA_QUERY = '{"and": [{"==": {"category": "RESOURCE"}},' \
|
||||||
'{"==": {"is_deleted": false}},' \
|
'{"==": {"is_deleted": false}},' \
|
||||||
@ -60,7 +63,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
num_volumes=self.NUM_VOLUME)
|
num_volumes=self.NUM_VOLUME)
|
||||||
|
|
||||||
# Calculate expected results
|
# 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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
@ -96,7 +99,8 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
@ -126,7 +130,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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)
|
graph = self._create_graph_from_tree_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
@ -156,7 +160,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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)
|
graph = self._create_graph_from_tree_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1, host_edges=1)
|
host_entities=1, host_edges=1)
|
||||||
@ -181,7 +185,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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)
|
graph = self._create_graph_from_tree_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1, host_edges=1)
|
host_entities=1, host_edges=1)
|
||||||
@ -206,7 +210,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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)
|
graph = self._create_graph_from_tree_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
@ -224,6 +228,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
finally:
|
finally:
|
||||||
self._rollback_to_default()
|
self._rollback_to_default()
|
||||||
|
|
||||||
|
@unittest.skip("testing skipping")
|
||||||
def test_graph_with_root_and_depth_exclude_instance(self):
|
def test_graph_with_root_and_depth_exclude_instance(self):
|
||||||
"""tree_with_query
|
"""tree_with_query
|
||||||
|
|
||||||
@ -236,7 +241,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1, host_edges=1)
|
host_entities=1, host_edges=1)
|
||||||
@ -249,6 +254,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
finally:
|
finally:
|
||||||
self._rollback_to_default()
|
self._rollback_to_default()
|
||||||
|
|
||||||
|
@unittest.skip("testing skipping")
|
||||||
def test_graph_with_root_and_depth_include_instance(self):
|
def test_graph_with_root_and_depth_include_instance(self):
|
||||||
"""graph_with_root_and_depth_include_instance
|
"""graph_with_root_and_depth_include_instance
|
||||||
|
|
||||||
@ -261,7 +267,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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)
|
graph = self._create_graph_from_graph_dictionary(api_graph)
|
||||||
entities = self._entities_validation_data(
|
entities = self._entities_validation_data(
|
||||||
host_entities=1,
|
host_entities=1,
|
||||||
@ -292,7 +298,8 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
self.vitrage_client.topology.get(limit=2,
|
self.vitrage_client.topology.get(limit=2,
|
||||||
root='RESOURCE:openstack.cluster')
|
root='RESOURCE:openstack.cluster',
|
||||||
|
all_tenants=1)
|
||||||
except ClientException as e:
|
except ClientException as e:
|
||||||
self.assertEqual(403, e.code)
|
self.assertEqual(403, e.code)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -314,7 +321,7 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
api_graph = self.vitrage_client.topology.get(
|
||||||
query=self._graph_no_match_query())
|
query=self._graph_no_match_query(), all_tenants=1)
|
||||||
|
|
||||||
# Test Assertions
|
# Test Assertions
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -338,7 +345,9 @@ class TestTopology(BaseTopologyTest):
|
|||||||
|
|
||||||
# Calculate expected results
|
# Calculate expected results
|
||||||
api_graph = self.vitrage_client.topology.get(
|
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
|
# Test Assertions
|
||||||
self.assertEqual({}, api_graph)
|
self.assertEqual({}, api_graph)
|
||||||
|
Loading…
Reference in New Issue
Block a user