alarm and RCA history support
Change-Id: I03a303e79ee12a399d32db3bfebad98eef50b52d Depends-On: I67a3c35d7bc96d966a90e962f197beebca7e0298
This commit is contained in:
parent
4686377074
commit
114966027c
@ -11,6 +11,7 @@
|
|||||||
License for the specific language governing permissions and limitations
|
License for the specific language governing permissions and limitations
|
||||||
under the License.
|
under the License.
|
||||||
|
|
||||||
|
|
||||||
Vitrage API
|
Vitrage API
|
||||||
-----------
|
-----------
|
||||||
|
|
|
|
||||||
@ -504,6 +505,16 @@ Query Parameters
|
|||||||
|
|
||||||
vitrage_id - (string(255)) get alarm on this resource can be 'all' for all alarms.
|
vitrage_id - (string(255)) get alarm on this resource can be 'all' for all alarms.
|
||||||
|
|
||||||
|
Optional Parameters:
|
||||||
|
|
||||||
|
- limit - (int) maximum number of items to return, if limit=0 the method will return all matched items in alarms table.
|
||||||
|
- sort_by - (array of string(255)) array of attributes by which results should be sorted.
|
||||||
|
- sort_dirs - (array of string(255)) per-column array of sort_dirs,corresponding to sort_keys ('asc' or 'desc').
|
||||||
|
- filter_by - (array of string(255)) array of attributes by which results will be filtered
|
||||||
|
- filter_vals - (array of string(255)) per-column array of filter values corresponding to filter_by.
|
||||||
|
- next_page - (bool) if True will return next page when marker is given, if False will return previous page when marker is given, otherwise, returns first page if no marker was given.
|
||||||
|
- marker - ((string(255)) if None returns first page, else if vitrage_id is given and next_page is True, return next #limit results after marker, else, if next page is False, return #limit results before marker.
|
||||||
|
|
||||||
Request Body
|
Request Body
|
||||||
============
|
============
|
||||||
|
|
||||||
|
@ -18,14 +18,13 @@ import pecan
|
|||||||
|
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_utils.strutils import bool_from_string
|
|
||||||
from osprofiler import profiler
|
from osprofiler import profiler
|
||||||
from pecan.core import abort
|
from pecan.core import abort
|
||||||
|
|
||||||
from vitrage.api.controllers.rest import RootRestController
|
from vitrage.api.controllers.v1.alarm_base import BaseAlarmsController
|
||||||
from vitrage.api.controllers.v1 import count
|
from vitrage.api.controllers.v1 import count
|
||||||
|
from vitrage.api.controllers.v1 import history
|
||||||
from vitrage.api.policy import enforce
|
from vitrage.api.policy import enforce
|
||||||
from vitrage.common.constants import TenantProps
|
|
||||||
from vitrage.common.constants import VertexProperties as Vprops
|
from vitrage.common.constants import VertexProperties as Vprops
|
||||||
|
|
||||||
|
|
||||||
@ -35,44 +34,19 @@ LOG = log.getLogger(__name__)
|
|||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
@profiler.trace_cls("alarm controller",
|
@profiler.trace_cls("alarm controller",
|
||||||
info={}, hide_args=False, trace_private=False)
|
info={}, hide_args=False, trace_private=False)
|
||||||
class AlarmsController(RootRestController):
|
class AlarmsController(BaseAlarmsController):
|
||||||
count = count.CountsController()
|
count = count.CountsController()
|
||||||
|
history = history.HistoryController()
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
def get_all(self, **kwargs):
|
def get_all(self, **kwargs):
|
||||||
vitrage_id = kwargs.get(Vprops.VITRAGE_ID)
|
|
||||||
all_tenants = kwargs.get(TenantProps.ALL_TENANTS, False)
|
|
||||||
all_tenants = bool_from_string(all_tenants)
|
|
||||||
if all_tenants:
|
|
||||||
enforce("list alarms:all_tenants", pecan.request.headers,
|
|
||||||
pecan.request.enforcer, {})
|
|
||||||
else:
|
|
||||||
enforce("list alarms", pecan.request.headers,
|
|
||||||
pecan.request.enforcer, {})
|
|
||||||
|
|
||||||
LOG.info('returns list alarms with vitrage id %s', vitrage_id)
|
kwargs['only_active_alarms'] = True
|
||||||
|
|
||||||
try:
|
LOG.info('returns alarms list with vitrage id %s',
|
||||||
return self._get_alarms(vitrage_id, all_tenants)
|
kwargs.get(Vprops.VITRAGE_ID))
|
||||||
except Exception:
|
|
||||||
LOG.exception('Failed to get alarms.')
|
|
||||||
abort(404, 'Failed to get alarms.')
|
|
||||||
|
|
||||||
@staticmethod
|
return self._get_alarms(**kwargs)
|
||||||
def _get_alarms(vitrage_id=None, all_tenants=False):
|
|
||||||
alarms_json = pecan.request.client.call(pecan.request.context,
|
|
||||||
'get_alarms',
|
|
||||||
vitrage_id=vitrage_id,
|
|
||||||
all_tenants=all_tenants)
|
|
||||||
LOG.info(alarms_json)
|
|
||||||
|
|
||||||
try:
|
|
||||||
alarms_list = json.loads(alarms_json)['alarms']
|
|
||||||
return alarms_list
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
LOG.exception('Failed to open file.')
|
|
||||||
abort(404, 'Failed to get alarms')
|
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
def get(self, vitrage_id):
|
def get(self, vitrage_id):
|
||||||
|
79
vitrage/api/controllers/v1/alarm_base.py
Normal file
79
vitrage/api/controllers/v1/alarm_base.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# Copyright 2018 - 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.
|
||||||
|
|
||||||
|
|
||||||
|
import json
|
||||||
|
from oslo_log import log
|
||||||
|
from oslo_utils.strutils import bool_from_string
|
||||||
|
import pecan
|
||||||
|
from pecan.core import abort
|
||||||
|
|
||||||
|
from vitrage.api.controllers.rest import RootRestController
|
||||||
|
from vitrage.api.policy import enforce
|
||||||
|
from vitrage.common.constants import TenantProps
|
||||||
|
from vitrage.common.constants import VertexProperties as Vprops
|
||||||
|
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyBroadException
|
||||||
|
class BaseAlarmsController(RootRestController):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_alarms(**kwargs):
|
||||||
|
vitrage_id = kwargs.get(Vprops.VITRAGE_ID)
|
||||||
|
start = kwargs.get('start')
|
||||||
|
end = kwargs.get('end')
|
||||||
|
limit = kwargs.get('limit', 10000)
|
||||||
|
sort_by = kwargs.get('sort_by', ['start_timestamp', 'vitrage_id'])
|
||||||
|
sort_dirs = kwargs.get('sort_dirs', ['asc', 'asc'])
|
||||||
|
filter_by = kwargs.get('filter_by', [])
|
||||||
|
filter_vals = kwargs.get('filter_vals', [])
|
||||||
|
next_page = kwargs.get('next_page', True)
|
||||||
|
marker = kwargs.get('marker')
|
||||||
|
only_active_alarms = kwargs.get('only_active_alarms', False)
|
||||||
|
all_tenants = kwargs.get(TenantProps.ALL_TENANTS, False)
|
||||||
|
all_tenants = bool_from_string(all_tenants)
|
||||||
|
if all_tenants:
|
||||||
|
enforce("list alarms:all_tenants", pecan.request.headers,
|
||||||
|
pecan.request.enforcer, {})
|
||||||
|
else:
|
||||||
|
enforce("list alarms", pecan.request.headers,
|
||||||
|
pecan.request.enforcer, {})
|
||||||
|
|
||||||
|
alarms_json = \
|
||||||
|
pecan.request.client.call(pecan.request.context,
|
||||||
|
'get_alarms',
|
||||||
|
vitrage_id=vitrage_id,
|
||||||
|
all_tenants=all_tenants,
|
||||||
|
start=start,
|
||||||
|
end=end,
|
||||||
|
limit=limit,
|
||||||
|
sort_by=sort_by,
|
||||||
|
sort_dirs=sort_dirs,
|
||||||
|
filter_by=filter_by,
|
||||||
|
filter_vals=filter_vals,
|
||||||
|
next_page=next_page,
|
||||||
|
marker=marker,
|
||||||
|
only_active_alarms=only_active_alarms
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
alarms_list = json.loads(alarms_json)['alarms']
|
||||||
|
return alarms_list
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
LOG.exception('Failed to get alarms')
|
||||||
|
abort(404, 'Failed to get alarms')
|
32
vitrage/api/controllers/v1/history.py
Normal file
32
vitrage/api/controllers/v1/history.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Copyright 2018 - 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.
|
||||||
|
|
||||||
|
from oslo_log import log
|
||||||
|
import pecan
|
||||||
|
|
||||||
|
from vitrage.api.controllers.v1.alarm_base import BaseAlarmsController
|
||||||
|
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyBroadException
|
||||||
|
class HistoryController(BaseAlarmsController):
|
||||||
|
|
||||||
|
@pecan.expose('json')
|
||||||
|
def get_all(self, **kwargs):
|
||||||
|
|
||||||
|
LOG.info('returns history alarms')
|
||||||
|
|
||||||
|
return self._get_alarms(**kwargs)
|
@ -12,19 +12,20 @@
|
|||||||
# 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 dateutil import parser
|
||||||
import json
|
import json
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from osprofiler import profiler
|
from osprofiler import profiler
|
||||||
|
|
||||||
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.api_handler.apis.base import EntityGraphApisBase
|
||||||
from vitrage.common.constants import EntityCategory as ECategory
|
from vitrage.common.constants import EntityCategory as ECategory
|
||||||
|
from vitrage.common.constants import HistoryProps as HProps
|
||||||
from vitrage.common.constants import TenantProps
|
from vitrage.common.constants import TenantProps
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.entity_graph.mappings.operational_alarm_severity import \
|
from vitrage.entity_graph.mappings.operational_alarm_severity import \
|
||||||
OperationalAlarmSeverity
|
OperationalAlarmSeverity
|
||||||
|
from vitrage.storage import db_time
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -33,34 +34,29 @@ LOG = log.getLogger(__name__)
|
|||||||
info={}, hide_args=False, trace_private=False)
|
info={}, hide_args=False, trace_private=False)
|
||||||
class AlarmApis(EntityGraphApisBase):
|
class AlarmApis(EntityGraphApisBase):
|
||||||
|
|
||||||
def __init__(self, entity_graph, conf):
|
def __init__(self, entity_graph, conf, db):
|
||||||
self.entity_graph = entity_graph
|
self.entity_graph = entity_graph
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
|
self.db = db
|
||||||
|
|
||||||
def get_alarms(self, ctx, vitrage_id, all_tenants):
|
def get_alarms(self, ctx, vitrage_id, all_tenants, *args, **kwargs):
|
||||||
LOG.debug("AlarmApis get_alarms - vitrage_id: %s, all_tenants=%s",
|
|
||||||
str(vitrage_id), all_tenants)
|
|
||||||
|
|
||||||
project_id = ctx.get(TenantProps.TENANT, None)
|
kwargs = self._parse_kwargs(kwargs)
|
||||||
is_admin_project = ctx.get(TenantProps.IS_ADMIN, False)
|
|
||||||
|
|
||||||
if not vitrage_id or vitrage_id == 'all':
|
if not vitrage_id or vitrage_id == 'all':
|
||||||
if all_tenants:
|
if not all_tenants:
|
||||||
alarms = self.entity_graph.get_vertices(
|
kwargs['project_id'] = \
|
||||||
query_dict=ALARMS_ALL_QUERY)
|
ctx.get(TenantProps.TENANT, 'no-project')
|
||||||
else:
|
kwargs['is_admin_project'] = \
|
||||||
alarms = self._get_alarms(project_id, is_admin_project)
|
ctx.get(TenantProps.IS_ADMIN, False)
|
||||||
alarms += self._get_alarms_via_resource(project_id,
|
|
||||||
is_admin_project)
|
|
||||||
alarms = set(alarms)
|
|
||||||
else:
|
else:
|
||||||
query = {VProps.VITRAGE_CATEGORY: ECategory.ALARM,
|
kwargs.get('filter_by', []).append(VProps.VITRAGE_RESOURCE_ID)
|
||||||
VProps.VITRAGE_IS_DELETED: False}
|
kwargs.get('filter_vals', []).append(vitrage_id)
|
||||||
alarms = self.entity_graph.neighbors(vitrage_id,
|
|
||||||
vertex_attr_filter=query)
|
|
||||||
|
|
||||||
return json.dumps({'alarms': [v.properties for v in alarms]})
|
alarms = self._get_alarms(*args, **kwargs)
|
||||||
|
return json.dumps({'alarms': [v.payload for v in alarms]})
|
||||||
|
|
||||||
|
# TODO(annarez): add db support
|
||||||
def show_alarm(self, ctx, vitrage_id):
|
def show_alarm(self, ctx, vitrage_id):
|
||||||
LOG.debug('Show alarm with vitrage_id: %s', vitrage_id)
|
LOG.debug('Show alarm with vitrage_id: %s', vitrage_id)
|
||||||
|
|
||||||
@ -85,70 +81,58 @@ class AlarmApis(EntityGraphApisBase):
|
|||||||
is_admin_project = ctx.get(TenantProps.IS_ADMIN, False)
|
is_admin_project = ctx.get(TenantProps.IS_ADMIN, False)
|
||||||
|
|
||||||
if all_tenants:
|
if all_tenants:
|
||||||
alarms = self.entity_graph.get_vertices(
|
counts = self.db.history_facade.count_active_alarms()
|
||||||
query_dict=ALARMS_ALL_QUERY)
|
|
||||||
else:
|
else:
|
||||||
alarms = self._get_alarms(project_id, is_admin_project)
|
counts = self.db.history_facade.count_active_alarms(
|
||||||
alarms += self._get_alarms_via_resource(project_id,
|
project_id=project_id,
|
||||||
is_admin_project)
|
is_admin_project=is_admin_project)
|
||||||
alarms = set(alarms)
|
|
||||||
|
|
||||||
counts = {OperationalAlarmSeverity.SEVERE: 0,
|
|
||||||
OperationalAlarmSeverity.CRITICAL: 0,
|
|
||||||
OperationalAlarmSeverity.WARNING: 0,
|
|
||||||
OperationalAlarmSeverity.OK: 0,
|
|
||||||
OperationalAlarmSeverity.NA: 0}
|
|
||||||
|
|
||||||
for alarm in alarms:
|
|
||||||
severity = alarm.get(VProps.VITRAGE_OPERATIONAL_SEVERITY)
|
|
||||||
if severity:
|
|
||||||
try:
|
|
||||||
counts[severity] += 1
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return json.dumps(counts)
|
return json.dumps(counts)
|
||||||
|
|
||||||
def _get_alarms(self, project_id, is_admin_project):
|
def _get_alarms(self, *args, **kwargs):
|
||||||
"""Finds all the alarms with project_id
|
"""Finds all the alarms with project_id
|
||||||
|
|
||||||
Finds all the alarms which has the project_id. In case the tenant is
|
Finds all the alarms which has the project_id. In case the tenant is
|
||||||
admin then project_id can also be None.
|
admin then project_id can also be None.
|
||||||
|
|
||||||
:type project_id: string
|
|
||||||
:type is_admin_project: boolean
|
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
|
alarms = self.db.history_facade.get_alarms(*args, **kwargs)
|
||||||
alarm_query = self._get_query_with_project(ECategory.ALARM,
|
if not kwargs.get('only_active_alarms'):
|
||||||
project_id,
|
for alarm in alarms:
|
||||||
is_admin_project)
|
# change operational severity of ended alarms to 'OK'
|
||||||
alarms = self.entity_graph.get_vertices(query_dict=alarm_query)
|
# TODO(annarez): in next version use 'state'
|
||||||
return self._filter_alarms(alarms, project_id)
|
if alarm.end_timestamp <= db_time():
|
||||||
|
alarm.payload[VProps.VITRAGE_OPERATIONAL_SEVERITY] = \
|
||||||
def _get_alarms_via_resource(self, project_id, is_admin_project):
|
OperationalAlarmSeverity.OK
|
||||||
"""Finds all the alarms with project_id on their resource
|
for alarm in alarms:
|
||||||
|
alarm.payload[HProps.START_TIMESTAMP] = str(alarm.start_timestamp)
|
||||||
Finds all the resource which has project_id and return all the alarms
|
if alarm.end_timestamp <= db_time():
|
||||||
on those resources project_id. In case the tenant is admin then
|
alarm.payload[HProps.END_TIMESTAMP] = str(alarm.end_timestamp)
|
||||||
project_id can also be None.
|
|
||||||
|
|
||||||
:type project_id: string
|
|
||||||
:type is_admin_project: boolean
|
|
||||||
:rtype: list
|
|
||||||
"""
|
|
||||||
|
|
||||||
resource_query = self._get_query_with_project(ECategory.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
|
return alarms
|
||||||
|
|
||||||
|
def _parse_kwargs(self, kwargs):
|
||||||
|
kwargs = {k: v for k, v in kwargs.items() if v is not None}
|
||||||
|
|
||||||
|
if kwargs.get('start'):
|
||||||
|
kwargs['start'] = parser.parse(kwargs['start'])
|
||||||
|
if kwargs.get('end'):
|
||||||
|
kwargs['end'] = parser.parse(kwargs['end'])
|
||||||
|
if kwargs.get('sort_by') and type(kwargs.get('sort_by')) != list:
|
||||||
|
kwargs['sort_by'] = [kwargs.get('sort_by')]
|
||||||
|
if kwargs.get('sort_dirs') and type(kwargs.get('sort_dirs')) != list:
|
||||||
|
kwargs['sort_dirs'] = [kwargs.get('sort_dirs')]
|
||||||
|
if str(kwargs.get('next_page')).lower() == 'false':
|
||||||
|
kwargs['next_page'] = False
|
||||||
|
else:
|
||||||
|
kwargs['next_page'] = True
|
||||||
|
|
||||||
|
if kwargs.get('filter_by') and type(kwargs.get('filter_by')) != list:
|
||||||
|
kwargs['filter_by'] = [kwargs.get('filter_by')]
|
||||||
|
if kwargs.get('filter_vals') and type(
|
||||||
|
kwargs.get('filter_vals')) != list:
|
||||||
|
kwargs['filter_vals'] = [kwargs.get('filter_vals')]
|
||||||
|
|
||||||
|
return kwargs
|
||||||
|
@ -21,8 +21,6 @@ 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
|
||||||
from vitrage.datasources.nova.zone import NOVA_ZONE_DATASOURCE
|
from vitrage.datasources.nova.zone import NOVA_ZONE_DATASOURCE
|
||||||
from vitrage.datasources import OPENSTACK_CLUSTER
|
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__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -57,12 +55,6 @@ TOPOLOGY_AND_ALARMS_QUERY = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
RCA_QUERY = {
|
|
||||||
'and': [
|
|
||||||
{'==': {VProps.VITRAGE_CATEGORY: EntityCategory.ALARM}},
|
|
||||||
{'==': {VProps.VITRAGE_IS_DELETED: False}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
ALARMS_ALL_QUERY = {
|
ALARMS_ALL_QUERY = {
|
||||||
'and': [
|
'and': [
|
||||||
@ -71,12 +63,6 @@ ALARMS_ALL_QUERY = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
ALARM_QUERY = {
|
|
||||||
VProps.VITRAGE_CATEGORY: EntityCategory.ALARM,
|
|
||||||
VProps.VITRAGE_IS_DELETED: False,
|
|
||||||
VProps.VITRAGE_IS_PLACEHOLDER: False
|
|
||||||
}
|
|
||||||
|
|
||||||
EDGE_QUERY = {'==': {EProps.VITRAGE_IS_DELETED: False}}
|
EDGE_QUERY = {'==': {EProps.VITRAGE_IS_DELETED: False}}
|
||||||
|
|
||||||
RESOURCES_ALL_QUERY = {
|
RESOURCES_ALL_QUERY = {
|
||||||
@ -142,81 +128,3 @@ class EntityGraphApisBase(object):
|
|||||||
query_with_project_id = {'and': [project_query, query]}
|
query_with_project_id = {'and': [project_query, query]}
|
||||||
|
|
||||||
return query_with_project_id
|
return query_with_project_id
|
||||||
|
|
||||||
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.VITRAGE_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.VITRAGE_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 _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()
|
|
||||||
|
@ -15,14 +15,13 @@
|
|||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from osprofiler import profiler
|
from osprofiler import profiler
|
||||||
|
|
||||||
|
|
||||||
from vitrage.api_handler.apis.base import ALARMS_ALL_QUERY
|
|
||||||
from vitrage.api_handler.apis.base import EDGE_QUERY
|
|
||||||
from vitrage.api_handler.apis.base import EntityGraphApisBase
|
from vitrage.api_handler.apis.base import EntityGraphApisBase
|
||||||
from vitrage.api_handler.apis.base import RCA_QUERY
|
from vitrage.common.constants import HistoryProps as HProps
|
||||||
from vitrage.common.constants import TenantProps
|
from vitrage.common.constants import TenantProps
|
||||||
from vitrage.graph import Direction
|
from vitrage.graph.driver.networkx_graph import NXGraph
|
||||||
|
from vitrage.graph import Edge
|
||||||
|
from vitrage.graph import Vertex
|
||||||
|
from vitrage.storage import db_time
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -31,9 +30,10 @@ LOG = log.getLogger(__name__)
|
|||||||
info={}, hide_args=False, trace_private=False)
|
info={}, hide_args=False, trace_private=False)
|
||||||
class RcaApis(EntityGraphApisBase):
|
class RcaApis(EntityGraphApisBase):
|
||||||
|
|
||||||
def __init__(self, entity_graph, conf):
|
def __init__(self, entity_graph, conf, db):
|
||||||
self.entity_graph = entity_graph
|
self.entity_graph = entity_graph
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
|
self.db = db
|
||||||
|
|
||||||
def get_rca(self, ctx, root, all_tenants):
|
def get_rca(self, ctx, root, all_tenants):
|
||||||
LOG.debug("RcaApis get_rca - root: %s, all_tenants=%s",
|
LOG.debug("RcaApis get_rca - root: %s, all_tenants=%s",
|
||||||
@ -41,110 +41,31 @@ class RcaApis(EntityGraphApisBase):
|
|||||||
|
|
||||||
project_id = ctx.get(TenantProps.TENANT, None)
|
project_id = ctx.get(TenantProps.TENANT, None)
|
||||||
is_admin_project = ctx.get(TenantProps.IS_ADMIN, False)
|
is_admin_project = ctx.get(TenantProps.IS_ADMIN, False)
|
||||||
ga = self.entity_graph.algo
|
|
||||||
|
|
||||||
found_graph_out = ga.graph_query_vertices(root,
|
|
||||||
query_dict=RCA_QUERY,
|
|
||||||
direction=Direction.OUT,
|
|
||||||
edge_query_dict=EDGE_QUERY)
|
|
||||||
found_graph_in = ga.graph_query_vertices(root,
|
|
||||||
query_dict=RCA_QUERY,
|
|
||||||
direction=Direction.IN,
|
|
||||||
edge_query_dict=EDGE_QUERY)
|
|
||||||
|
|
||||||
if all_tenants:
|
if all_tenants:
|
||||||
unified_graph = found_graph_in
|
db_nodes, db_edges = self.db.history_facade.alarm_rca(root)
|
||||||
unified_graph.union(found_graph_out)
|
|
||||||
else:
|
else:
|
||||||
unified_graph = \
|
db_nodes, db_edges = self.db.history_facade.alarm_rca(
|
||||||
self._get_rca_for_specific_project(ga,
|
root,
|
||||||
found_graph_in,
|
project_id=project_id,
|
||||||
found_graph_out,
|
admin=is_admin_project)
|
||||||
root,
|
|
||||||
project_id,
|
|
||||||
is_admin_project)
|
|
||||||
|
|
||||||
alarms = unified_graph.get_vertices(query_dict=ALARMS_ALL_QUERY)
|
for n in db_nodes:
|
||||||
unified_graph.update_vertices(alarms)
|
n.payload[HProps.START_TIMESTAMP] = str(n.start_timestamp)
|
||||||
|
if n.end_timestamp <= db_time():
|
||||||
|
n.payload[HProps.END_TIMESTAMP] = str(n.end_timestamp)
|
||||||
|
|
||||||
json_graph = unified_graph.json_output_graph(
|
vertices = [Vertex(vertex_id=n.vitrage_id, properties=n.payload) for n
|
||||||
inspected_index=self._find_rca_index(unified_graph, root))
|
in db_nodes]
|
||||||
|
edges = [Edge(source_id=e.source_id, target_id=e.target_id,
|
||||||
|
label=e.label, properties=e.payload) for e in db_edges]
|
||||||
|
rca_graph = NXGraph(vertices=vertices, edges=edges)
|
||||||
|
|
||||||
|
json_graph = rca_graph.json_output_graph(
|
||||||
|
inspected_index=self._find_rca_index(rca_graph, root))
|
||||||
|
|
||||||
return json_graph
|
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
|
@staticmethod
|
||||||
def _find_rca_index(found_graph, root):
|
def _find_rca_index(found_graph, root):
|
||||||
for root_index, vertex in enumerate(found_graph._g):
|
for root_index, vertex in enumerate(found_graph._g):
|
||||||
|
@ -30,3 +30,6 @@ def purge_data():
|
|||||||
db.active_actions.delete()
|
db.active_actions.delete()
|
||||||
db.events.delete()
|
db.events.delete()
|
||||||
db.graph_snapshots.delete()
|
db.graph_snapshots.delete()
|
||||||
|
db.changes.delete()
|
||||||
|
db.edges.delete()
|
||||||
|
db.alarms.delete()
|
||||||
|
@ -31,6 +31,7 @@ class VertexProperties(ElementProperties):
|
|||||||
VITRAGE_AGGREGATED_SEVERITY = 'vitrage_aggregated_severity'
|
VITRAGE_AGGREGATED_SEVERITY = 'vitrage_aggregated_severity'
|
||||||
VITRAGE_OPERATIONAL_SEVERITY = 'vitrage_operational_severity'
|
VITRAGE_OPERATIONAL_SEVERITY = 'vitrage_operational_severity'
|
||||||
VITRAGE_RESOURCE_ID = 'vitrage_resource_id'
|
VITRAGE_RESOURCE_ID = 'vitrage_resource_id'
|
||||||
|
VITRAGE_RESOURCE_PROJECT_ID = 'vitrage_resource_project_id'
|
||||||
VITRAGE_CACHED_ID = 'vitrage_cached_id'
|
VITRAGE_CACHED_ID = 'vitrage_cached_id'
|
||||||
ID = 'id'
|
ID = 'id'
|
||||||
STATE = 'state'
|
STATE = 'state'
|
||||||
@ -49,6 +50,8 @@ class VertexProperties(ElementProperties):
|
|||||||
|
|
||||||
|
|
||||||
class EdgeProperties(ElementProperties):
|
class EdgeProperties(ElementProperties):
|
||||||
|
SOURCE_ID = 'source_id'
|
||||||
|
TARGET_ID = 'target_id'
|
||||||
RELATIONSHIP_TYPE = 'relationship_type'
|
RELATIONSHIP_TYPE = 'relationship_type'
|
||||||
|
|
||||||
|
|
||||||
@ -118,9 +121,13 @@ class NotifierEventTypes(object):
|
|||||||
DEACTIVATE_DEDUCED_ALARM_EVENT = 'vitrage.deduced_alarm.deactivate'
|
DEACTIVATE_DEDUCED_ALARM_EVENT = 'vitrage.deduced_alarm.deactivate'
|
||||||
ACTIVATE_ALARM_EVENT = 'vitrage.alarm.activate'
|
ACTIVATE_ALARM_EVENT = 'vitrage.alarm.activate'
|
||||||
DEACTIVATE_ALARM_EVENT = 'vitrage.alarm.deactivate'
|
DEACTIVATE_ALARM_EVENT = 'vitrage.alarm.deactivate'
|
||||||
|
CHANGE_IN_ALARM_EVENT = 'vitrage.alarm.change'
|
||||||
|
CHANGE_PROJECT_ID_EVENT = 'vitrage.alarm.change_project_id'
|
||||||
ACTIVATE_MARK_DOWN_EVENT = 'vitrage.mark_down.activate'
|
ACTIVATE_MARK_DOWN_EVENT = 'vitrage.mark_down.activate'
|
||||||
DEACTIVATE_MARK_DOWN_EVENT = 'vitrage.mark_down.deactivate'
|
DEACTIVATE_MARK_DOWN_EVENT = 'vitrage.mark_down.deactivate'
|
||||||
EXECUTE_EXTERNAL_ACTION = 'vitrage.execute_external_action'
|
EXECUTE_EXTERNAL_ACTION = 'vitrage.execute_external_action'
|
||||||
|
ACTIVATE_CAUSAL_RELATION = 'vitrage.causal_relationship.activate'
|
||||||
|
DEACTIVATE_CAUSAL_RELATION = 'vitrage.causal_relationship.deactivate'
|
||||||
|
|
||||||
|
|
||||||
class TemplateTopologyFields(object):
|
class TemplateTopologyFields(object):
|
||||||
@ -182,3 +189,11 @@ class TenantProps(object):
|
|||||||
ALL_TENANTS = 'all_tenants'
|
ALL_TENANTS = 'all_tenants'
|
||||||
TENANT = 'tenant'
|
TENANT = 'tenant'
|
||||||
IS_ADMIN = 'is_admin'
|
IS_ADMIN = 'is_admin'
|
||||||
|
|
||||||
|
|
||||||
|
class HistoryProps(object):
|
||||||
|
VITRAGE_ID = 'vitrage_id'
|
||||||
|
SOURCE_ID = 'source_id'
|
||||||
|
TARGET_ID = 'target_id'
|
||||||
|
START_TIMESTAMP = 'start_timestamp'
|
||||||
|
END_TIMESTAMP = 'end_timestamp'
|
||||||
|
@ -49,6 +49,29 @@ rules = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='list alarms history',
|
||||||
|
check_str=base.UNPROTECTED,
|
||||||
|
description='List the alarms history',
|
||||||
|
operations=[
|
||||||
|
{
|
||||||
|
'path': '/alarm/history',
|
||||||
|
'method': 'GET'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='list alarms history:all_tenants',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='List alarms history of all tenants '
|
||||||
|
'(if the user has the permissions)',
|
||||||
|
operations=[
|
||||||
|
{
|
||||||
|
'path': '/alarm/history',
|
||||||
|
'method': 'GET'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
name='get alarms count',
|
name='get alarms count',
|
||||||
check_str=base.UNPROTECTED,
|
check_str=base.UNPROTECTED,
|
||||||
|
@ -25,6 +25,7 @@ from vitrage.entity_graph import datasource_rpc as ds_rpc
|
|||||||
from vitrage.entity_graph import EVALUATOR_TOPIC
|
from vitrage.entity_graph import EVALUATOR_TOPIC
|
||||||
from vitrage.entity_graph.graph_persistency import GraphPersistency
|
from vitrage.entity_graph.graph_persistency import GraphPersistency
|
||||||
from vitrage.entity_graph.processor.notifier import GraphNotifier
|
from vitrage.entity_graph.processor.notifier import GraphNotifier
|
||||||
|
from vitrage.entity_graph.processor.notifier import PersistNotifier
|
||||||
from vitrage.entity_graph.processor.processor import Processor
|
from vitrage.entity_graph.processor.processor import Processor
|
||||||
from vitrage.entity_graph.scheduler import Scheduler
|
from vitrage.entity_graph.scheduler import Scheduler
|
||||||
from vitrage.entity_graph.workers import GraphWorkersManager
|
from vitrage.entity_graph.workers import GraphWorkersManager
|
||||||
@ -67,12 +68,16 @@ class VitrageGraphInit(object):
|
|||||||
self.persist.replay_events(self.graph, graph_snapshot.event_id)
|
self.persist.replay_events(self.graph, graph_snapshot.event_id)
|
||||||
self._recreate_transformers_id_cache()
|
self._recreate_transformers_id_cache()
|
||||||
LOG.info("%s vertices loaded", self.graph.num_vertices())
|
LOG.info("%s vertices loaded", self.graph.num_vertices())
|
||||||
|
self.subscribe_presist_notifier()
|
||||||
spawn(self._start_all_workers, is_snapshot=True)
|
spawn(self._start_all_workers, is_snapshot=True)
|
||||||
|
|
||||||
def _start_from_scratch(self):
|
def _start_from_scratch(self):
|
||||||
LOG.info('Starting for the first time')
|
LOG.info('Starting for the first time')
|
||||||
LOG.info('Clearing database active_actions')
|
LOG.info('Clearing database active_actions')
|
||||||
self.db.active_actions.delete()
|
self.db.active_actions.delete()
|
||||||
|
LOG.info('Disabling previously active alarms')
|
||||||
|
self.db.history_facade.disable_alarms_in_history()
|
||||||
|
self.subscribe_presist_notifier()
|
||||||
ds_rpc.get_all(
|
ds_rpc.get_all(
|
||||||
ds_rpc.create_rpc_client_instance(self.conf),
|
ds_rpc.create_rpc_client_instance(self.conf),
|
||||||
self.events_coordination,
|
self.events_coordination,
|
||||||
@ -118,6 +123,8 @@ class VitrageGraphInit(object):
|
|||||||
self.graph.subscribe(self.persist.persist_event,
|
self.graph.subscribe(self.persist.persist_event,
|
||||||
finalization=True)
|
finalization=True)
|
||||||
|
|
||||||
|
def subscribe_presist_notifier(self):
|
||||||
|
self.graph.subscribe(PersistNotifier(self.conf).notify_when_applicable)
|
||||||
|
|
||||||
PRIORITY_DELAY = 0.05
|
PRIORITY_DELAY = 0.05
|
||||||
|
|
||||||
|
@ -14,14 +14,16 @@
|
|||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
import oslo_messaging
|
import oslo_messaging
|
||||||
|
|
||||||
|
from vitrage.common.constants import EdgeLabel as ELabel
|
||||||
|
from vitrage.common.constants import EdgeProperties as EProps
|
||||||
from vitrage.common.constants import EntityCategory
|
from vitrage.common.constants import EntityCategory
|
||||||
from vitrage.common.constants import NotifierEventTypes
|
from vitrage.common.constants import NotifierEventTypes
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.evaluator.actions import evaluator_event_transformer as evaluator
|
from vitrage.evaluator.actions import evaluator_event_transformer as evaluator
|
||||||
|
from vitrage.graph.driver.networkx_graph import edge_copy
|
||||||
from vitrage.graph.driver.networkx_graph import vertex_copy
|
from vitrage.graph.driver.networkx_graph import vertex_copy
|
||||||
from vitrage.messaging import get_transport
|
from vitrage.messaging import get_transport
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -66,18 +68,9 @@ class GraphNotifier(object):
|
|||||||
return topics
|
return topics
|
||||||
|
|
||||||
def notify_when_applicable(self, before, current, is_vertex, graph):
|
def notify_when_applicable(self, before, current, is_vertex, graph):
|
||||||
"""Callback subscribed to driver.graph updates
|
|
||||||
|
|
||||||
:param is_vertex:
|
|
||||||
:param before: The graph element (vertex or edge) prior to the
|
|
||||||
change that happened. None if the element was just created.
|
|
||||||
:param current: The graph element (vertex or edge) after the
|
|
||||||
change that happened. Deleted elements should arrive with the
|
|
||||||
vitrage_is_deleted property set to True
|
|
||||||
:param graph: The graph
|
|
||||||
"""
|
|
||||||
curr = current
|
curr = current
|
||||||
notification_types = _get_notification_type(before, curr, is_vertex)
|
notification_types = \
|
||||||
|
self._get_notification_type(before, curr, is_vertex)
|
||||||
if not notification_types:
|
if not notification_types:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -88,8 +81,8 @@ class GraphNotifier(object):
|
|||||||
curr.properties[VProps.RESOURCE] = graph.get_vertex(
|
curr.properties[VProps.RESOURCE] = graph.get_vertex(
|
||||||
curr.get(VProps.VITRAGE_RESOURCE_ID))
|
curr.get(VProps.VITRAGE_RESOURCE_ID))
|
||||||
|
|
||||||
LOG.info('notification_types : %s', str(notification_types))
|
LOG.debug('notification_types : %s', str(notification_types))
|
||||||
LOG.info('notification properties : %s', curr.properties)
|
LOG.debug('notification properties : %s', curr.properties)
|
||||||
|
|
||||||
for notification_type in notification_types:
|
for notification_type in notification_types:
|
||||||
try:
|
try:
|
||||||
@ -100,61 +93,156 @@ class GraphNotifier(object):
|
|||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception('Cannot notify - %s.', notification_type)
|
LOG.exception('Cannot notify - %s.', notification_type)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_notification_type(before, current, is_vertex):
|
||||||
|
if not is_vertex:
|
||||||
|
return None
|
||||||
|
|
||||||
def _get_notification_type(before, current, is_vertex):
|
notification_types = [
|
||||||
if not is_vertex:
|
notification_type(
|
||||||
return None
|
before, current, _is_active_deduced_alarm,
|
||||||
|
NotifierEventTypes.ACTIVATE_DEDUCED_ALARM_EVENT,
|
||||||
def notification_type(is_active,
|
NotifierEventTypes.DEACTIVATE_DEDUCED_ALARM_EVENT),
|
||||||
activate_event_type,
|
notification_type(
|
||||||
deactivate_event_type):
|
before, current, _is_active_alarm,
|
||||||
if not is_active(before):
|
NotifierEventTypes.ACTIVATE_ALARM_EVENT,
|
||||||
if is_active(current):
|
NotifierEventTypes.DEACTIVATE_ALARM_EVENT),
|
||||||
return activate_event_type
|
notification_type(
|
||||||
else:
|
before, current, _is_marked_down,
|
||||||
if not is_active(current):
|
NotifierEventTypes.ACTIVATE_MARK_DOWN_EVENT,
|
||||||
return deactivate_event_type
|
NotifierEventTypes.DEACTIVATE_MARK_DOWN_EVENT),
|
||||||
|
]
|
||||||
notification_types = [
|
return list(filter(None, notification_types))
|
||||||
notification_type(_is_active_deduced_alarm,
|
|
||||||
NotifierEventTypes.ACTIVATE_DEDUCED_ALARM_EVENT,
|
|
||||||
NotifierEventTypes.DEACTIVATE_DEDUCED_ALARM_EVENT),
|
|
||||||
notification_type(_is_active_alarm,
|
|
||||||
NotifierEventTypes.ACTIVATE_ALARM_EVENT,
|
|
||||||
NotifierEventTypes.DEACTIVATE_ALARM_EVENT),
|
|
||||||
notification_type(_is_marked_down,
|
|
||||||
NotifierEventTypes.ACTIVATE_MARK_DOWN_EVENT,
|
|
||||||
NotifierEventTypes.DEACTIVATE_MARK_DOWN_EVENT),
|
|
||||||
]
|
|
||||||
return list(filter(None, notification_types))
|
|
||||||
|
|
||||||
|
|
||||||
def _is_active_deduced_alarm(vertex):
|
class PersistNotifier(object):
|
||||||
if not vertex:
|
"""Allows writing to message bus"""
|
||||||
|
def __init__(self, conf):
|
||||||
|
self.oslo_notifier = None
|
||||||
|
topics = [conf.persistency.persistor_topic]
|
||||||
|
self.oslo_notifier = oslo_messaging.Notifier(
|
||||||
|
get_transport(conf),
|
||||||
|
driver='messagingv2',
|
||||||
|
publisher_id='vitrage.graph',
|
||||||
|
topics=topics)
|
||||||
|
|
||||||
|
def notify_when_applicable(self, before, current, is_vertex, graph):
|
||||||
|
|
||||||
|
curr = current
|
||||||
|
notification_types = \
|
||||||
|
self._get_notification_type(before, curr, is_vertex)
|
||||||
|
if not notification_types:
|
||||||
|
return
|
||||||
|
|
||||||
|
# in case the event is on edge, add source and target ids to properties
|
||||||
|
# for history
|
||||||
|
if not is_vertex:
|
||||||
|
curr = edge_copy(
|
||||||
|
curr.source_id, curr.target_id, curr.label, curr.properties)
|
||||||
|
curr.properties[EProps.SOURCE_ID] = curr.source_id
|
||||||
|
curr.properties[EProps.TARGET_ID] = curr.target_id
|
||||||
|
|
||||||
|
LOG.debug('persist_notification_types : %s', str(notification_types))
|
||||||
|
LOG.debug('persist_notification properties : %s', curr.properties)
|
||||||
|
|
||||||
|
for notification_type in notification_types:
|
||||||
|
try:
|
||||||
|
self.oslo_notifier.info(
|
||||||
|
{},
|
||||||
|
notification_type,
|
||||||
|
curr.properties)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception('Cannot notify - %s.', notification_type)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_notification_type(before, current, is_vertex):
|
||||||
|
|
||||||
|
notification_types = [
|
||||||
|
notification_type(
|
||||||
|
before, current, _is_active_alarm,
|
||||||
|
NotifierEventTypes.ACTIVATE_ALARM_EVENT,
|
||||||
|
NotifierEventTypes.DEACTIVATE_ALARM_EVENT),
|
||||||
|
notification_type(
|
||||||
|
before, current, _is_active_causes_edge,
|
||||||
|
NotifierEventTypes.ACTIVATE_CAUSAL_RELATION,
|
||||||
|
NotifierEventTypes.DEACTIVATE_CAUSAL_RELATION),
|
||||||
|
NotifierEventTypes.CHANGE_IN_ALARM_EVENT if
|
||||||
|
_is_alarm_severity_change(before, current) else None,
|
||||||
|
NotifierEventTypes.CHANGE_PROJECT_ID_EVENT if
|
||||||
|
_is_resource_project_id_change(before, current) else None,
|
||||||
|
]
|
||||||
|
return list(filter(None, notification_types))
|
||||||
|
|
||||||
|
|
||||||
|
def notification_type(before,
|
||||||
|
current,
|
||||||
|
is_active,
|
||||||
|
activate_event_type,
|
||||||
|
deactivate_event_type):
|
||||||
|
if not is_active(before):
|
||||||
|
if is_active(current):
|
||||||
|
return activate_event_type
|
||||||
|
else:
|
||||||
|
if not is_active(current):
|
||||||
|
return deactivate_event_type
|
||||||
|
|
||||||
|
|
||||||
|
def _is_active_deduced_alarm(entity):
|
||||||
|
if not entity:
|
||||||
return False
|
return False
|
||||||
if vertex.get(VProps.VITRAGE_CATEGORY) == EntityCategory.ALARM and \
|
if entity.get(VProps.VITRAGE_CATEGORY) == EntityCategory.ALARM and \
|
||||||
vertex.get(VProps.VITRAGE_TYPE) == evaluator.VITRAGE_DATASOURCE:
|
entity.get(VProps.VITRAGE_TYPE) == evaluator.VITRAGE_DATASOURCE:
|
||||||
return _is_relevant_vertex(vertex)
|
return _is_relevant_vertex(entity)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _is_active_alarm(vertex):
|
def _is_active_alarm(entity):
|
||||||
if vertex and vertex.get(VProps.VITRAGE_CATEGORY) == EntityCategory.ALARM:
|
if entity and entity.get(VProps.VITRAGE_CATEGORY) == EntityCategory.ALARM:
|
||||||
return _is_relevant_vertex(vertex)
|
return _is_relevant_vertex(entity)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _is_marked_down(vertex):
|
def _is_marked_down(entity):
|
||||||
if not vertex:
|
if not entity:
|
||||||
return False
|
return False
|
||||||
if vertex.get(VProps.VITRAGE_CATEGORY) == EntityCategory.RESOURCE and \
|
if entity.get(VProps.VITRAGE_CATEGORY) == EntityCategory.RESOURCE and \
|
||||||
vertex.get(VProps.IS_MARKED_DOWN) is True:
|
entity.get(VProps.IS_MARKED_DOWN) is True:
|
||||||
return _is_relevant_vertex(vertex)
|
return _is_relevant_vertex(entity)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _is_relevant_vertex(vertex):
|
def _is_relevant_vertex(entity):
|
||||||
if vertex.get(VProps.VITRAGE_IS_DELETED, False) or \
|
if entity.get(VProps.VITRAGE_IS_DELETED, False) or \
|
||||||
vertex.get(VProps.VITRAGE_IS_PLACEHOLDER, False):
|
entity.get(VProps.VITRAGE_IS_PLACEHOLDER, False):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _is_active_causes_edge(entity):
|
||||||
|
if not entity:
|
||||||
|
return False
|
||||||
|
if not entity.get(EProps.RELATIONSHIP_TYPE) == ELabel.CAUSES:
|
||||||
|
return False
|
||||||
|
return not entity.get(EProps.VITRAGE_IS_DELETED)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_alarm_severity_change(before, curr):
|
||||||
|
if not (_is_active_alarm(before) and
|
||||||
|
_is_active_alarm(curr)):
|
||||||
|
return False
|
||||||
|
# returns true on activation, deactivation and severity change
|
||||||
|
if not before and curr \
|
||||||
|
or (before.get(VProps.VITRAGE_AGGREGATED_SEVERITY) !=
|
||||||
|
curr.get(VProps.VITRAGE_AGGREGATED_SEVERITY)):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _is_resource_project_id_change(before, curr):
|
||||||
|
if not (_is_active_alarm(before) and
|
||||||
|
_is_active_alarm(curr)):
|
||||||
|
return False
|
||||||
|
if (before.get(VProps.VITRAGE_RESOURCE_PROJECT_ID) !=
|
||||||
|
curr.get(VProps.VITRAGE_RESOURCE_PROJECT_ID)):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
from vitrage.common.constants import EntityCategory
|
from vitrage.common.constants import EntityCategory as ECategory
|
||||||
from vitrage.common.constants import GraphAction
|
from vitrage.common.constants import GraphAction
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.datasources.transformer_base import TransformerBase
|
from vitrage.datasources.transformer_base import TransformerBase
|
||||||
@ -318,22 +318,45 @@ class Processor(processor.ProcessorBase):
|
|||||||
result = self.entity_graph.get_vertices(attr)
|
result = self.entity_graph.get_vertices(attr)
|
||||||
event[TransformerBase.QUERY_RESULT] = result
|
event[TransformerBase.QUERY_RESULT] = result
|
||||||
|
|
||||||
@staticmethod
|
def _add_resource_details_to_alarm(self, vertex, neighbors):
|
||||||
def _add_resource_details_to_alarm(vertex, neighbors):
|
|
||||||
|
|
||||||
if not vertex.get(VProps.VITRAGE_CATEGORY) == EntityCategory.ALARM \
|
if not neighbors:
|
||||||
or not neighbors:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# for the possibility that alarm doesn't have resource
|
resource = None
|
||||||
vertex.properties[VProps.VITRAGE_RESOURCE_ID] = None
|
alarms = []
|
||||||
vertex.properties[VProps.VITRAGE_RESOURCE_TYPE] = None
|
|
||||||
|
|
||||||
for neighbor in neighbors:
|
if vertex.get(VProps.VITRAGE_CATEGORY) == ECategory.ALARM:
|
||||||
|
alarms = [vertex]
|
||||||
|
for neighbor in neighbors:
|
||||||
|
if neighbor.vertex.get(VProps.VITRAGE_CATEGORY) ==\
|
||||||
|
ECategory.RESOURCE:
|
||||||
|
resource = neighbor.vertex
|
||||||
|
elif vertex.get(VProps.VITRAGE_CATEGORY) == ECategory.RESOURCE:
|
||||||
|
resource = vertex
|
||||||
|
for neighbor in neighbors:
|
||||||
|
if neighbor.vertex.get(VProps.VITRAGE_CATEGORY) == \
|
||||||
|
ECategory.ALARM:
|
||||||
|
alarms.append(neighbor.vertex)
|
||||||
|
|
||||||
if neighbor.vertex.get(VProps.VITRAGE_CATEGORY) == \
|
for alarm in alarms:
|
||||||
EntityCategory.RESOURCE:
|
if not resource:
|
||||||
vertex.properties[VProps.VITRAGE_RESOURCE_ID] = \
|
self.add_resource_details(alarm, None, None, None)
|
||||||
neighbor.vertex.vertex_id
|
continue
|
||||||
vertex.properties[VProps.VITRAGE_RESOURCE_TYPE] = \
|
|
||||||
neighbor.vertex.get(VProps.VITRAGE_TYPE)
|
project_id = resource.get(VProps.PROJECT_ID)
|
||||||
|
if not project_id:
|
||||||
|
n_vertex = self.entity_graph.get_vertex(resource.vertex_id)
|
||||||
|
project_id = n_vertex.get(VProps.PROJECT_ID) \
|
||||||
|
if n_vertex else None
|
||||||
|
self.add_resource_details(
|
||||||
|
alarm,
|
||||||
|
r_id=resource.vertex_id,
|
||||||
|
r_type=resource.get(VProps.VITRAGE_TYPE),
|
||||||
|
r_project_id=project_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_resource_details(alarm, r_id, r_type, r_project_id):
|
||||||
|
alarm[VProps.VITRAGE_RESOURCE_ID] = r_id
|
||||||
|
alarm[VProps.VITRAGE_RESOURCE_TYPE] = r_type
|
||||||
|
alarm[VProps.VITRAGE_RESOURCE_PROJECT_ID] = r_project_id
|
||||||
|
@ -382,8 +382,8 @@ class ApiWorker(GraphCloneWorkerBase):
|
|||||||
server=rabbit_hosts)
|
server=rabbit_hosts)
|
||||||
|
|
||||||
endpoints = [TopologyApis(self._entity_graph, conf),
|
endpoints = [TopologyApis(self._entity_graph, conf),
|
||||||
AlarmApis(self._entity_graph, conf),
|
AlarmApis(self._entity_graph, conf, db),
|
||||||
RcaApis(self._entity_graph, conf),
|
RcaApis(self._entity_graph, conf, db),
|
||||||
TemplateApis(notifier, db),
|
TemplateApis(notifier, db),
|
||||||
EventApis(conf),
|
EventApis(conf),
|
||||||
ResourceApis(self._entity_graph, conf),
|
ResourceApis(self._entity_graph, conf),
|
||||||
|
@ -29,6 +29,7 @@ def create_vertex(vitrage_id,
|
|||||||
entity_state=None,
|
entity_state=None,
|
||||||
update_timestamp=None,
|
update_timestamp=None,
|
||||||
project_id=None,
|
project_id=None,
|
||||||
|
vitrage_resource_project_id=None,
|
||||||
metadata=None):
|
metadata=None):
|
||||||
"""A builder to create a vertex
|
"""A builder to create a vertex
|
||||||
|
|
||||||
@ -68,7 +69,8 @@ def create_vertex(vitrage_id,
|
|||||||
VConst.VITRAGE_SAMPLE_TIMESTAMP: vitrage_sample_timestamp,
|
VConst.VITRAGE_SAMPLE_TIMESTAMP: vitrage_sample_timestamp,
|
||||||
VConst.VITRAGE_IS_PLACEHOLDER: vitrage_is_placeholder,
|
VConst.VITRAGE_IS_PLACEHOLDER: vitrage_is_placeholder,
|
||||||
VConst.VITRAGE_ID: vitrage_id,
|
VConst.VITRAGE_ID: vitrage_id,
|
||||||
VConst.PROJECT_ID: project_id
|
VConst.PROJECT_ID: project_id,
|
||||||
|
VConst.VITRAGE_RESOURCE_PROJECT_ID: vitrage_resource_project_id,
|
||||||
}
|
}
|
||||||
if metadata:
|
if metadata:
|
||||||
properties.update(metadata)
|
properties.update(metadata)
|
||||||
|
@ -18,4 +18,7 @@ OPTS = [
|
|||||||
cfg.StrOpt('persistor_topic',
|
cfg.StrOpt('persistor_topic',
|
||||||
default='vitrage_persistor',
|
default='vitrage_persistor',
|
||||||
help='persistor will listen on this topic for events to store'),
|
help='persistor will listen on this topic for events to store'),
|
||||||
]
|
cfg.IntOpt('alarm_history_ttl',
|
||||||
|
default=30,
|
||||||
|
help='The number of days inactive alarms history is kept'),
|
||||||
|
]
|
||||||
|
@ -13,16 +13,27 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
import cotyledon
|
import cotyledon
|
||||||
|
import dateutil.parser
|
||||||
from futurist import periodics
|
from futurist import periodics
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
import oslo_messaging as oslo_m
|
import oslo_messaging as oslo_m
|
||||||
|
from oslo_utils import timeutils
|
||||||
|
|
||||||
|
from vitrage.common.constants import EdgeProperties as EProps
|
||||||
|
from vitrage.common.constants import ElementProperties as ElementProps
|
||||||
|
from vitrage.common.constants import HistoryProps as HProps
|
||||||
|
from vitrage.common.constants import NotifierEventTypes as NETypes
|
||||||
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.common.utils import spawn
|
from vitrage.common.utils import spawn
|
||||||
from vitrage import messaging
|
from vitrage import messaging
|
||||||
|
from vitrage.storage.sqlalchemy import models
|
||||||
|
from vitrage.utils.datetime import utcnow
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -58,16 +69,94 @@ class PersistorService(cotyledon.Service):
|
|||||||
|
|
||||||
|
|
||||||
class VitragePersistorEndpoint(object):
|
class VitragePersistorEndpoint(object):
|
||||||
|
|
||||||
funcs = {}
|
|
||||||
|
|
||||||
def __init__(self, db_connection):
|
def __init__(self, db_connection):
|
||||||
self.db_connection = db_connection
|
self.db = db_connection
|
||||||
|
self.event_type_to_writer = {
|
||||||
|
NETypes.ACTIVATE_ALARM_EVENT: self._persist_activated_alarm,
|
||||||
|
NETypes.DEACTIVATE_ALARM_EVENT: self._persist_deactivate_alarm,
|
||||||
|
NETypes.ACTIVATE_CAUSAL_RELATION: self._persist_activate_edge,
|
||||||
|
NETypes.DEACTIVATE_CAUSAL_RELATION: self._persist_deactivate_edge,
|
||||||
|
NETypes.CHANGE_IN_ALARM_EVENT: self._persist_change,
|
||||||
|
NETypes.CHANGE_PROJECT_ID_EVENT: self._persist_alarm_proj_change,
|
||||||
|
}
|
||||||
|
|
||||||
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
||||||
LOG.debug('Event_type: %s Payload %s', event_type, payload)
|
LOG.debug('Event_type: %s Payload %s', event_type, payload)
|
||||||
if event_type and event_type in self.funcs.keys():
|
self.process_event(event_type, payload)
|
||||||
self.funcs[event_type](self.db_connection, event_type, payload)
|
|
||||||
|
def process_event(self, event_type, payload):
|
||||||
|
writer = self.event_type_to_writer.get(event_type)
|
||||||
|
if not writer:
|
||||||
|
LOG.warning('Unrecognized event_type: %s', event_type)
|
||||||
|
return
|
||||||
|
writer(event_type, payload)
|
||||||
|
|
||||||
|
def _persist_activated_alarm(self, event_type, data):
|
||||||
|
event_timestamp = self.event_time(data)
|
||||||
|
|
||||||
|
alarm_row = \
|
||||||
|
models.Alarm(
|
||||||
|
vitrage_id=data.get(VProps.VITRAGE_ID),
|
||||||
|
start_timestamp=event_timestamp,
|
||||||
|
name=data.get(VProps.NAME),
|
||||||
|
vitrage_type=data.get(VProps.VITRAGE_TYPE),
|
||||||
|
vitrage_aggregated_severity=data.get(
|
||||||
|
VProps.VITRAGE_AGGREGATED_SEVERITY),
|
||||||
|
project_id=data.get(VProps.PROJECT_ID),
|
||||||
|
vitrage_resource_type=data.get(VProps.VITRAGE_RESOURCE_TYPE),
|
||||||
|
vitrage_resource_id=data.get(VProps.VITRAGE_RESOURCE_ID),
|
||||||
|
vitrage_resource_project_id=data.get(
|
||||||
|
VProps.VITRAGE_RESOURCE_PROJECT_ID),
|
||||||
|
payload=data)
|
||||||
|
self.db.alarms.create(alarm_row)
|
||||||
|
|
||||||
|
def _persist_deactivate_alarm(self, event_type, data):
|
||||||
|
vitrage_id = data.get(VProps.VITRAGE_ID)
|
||||||
|
event_timestamp = self.event_time(data)
|
||||||
|
self.db.alarms.update(
|
||||||
|
vitrage_id, HProps.END_TIMESTAMP, event_timestamp)
|
||||||
|
|
||||||
|
def _persist_alarm_proj_change(self, event_type, data):
|
||||||
|
vitrage_id = data.get(VProps.VITRAGE_ID)
|
||||||
|
self.db.alarms.update(vitrage_id,
|
||||||
|
VProps.VITRAGE_RESOURCE_PROJECT_ID,
|
||||||
|
data.get(VProps.VITRAGE_RESOURCE_PROJECT_ID))
|
||||||
|
|
||||||
|
def _persist_activate_edge(self, event_type, data):
|
||||||
|
event_timestamp = self.event_time(data)
|
||||||
|
|
||||||
|
edge_row = \
|
||||||
|
models.Edge(
|
||||||
|
source_id=data.get(EProps.SOURCE_ID),
|
||||||
|
target_id=data.get(EProps.TARGET_ID),
|
||||||
|
label=data.get(EProps.RELATIONSHIP_TYPE),
|
||||||
|
start_timestamp=event_timestamp,
|
||||||
|
payload=data)
|
||||||
|
self.db.edges.create(edge_row)
|
||||||
|
|
||||||
|
def _persist_deactivate_edge(self, event_type, data):
|
||||||
|
event_timestamp = self.event_time(data)
|
||||||
|
source_id = data.get(EProps.SOURCE_ID)
|
||||||
|
target_id = data.get(EProps.TARGET_ID)
|
||||||
|
self.db.edges.update(
|
||||||
|
source_id, target_id, end_timestamp=event_timestamp)
|
||||||
|
|
||||||
|
def _persist_change(self, event_type, data):
|
||||||
|
event_timestamp = self.event_time(data)
|
||||||
|
change_row = \
|
||||||
|
models.Change(
|
||||||
|
vitrage_id=data.get(VProps.VITRAGE_ID),
|
||||||
|
timestamp=event_timestamp,
|
||||||
|
severity=data.get(VProps.VITRAGE_AGGREGATED_SEVERITY),
|
||||||
|
payload=data)
|
||||||
|
self.db.changes.create(change_row)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def event_time(data):
|
||||||
|
event_timestamp = \
|
||||||
|
dateutil.parser.parse(data.get(ElementProps.UPDATE_TIMESTAMP))
|
||||||
|
event_timestamp = timeutils.normalize_time(event_timestamp)
|
||||||
|
return event_timestamp
|
||||||
|
|
||||||
|
|
||||||
class Scheduler(object):
|
class Scheduler(object):
|
||||||
@ -81,10 +170,11 @@ class Scheduler(object):
|
|||||||
self.periodic = periodics.PeriodicWorker.create(
|
self.periodic = periodics.PeriodicWorker.create(
|
||||||
[], executor_factory=lambda: ThreadPoolExecutor(max_workers=10))
|
[], executor_factory=lambda: ThreadPoolExecutor(max_workers=10))
|
||||||
|
|
||||||
self.add_expirer_timer()
|
self.add_events_table_expirer_timer()
|
||||||
|
self.add_history_tables_expirer_timer()
|
||||||
spawn(self.periodic.start)
|
spawn(self.periodic.start)
|
||||||
|
|
||||||
def add_expirer_timer(self):
|
def add_events_table_expirer_timer(self):
|
||||||
spacing = 60
|
spacing = 60
|
||||||
|
|
||||||
@periodics.periodic(spacing=spacing)
|
@periodics.periodic(spacing=spacing)
|
||||||
@ -92,11 +182,27 @@ class Scheduler(object):
|
|||||||
try:
|
try:
|
||||||
event_id = self.db.graph_snapshots.query_snapshot_event_id()
|
event_id = self.db.graph_snapshots.query_snapshot_event_id()
|
||||||
if event_id:
|
if event_id:
|
||||||
LOG.debug('Expirer deleting event - id=%s', event_id)
|
LOG.debug('Table events - deleting event id=%s', event_id)
|
||||||
self.db.events.delete(event_id)
|
self.db.events.delete(event_id)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception('DB periodic cleanup run failed.')
|
LOG.exception('Table events - periodic cleanup run failed.')
|
||||||
|
|
||||||
self.periodic.add(expirer_periodic)
|
self.periodic.add(expirer_periodic)
|
||||||
LOG.info("Database periodic cleanup starting (spacing=%ss)", spacing)
|
LOG.info("Table events - periodic cleanup started (%ss)", spacing)
|
||||||
|
|
||||||
|
def add_history_tables_expirer_timer(self):
|
||||||
|
spacing = 60
|
||||||
|
|
||||||
|
@periodics.periodic(spacing=spacing)
|
||||||
|
def expirer_periodic():
|
||||||
|
expire_by = \
|
||||||
|
utcnow(with_timezone=False) - \
|
||||||
|
timedelta(days=self.conf.persistency.alarm_history_ttl)
|
||||||
|
try:
|
||||||
|
self.db.alarms.delete_expired(expire_by)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception('History tables - periodic cleanup run failed.')
|
||||||
|
|
||||||
|
self.periodic.add(expirer_periodic)
|
||||||
|
LOG.info("History tables - periodic cleanup started (%ss)", spacing)
|
||||||
|
@ -17,6 +17,8 @@ import six.moves.urllib.parse as urlparse
|
|||||||
from stevedore import driver
|
from stevedore import driver
|
||||||
import tenacity
|
import tenacity
|
||||||
|
|
||||||
|
from vitrage.utils.datetime import utcnow
|
||||||
|
|
||||||
_NAMESPACE = 'vitrage.storage'
|
_NAMESPACE = 'vitrage.storage'
|
||||||
|
|
||||||
|
|
||||||
@ -50,3 +52,8 @@ def get_connection_from_config(conf):
|
|||||||
return mgr.driver(conf, url)
|
return mgr.driver(conf, url)
|
||||||
|
|
||||||
return _get_connection()
|
return _get_connection()
|
||||||
|
|
||||||
|
|
||||||
|
def db_time():
|
||||||
|
ret = utcnow(with_timezone=False)
|
||||||
|
return ret.replace(microsecond=0)
|
||||||
|
@ -43,6 +43,18 @@ class Connection(object):
|
|||||||
def webhooks(self):
|
def webhooks(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alarms(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def edges(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def changes(self):
|
||||||
|
return None
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def upgrade(self, nocreate=False):
|
def upgrade(self, nocreate=False):
|
||||||
raise NotImplementedError('upgrade is not implemented')
|
raise NotImplementedError('upgrade is not implemented')
|
||||||
@ -231,6 +243,48 @@ class GraphSnapshotsConnection(object):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError('query graph snapshot not implemented')
|
raise NotImplementedError('query graph snapshot not implemented')
|
||||||
|
|
||||||
def delete(self, timestamp=None):
|
def delete(self):
|
||||||
"""Delete all graph snapshots taken until timestamp."""
|
"""Delete all graph snapshots taken until timestamp."""
|
||||||
raise NotImplementedError('delete graph snapshots not implemented')
|
raise NotImplementedError('delete graph snapshots not implemented')
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class AlarmsConnection(object):
|
||||||
|
def create(self, alarm):
|
||||||
|
raise NotImplementedError('create alarm not implemented')
|
||||||
|
|
||||||
|
def update(self, vitrage_id, key, val):
|
||||||
|
raise NotImplementedError('update alarms not implemented')
|
||||||
|
|
||||||
|
def end_all_alarms(self, end_time):
|
||||||
|
raise NotImplementedError('end all alarms not implemented')
|
||||||
|
|
||||||
|
def delete(self, timestamp=None):
|
||||||
|
raise NotImplementedError('delete alarms not implemented')
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class EdgesConnection(object):
|
||||||
|
def create(self, edge):
|
||||||
|
raise NotImplementedError('create edge not implemented')
|
||||||
|
|
||||||
|
def update(self, source_id, target_id, timestamp):
|
||||||
|
raise NotImplementedError('update edge not implemented')
|
||||||
|
|
||||||
|
def end_all_edges(self, end_time):
|
||||||
|
raise NotImplementedError('end all edges not implemented')
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
raise NotImplementedError('delete edges not implemented')
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class ChangesConnection(object):
|
||||||
|
def create(self, change):
|
||||||
|
raise NotImplementedError('create change not implemented')
|
||||||
|
|
||||||
|
def add_end_changes(self, chnges_to_add, end_time):
|
||||||
|
raise NotImplementedError('add end changes not implemented')
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
raise NotImplementedError('delete changes not implemented')
|
||||||
|
435
vitrage/storage/history_facade.py
Normal file
435
vitrage/storage/history_facade.py
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
# Copyright 2018 - 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 __future__ import absolute_import
|
||||||
|
|
||||||
|
from oslo_db.sqlalchemy import utils as sqlalchemyutils
|
||||||
|
from oslo_log import log
|
||||||
|
from oslo_utils import timeutils
|
||||||
|
import sqlalchemy
|
||||||
|
from sqlalchemy import and_
|
||||||
|
from sqlalchemy import or_
|
||||||
|
from vitrage.common.constants import EdgeLabel as ELable
|
||||||
|
from vitrage.common.constants import HistoryProps as HProps
|
||||||
|
from vitrage.common.exception import VitrageInputError
|
||||||
|
from vitrage.entity_graph.mappings.operational_alarm_severity import \
|
||||||
|
OperationalAlarmSeverity as OSeverity
|
||||||
|
from vitrage.storage import db_time
|
||||||
|
from vitrage.storage.sqlalchemy import models
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
LIMIT = 10000
|
||||||
|
ASC = 'asc'
|
||||||
|
DESC = 'desc'
|
||||||
|
|
||||||
|
|
||||||
|
class HistoryFacadeConnection(object):
|
||||||
|
def __init__(self, engine_facade, alarms, edges, changes):
|
||||||
|
self._engine_facade = engine_facade
|
||||||
|
self._alarms = alarms
|
||||||
|
self._edges = edges
|
||||||
|
self._changes = changes
|
||||||
|
|
||||||
|
def disable_alarms_in_history(self):
|
||||||
|
end_time = db_time()
|
||||||
|
active_alarms = self.get_alarms(limit=0)
|
||||||
|
changes_to_add = [alarm.vitrage_id for alarm in active_alarms]
|
||||||
|
self._alarms.end_all_alarms(end_time)
|
||||||
|
self._edges.end_all_edges(end_time)
|
||||||
|
self._changes.add_end_changes(changes_to_add, end_time)
|
||||||
|
|
||||||
|
def count_active_alarms(self, project_id=None, is_admin_project=False):
|
||||||
|
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
query = session.query(models.Alarm)
|
||||||
|
query = query.filter(models.Alarm.end_timestamp > db_time())
|
||||||
|
query = self._add_project_filtering_to_query(
|
||||||
|
query, project_id, is_admin_project)
|
||||||
|
|
||||||
|
query_severe = query.filter(
|
||||||
|
models.Alarm.vitrage_aggregated_severity == OSeverity.SEVERE)
|
||||||
|
query_critical = query.filter(
|
||||||
|
models.Alarm.vitrage_aggregated_severity == OSeverity.CRITICAL)
|
||||||
|
query_warning = query.filter(
|
||||||
|
models.Alarm.vitrage_aggregated_severity == OSeverity.WARNING)
|
||||||
|
query_ok = query.filter(
|
||||||
|
models.Alarm.vitrage_aggregated_severity == OSeverity.OK)
|
||||||
|
query_na = query.filter(
|
||||||
|
models.Alarm.vitrage_aggregated_severity == OSeverity.NA)
|
||||||
|
|
||||||
|
counts = {OSeverity.SEVERE: query_severe.count(),
|
||||||
|
OSeverity.CRITICAL: query_critical.count(),
|
||||||
|
OSeverity.WARNING: query_warning.count(),
|
||||||
|
OSeverity.OK: query_ok.count(),
|
||||||
|
OSeverity.NA: query_na.count()}
|
||||||
|
|
||||||
|
return counts
|
||||||
|
|
||||||
|
def get_alarms(self,
|
||||||
|
start=None,
|
||||||
|
end=None,
|
||||||
|
limit=LIMIT,
|
||||||
|
sort_by=(HProps.START_TIMESTAMP, HProps.VITRAGE_ID),
|
||||||
|
sort_dirs=(ASC, ASC),
|
||||||
|
filter_by=None,
|
||||||
|
filter_vals=None,
|
||||||
|
next_page=True,
|
||||||
|
marker=None,
|
||||||
|
only_active_alarms=False,
|
||||||
|
project_id=None,
|
||||||
|
is_admin_project=False):
|
||||||
|
"""Return alarms that match all filters sorted by the given keys.
|
||||||
|
|
||||||
|
Deleted alarms will be returned when only_active_alarms=False.
|
||||||
|
|
||||||
|
filtering and sorting are possible on each row of alarms table
|
||||||
|
(pay attantion: it is not recommended to filter by start_timestamp
|
||||||
|
and end_timestamp when start or end arguments are passed):
|
||||||
|
vitrage_id,
|
||||||
|
start_timestamp,
|
||||||
|
end_timestamp,
|
||||||
|
name,
|
||||||
|
vitrage_type,
|
||||||
|
vitrage_aggregated_severity,
|
||||||
|
project_id,
|
||||||
|
vitrage_resource_type,
|
||||||
|
vitrage_resource_id,
|
||||||
|
vitrage_resource_project_id,
|
||||||
|
payload
|
||||||
|
|
||||||
|
Time Frame:
|
||||||
|
start and end arguments gives the time frame for required alarms.
|
||||||
|
Required format is the format that can be parsed by timeutils library.
|
||||||
|
If both arguments are given, returned alarms are the alarms that
|
||||||
|
where active sometime during given time frame
|
||||||
|
(including active and inactive alarms):
|
||||||
|
|
||||||
|
1. start_ts------------end_ts
|
||||||
|
2. start_ts------------end_ts
|
||||||
|
3. start_ts------------end_ts
|
||||||
|
4. start_ts---------------------------------------end_ts
|
||||||
|
start end
|
||||||
|
|_______________________________|
|
||||||
|
|
||||||
|
If only start is given, all alarms that started after this time
|
||||||
|
will be returned (including active and inactive alarms):
|
||||||
|
1. start_ts------------end_ts
|
||||||
|
2. start_ts------
|
||||||
|
start now
|
||||||
|
|_______________________________|
|
||||||
|
|
||||||
|
note1: end argument can't be used without start argument
|
||||||
|
note2: time frame can't be used with flag only_active_alarms=True
|
||||||
|
|
||||||
|
Filtering:
|
||||||
|
filter_by represents parameters to filter on,
|
||||||
|
and filter_vals contains the values to filter on in corresponding
|
||||||
|
order to the order of parameters in filter_by.
|
||||||
|
The filtering is according to SQL 'like' statement.
|
||||||
|
It's possible to filter on each row of alarms table
|
||||||
|
The filtering is also possible on list of values.
|
||||||
|
|
||||||
|
examples:
|
||||||
|
1. In the following example:
|
||||||
|
| filter_by = ['vitrage_type', 'vitrage_resource_type']
|
||||||
|
| filter_vals = ['zabbix', 'nova']
|
||||||
|
which will be evaluated to:
|
||||||
|
Alarm.vitrage_type like '%zabbix%'
|
||||||
|
and Alarm.vitrage_resource_type like '%nova%'
|
||||||
|
Tthe filtering will be done so the query returns all the alarms
|
||||||
|
in the DB with vitrage type containing the string 'zabbix'
|
||||||
|
and vitrage resource type containing the string 'nova'
|
||||||
|
|
||||||
|
2. Following example is filtering list of values for one same property:
|
||||||
|
| filter_by = ['vitrage_type', 'vitrage_id']
|
||||||
|
| filter_vals = ['zabbix', ['123', '456', '789']]
|
||||||
|
It will be evaluated to:
|
||||||
|
Alarm.vitrage_type like '%zabbix%'
|
||||||
|
and Alarm.vitrage_resource_type like '%123%'
|
||||||
|
or like '%456%'
|
||||||
|
or like '%789%'
|
||||||
|
Tthe filtering will be done so the query returns all the alarms
|
||||||
|
in the DB with vitrage type containing the string 'zabbix'
|
||||||
|
and with one of vitrage_ids that are in the list in filter_vals[1]
|
||||||
|
|
||||||
|
|
||||||
|
:param start: start of time frame
|
||||||
|
:param end: end of time frame
|
||||||
|
:param limit: maximum number of items to return,
|
||||||
|
if limit=0 the method will return all matched items in alarms table,
|
||||||
|
if limit is bigger then default parameter LIMIT, the number of items
|
||||||
|
that will be returned will be defined by the default parameter LIMIT
|
||||||
|
:param sort_by: array of attributes by which results should be sorted
|
||||||
|
:param sort_dirs: per-column array of sort_dirs,
|
||||||
|
corresponding to sort_keys ('asc' or 'desc').
|
||||||
|
:param filter_by: array of attributes by which results will be filtered
|
||||||
|
:param filter_vals: per-column array of filter values
|
||||||
|
corresponding to filter_by
|
||||||
|
:param next_page: if True will return next page when marker is given,
|
||||||
|
if False will return previous page when marker is given,
|
||||||
|
otherwise, returns first page if no marker was given.
|
||||||
|
:param marker: if None returns first page, else if vitrage_id is given
|
||||||
|
and next_page is True, return next #limit results after marker,
|
||||||
|
else, if next page is False,return #limit results before marker.
|
||||||
|
:param only_active_alarms: if True, returns only active alarms,
|
||||||
|
if False return active and non-active alarms.
|
||||||
|
:param project_id: if None there is no filtering by project_id
|
||||||
|
(equals to All Tenants=True),
|
||||||
|
if id is given, query will be fillter alarms by project id.
|
||||||
|
:param is_admin_project: True to return alarms with
|
||||||
|
project_id=None or resource_project_id=None
|
||||||
|
"""
|
||||||
|
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
query = session.query(models.Alarm)
|
||||||
|
query = self._add_project_filtering_to_query(
|
||||||
|
query, project_id, is_admin_project)
|
||||||
|
|
||||||
|
self.assert_args(start, end, filter_by, filter_vals,
|
||||||
|
only_active_alarms, sort_dirs)
|
||||||
|
|
||||||
|
if only_active_alarms:
|
||||||
|
query = query.filter(models.Alarm.end_timestamp > db_time())
|
||||||
|
elif (start and end) or start:
|
||||||
|
query = self._add_time_frame_to_query(query, start, end)
|
||||||
|
|
||||||
|
query = self._add_filtering_to_query(query, filter_by, filter_vals)
|
||||||
|
|
||||||
|
if limit:
|
||||||
|
query = self._generate_alarms_paginate_query(query,
|
||||||
|
limit,
|
||||||
|
sort_by,
|
||||||
|
sort_dirs,
|
||||||
|
next_page,
|
||||||
|
marker)
|
||||||
|
elif limit == 0:
|
||||||
|
sort_dir_func = {
|
||||||
|
ASC: sqlalchemy.asc,
|
||||||
|
DESC: sqlalchemy.desc,
|
||||||
|
}
|
||||||
|
for i in range(len(sort_by)):
|
||||||
|
query.order_by(sort_dir_func[sort_dirs[i]](
|
||||||
|
getattr(models.Alarm, sort_by[i])))
|
||||||
|
return query.all()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def assert_args(start,
|
||||||
|
end,
|
||||||
|
filter_by,
|
||||||
|
filter_vals,
|
||||||
|
only_active_alarms,
|
||||||
|
sort_dirs):
|
||||||
|
if only_active_alarms and (start or end):
|
||||||
|
raise VitrageInputError("'only_active_alarms' can't be used "
|
||||||
|
"with 'start' or 'end' ")
|
||||||
|
if end and not start:
|
||||||
|
raise VitrageInputError("'end' can't be used without 'start'")
|
||||||
|
if (filter_by and not filter_vals) or (filter_vals and not filter_by):
|
||||||
|
raise VitrageInputError('Cannot perform filtering, one of '
|
||||||
|
'filter_by or filter_vals are missing')
|
||||||
|
if filter_by and filter_vals and len(filter_by) != len(filter_vals):
|
||||||
|
raise VitrageInputError("Cannot perform filtering, len of "
|
||||||
|
"'filter_by' and 'filter_vals' differs")
|
||||||
|
for d in sort_dirs:
|
||||||
|
if d not in (ASC, DESC):
|
||||||
|
raise VitrageInputError("Unknown sort direction %s", str(d))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _add_time_frame_to_query(query, start, end):
|
||||||
|
start = timeutils.normalize_time(start)
|
||||||
|
end = timeutils.normalize_time(end)
|
||||||
|
if start and end:
|
||||||
|
query = \
|
||||||
|
query.filter(
|
||||||
|
or_(and_(models.Alarm.start_timestamp >= start,
|
||||||
|
models.Alarm.start_timestamp <= end),
|
||||||
|
and_(models.Alarm.end_timestamp >= start,
|
||||||
|
models.Alarm.end_timestamp <= end),
|
||||||
|
and_(models.Alarm.start_timestamp <= start,
|
||||||
|
models.Alarm.end_timestamp >= end)))
|
||||||
|
elif start:
|
||||||
|
query = query.filter(models.Alarm.end_timestamp >= start)
|
||||||
|
return query
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _add_project_filtering_to_query(query, project_id=None,
|
||||||
|
is_admin_project=False):
|
||||||
|
|
||||||
|
if project_id:
|
||||||
|
if is_admin_project:
|
||||||
|
query = query.filter(or_(
|
||||||
|
or_(models.Alarm.project_id == project_id,
|
||||||
|
models.Alarm.vitrage_resource_project_id ==
|
||||||
|
project_id),
|
||||||
|
and_(
|
||||||
|
or_(
|
||||||
|
models.Alarm.project_id == project_id,
|
||||||
|
models.Alarm.project_id == None),
|
||||||
|
or_(
|
||||||
|
models.Alarm.vitrage_resource_project_id ==
|
||||||
|
project_id,
|
||||||
|
models.Alarm.vitrage_resource_project_id == None)
|
||||||
|
))) # noqa
|
||||||
|
else:
|
||||||
|
query = query.filter(
|
||||||
|
or_(models.Alarm.project_id == project_id,
|
||||||
|
models.Alarm.vitrage_resource_project_id ==
|
||||||
|
project_id))
|
||||||
|
return query
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _add_filtering_to_query(query, filter_by, filter_vals):
|
||||||
|
|
||||||
|
if not (filter_by or filter_vals):
|
||||||
|
return query
|
||||||
|
|
||||||
|
for i in range(len(filter_by)):
|
||||||
|
key = filter_by[i]
|
||||||
|
val = filter_vals[i]
|
||||||
|
val = val if val and type(val) == list else [val]
|
||||||
|
cond = or_(*[getattr(models.Alarm, key).like(
|
||||||
|
'%' + val[j] + '%') for j in range(len(val))])
|
||||||
|
query = query.filter(cond)
|
||||||
|
return query
|
||||||
|
|
||||||
|
def _generate_alarms_paginate_query(self,
|
||||||
|
query,
|
||||||
|
limit,
|
||||||
|
sort_by,
|
||||||
|
sort_dirs,
|
||||||
|
next_page,
|
||||||
|
marker):
|
||||||
|
|
||||||
|
limit = min(int(limit), LIMIT)
|
||||||
|
|
||||||
|
if marker:
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
marker = session.query(models.Alarm). \
|
||||||
|
filter(models.Alarm.vitrage_id ==
|
||||||
|
marker).first()
|
||||||
|
|
||||||
|
if HProps.VITRAGE_ID not in sort_by:
|
||||||
|
sort_by.append(HProps.VITRAGE_ID)
|
||||||
|
sort_dirs.append(ASC)
|
||||||
|
|
||||||
|
if not next_page and marker: # 'not next_page' means previous page
|
||||||
|
marker = self._create_marker_for_prev(
|
||||||
|
query, limit, sort_by, sort_dirs, marker)
|
||||||
|
|
||||||
|
query = sqlalchemyutils.paginate_query(query,
|
||||||
|
models.Alarm,
|
||||||
|
limit,
|
||||||
|
sort_by,
|
||||||
|
sort_dirs=sort_dirs,
|
||||||
|
marker=marker)
|
||||||
|
return query
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_marker_for_prev(query, limit, sort_by, sort_dirs, marker):
|
||||||
|
|
||||||
|
dirs = [DESC if d == ASC else ASC for d in sort_dirs]
|
||||||
|
query = sqlalchemyutils.paginate_query(query,
|
||||||
|
models.Alarm,
|
||||||
|
limit + 1,
|
||||||
|
sort_by,
|
||||||
|
marker=marker,
|
||||||
|
sort_dirs=dirs)
|
||||||
|
|
||||||
|
alarms = query.all()
|
||||||
|
if len(alarms) < limit + 1:
|
||||||
|
new_marker = None
|
||||||
|
else:
|
||||||
|
new_marker = alarms[-1]
|
||||||
|
|
||||||
|
return new_marker
|
||||||
|
|
||||||
|
def alarm_rca(self,
|
||||||
|
alarm_id,
|
||||||
|
forward=True,
|
||||||
|
backward=True,
|
||||||
|
depth=None,
|
||||||
|
project_id=None,
|
||||||
|
admin=False):
|
||||||
|
|
||||||
|
n_result_f = []
|
||||||
|
e_result_f = []
|
||||||
|
if forward:
|
||||||
|
n_result_f, e_result_f = \
|
||||||
|
self._bfs(alarm_id, self._out_rca, depth, admin=admin,
|
||||||
|
project_id=project_id)
|
||||||
|
|
||||||
|
n_result_b = []
|
||||||
|
e_result_b = []
|
||||||
|
if backward:
|
||||||
|
n_result_b, e_result_b = \
|
||||||
|
self._bfs(alarm_id, self._in_rca, depth, admin=admin,
|
||||||
|
project_id=project_id)
|
||||||
|
|
||||||
|
n_result = self.get_alarms(limit=0,
|
||||||
|
filter_by=[HProps.VITRAGE_ID],
|
||||||
|
filter_vals=[n_result_f + n_result_b])
|
||||||
|
|
||||||
|
e_result = e_result_f + e_result_b
|
||||||
|
|
||||||
|
return n_result, e_result
|
||||||
|
|
||||||
|
def _rca_edges(self, filter_by, a_ids, proj_id, admin):
|
||||||
|
alarm_ids = [str(alarm) for alarm in a_ids]
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
query = session.query(models.Edge)\
|
||||||
|
.filter(and_(getattr(models.Edge, filter_by).in_(alarm_ids),
|
||||||
|
models.Edge.label == ELable.CAUSES))
|
||||||
|
|
||||||
|
query = query.join(models.Edge.target)
|
||||||
|
query = self._add_project_filtering_to_query(query, proj_id, admin)
|
||||||
|
|
||||||
|
return query.all()
|
||||||
|
|
||||||
|
def _out_rca(self, sources, proj_id, admin):
|
||||||
|
return self._rca_edges(HProps.SOURCE_ID, sources, proj_id, admin)
|
||||||
|
|
||||||
|
def _in_rca(self, targets, proj_id, admin):
|
||||||
|
return self._rca_edges(HProps.TARGET_ID, targets, proj_id, admin)
|
||||||
|
|
||||||
|
def _bfs(self, alarm_id, neighbors_func,
|
||||||
|
depth=None,
|
||||||
|
project_id=None,
|
||||||
|
admin=False):
|
||||||
|
n_result = []
|
||||||
|
visited_nodes = set()
|
||||||
|
n_result.append(alarm_id)
|
||||||
|
e_result = []
|
||||||
|
curr_depth = 0
|
||||||
|
nodes_q = {curr_depth: [alarm_id]}
|
||||||
|
while nodes_q:
|
||||||
|
node_ids = nodes_q.pop(curr_depth)
|
||||||
|
if depth and curr_depth >= depth:
|
||||||
|
break
|
||||||
|
for node_id in node_ids:
|
||||||
|
if node_id in visited_nodes:
|
||||||
|
node_ids.remove(node_id)
|
||||||
|
visited_nodes.update(node_ids)
|
||||||
|
e_list = neighbors_func(node_ids, project_id, admin)
|
||||||
|
n_list = \
|
||||||
|
[edge.target_id if edge.source_id in node_ids
|
||||||
|
else edge.source_id for edge in e_list]
|
||||||
|
n_result.extend(n_list)
|
||||||
|
e_result.extend(e_list)
|
||||||
|
if n_list:
|
||||||
|
curr_depth += 1
|
||||||
|
nodes_q[curr_depth] = n_list
|
||||||
|
|
||||||
|
return n_result, e_result
|
@ -17,12 +17,17 @@ from __future__ import absolute_import
|
|||||||
|
|
||||||
from oslo_db.sqlalchemy import session as db_session
|
from oslo_db.sqlalchemy import session as db_session
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
from sqlalchemy import and_
|
||||||
from sqlalchemy.engine import url as sqlalchemy_url
|
from sqlalchemy.engine import url as sqlalchemy_url
|
||||||
|
from sqlalchemy import func
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
|
|
||||||
from vitrage.common.exception import VitrageInputError
|
from vitrage.common.exception import VitrageInputError
|
||||||
|
from vitrage.entity_graph.mappings.operational_alarm_severity import \
|
||||||
|
OperationalAlarmSeverity
|
||||||
from vitrage import storage
|
from vitrage import storage
|
||||||
from vitrage.storage import base
|
from vitrage.storage import base
|
||||||
|
from vitrage.storage.history_facade import HistoryFacadeConnection
|
||||||
from vitrage.storage.sqlalchemy import models
|
from vitrage.storage.sqlalchemy import models
|
||||||
from vitrage.storage.sqlalchemy.models import Template
|
from vitrage.storage.sqlalchemy.models import Template
|
||||||
|
|
||||||
@ -47,6 +52,14 @@ class Connection(base.Connection):
|
|||||||
self._graph_snapshots = GraphSnapshotsConnection(self._engine_facade)
|
self._graph_snapshots = GraphSnapshotsConnection(self._engine_facade)
|
||||||
self._webhooks = WebhooksConnection(
|
self._webhooks = WebhooksConnection(
|
||||||
self._engine_facade)
|
self._engine_facade)
|
||||||
|
self._alarms = AlarmsConnection(
|
||||||
|
self._engine_facade)
|
||||||
|
self._edges = EdgesConnection(
|
||||||
|
self._engine_facade)
|
||||||
|
self._changes = ChangesConnection(
|
||||||
|
self._engine_facade)
|
||||||
|
self._history_facade = HistoryFacadeConnection(
|
||||||
|
self._engine_facade, self._alarms, self._edges, self._changes)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def webhooks(self):
|
def webhooks(self):
|
||||||
@ -68,6 +81,22 @@ class Connection(base.Connection):
|
|||||||
def graph_snapshots(self):
|
def graph_snapshots(self):
|
||||||
return self._graph_snapshots
|
return self._graph_snapshots
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alarms(self):
|
||||||
|
return self._alarms
|
||||||
|
|
||||||
|
@property
|
||||||
|
def edges(self):
|
||||||
|
return self._edges
|
||||||
|
|
||||||
|
@property
|
||||||
|
def changes(self):
|
||||||
|
return self._changes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def history_facade(self):
|
||||||
|
return self._history_facade
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _dress_url(url):
|
def _dress_url(url):
|
||||||
# If no explicit driver has been set, we default to pymysql
|
# If no explicit driver has been set, we default to pymysql
|
||||||
@ -97,7 +126,10 @@ class Connection(base.Connection):
|
|||||||
models.Template.__table__,
|
models.Template.__table__,
|
||||||
models.Webhooks.__table__,
|
models.Webhooks.__table__,
|
||||||
models.Event.__table__,
|
models.Event.__table__,
|
||||||
models.GraphSnapshot.__table__])
|
models.GraphSnapshot.__table__,
|
||||||
|
models.Alarm.__table__,
|
||||||
|
models.Edge.__table__,
|
||||||
|
models.Change.__table__])
|
||||||
# TODO(idan_hefetz) upgrade logic is missing
|
# TODO(idan_hefetz) upgrade logic is missing
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
@ -359,3 +391,112 @@ class GraphSnapshotsConnection(base.GraphSnapshotsConnection, BaseTableConn):
|
|||||||
"""Delete all graph snapshots"""
|
"""Delete all graph snapshots"""
|
||||||
query = self.query_filter(models.GraphSnapshot)
|
query = self.query_filter(models.GraphSnapshot)
|
||||||
query.delete()
|
query.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmsConnection(base.AlarmsConnection, BaseTableConn):
|
||||||
|
def __init__(self, engine_facade):
|
||||||
|
super(AlarmsConnection, self).__init__(engine_facade)
|
||||||
|
|
||||||
|
def create(self, alarm):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
with session.begin():
|
||||||
|
session.add(alarm)
|
||||||
|
|
||||||
|
def update(self, vitrage_id, key, val):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = session.query(models.Alarm).filter(
|
||||||
|
models.Alarm.vitrage_id == vitrage_id)
|
||||||
|
query.update({getattr(models.Alarm, key): val})
|
||||||
|
|
||||||
|
def end_all_alarms(self, end_time):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
query = session.query(models.Alarm).filter(
|
||||||
|
models.Alarm.end_timestamp > end_time)
|
||||||
|
query.update({models.Alarm.end_timestamp: end_time})
|
||||||
|
|
||||||
|
def delete_expired(self, expire_by=None):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
query = session.query(models.Alarm)
|
||||||
|
query = query.filter(models.Alarm.end_timestamp < expire_by)
|
||||||
|
return query.delete()
|
||||||
|
|
||||||
|
def delete(self,
|
||||||
|
vitrage_id=None,
|
||||||
|
start_timestamp=None,
|
||||||
|
end_timestamp=None):
|
||||||
|
query = self.query_filter(
|
||||||
|
models.Alarm,
|
||||||
|
vitrage_id=vitrage_id,
|
||||||
|
start_timestamp=start_timestamp,
|
||||||
|
end_timestamp=end_timestamp)
|
||||||
|
return query.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class EdgesConnection(base.EdgesConnection, BaseTableConn):
|
||||||
|
def __init__(self, engine_facade):
|
||||||
|
super(EdgesConnection, self).__init__(engine_facade)
|
||||||
|
|
||||||
|
def create(self, edge):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
with session.begin():
|
||||||
|
session.add(edge)
|
||||||
|
|
||||||
|
def update(self, source_id, target_id, end_timestamp):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = session.query(models.Edge).filter(and_(
|
||||||
|
models.Edge.source_id == source_id,
|
||||||
|
models.Edge.target_id == target_id))
|
||||||
|
query.update({models.Edge.end_timestamp: end_timestamp})
|
||||||
|
|
||||||
|
def end_all_edges(self, end_time):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
query = session.query(models.Edge).filter(
|
||||||
|
models.Edge.end_timestamp > end_time)
|
||||||
|
query.update({models.Edge.end_timestamp: end_time})
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
query = self.query_filter(models.Edge)
|
||||||
|
return query.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class ChangesConnection(base.ChangesConnection, BaseTableConn):
|
||||||
|
def __init__(self, engine_facade):
|
||||||
|
super(ChangesConnection, self).__init__(engine_facade)
|
||||||
|
|
||||||
|
def create(self, change):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
with session.begin():
|
||||||
|
session.add(change)
|
||||||
|
|
||||||
|
def add_end_changes(self, vitrage_ids, end_time):
|
||||||
|
last_changes = self._get_alarms_last_change(vitrage_ids)
|
||||||
|
for id, change in last_changes.items():
|
||||||
|
change_row = \
|
||||||
|
models.Change(
|
||||||
|
vitrage_id=id,
|
||||||
|
timestamp=end_time,
|
||||||
|
severity=OperationalAlarmSeverity.OK,
|
||||||
|
payload=change.payload)
|
||||||
|
self.create(change_row)
|
||||||
|
|
||||||
|
def _get_alarms_last_change(self, alarm_ids):
|
||||||
|
session = self._engine_facade.get_session()
|
||||||
|
query = session.query(func.max(models.Change.timestamp),
|
||||||
|
models.Change.vitrage_id,
|
||||||
|
models.Change.payload).\
|
||||||
|
filter(models.Change.vitrage_id.in_(alarm_ids)).\
|
||||||
|
group_by(models.Change.vitrage_id)
|
||||||
|
|
||||||
|
rows = query.all()
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for change in rows:
|
||||||
|
result[change.vitrage_id] = change
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
query = self.query_filter(models.Change)
|
||||||
|
return query.delete()
|
||||||
|
@ -11,18 +11,22 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
import zlib
|
import zlib
|
||||||
|
|
||||||
from oslo_db.sqlalchemy import models
|
from oslo_db.sqlalchemy import models
|
||||||
|
|
||||||
from sqlalchemy import Column, INTEGER, String, \
|
from sqlalchemy import Column, DateTime, INTEGER, String, \
|
||||||
SmallInteger, BigInteger, Index, Boolean
|
SmallInteger, BigInteger, Index, Boolean, ForeignKey
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
import sqlalchemy.types as types
|
import sqlalchemy.types as types
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_END_TIME = datetime.datetime(2222, 2, 22, 22, 22, 22)
|
||||||
|
|
||||||
|
|
||||||
class VitrageBase(models.TimestampMixin, models.ModelBase):
|
class VitrageBase(models.TimestampMixin, models.ModelBase):
|
||||||
"""Base class for Vitrage Models."""
|
"""Base class for Vitrage Models."""
|
||||||
__table_args__ = {'mysql_charset': "utf8",
|
__table_args__ = {'mysql_charset': "utf8",
|
||||||
@ -44,6 +48,22 @@ class VitrageBase(models.TimestampMixin, models.ModelBase):
|
|||||||
Base = declarative_base(cls=VitrageBase)
|
Base = declarative_base(cls=VitrageBase)
|
||||||
|
|
||||||
|
|
||||||
|
class AutoIncrementInteger(types.TypeDecorator):
|
||||||
|
impl = types.INT
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
value = self.count
|
||||||
|
self.count += 1
|
||||||
|
return value
|
||||||
|
|
||||||
|
def process_result_value(self, value, dialect):
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
MagicBigInt = types.BigInteger().with_variant(AutoIncrementInteger, 'sqlite')
|
||||||
|
|
||||||
|
|
||||||
class JSONEncodedDict(types.TypeDecorator):
|
class JSONEncodedDict(types.TypeDecorator):
|
||||||
"""Represents an immutable structure as a json-encoded string"""
|
"""Represents an immutable structure as a json-encoded string"""
|
||||||
|
|
||||||
@ -204,3 +224,118 @@ class Webhooks(Base):
|
|||||||
self.headers,
|
self.headers,
|
||||||
self.regex_filter
|
self.regex_filter
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Alarm(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'alarms'
|
||||||
|
|
||||||
|
vitrage_id = Column(String(128), primary_key=True)
|
||||||
|
start_timestamp = Column(DateTime, index=True, nullable=False)
|
||||||
|
end_timestamp = Column(DateTime, index=True, nullable=False,
|
||||||
|
default=DEFAULT_END_TIME)
|
||||||
|
name = Column(String(256), nullable=False)
|
||||||
|
vitrage_type = Column(String(64), nullable=False)
|
||||||
|
vitrage_aggregated_severity = Column(String(64), index=True,
|
||||||
|
nullable=False)
|
||||||
|
project_id = Column(String(64), index=True)
|
||||||
|
vitrage_resource_type = Column(String(64))
|
||||||
|
vitrage_resource_id = Column(String(64))
|
||||||
|
vitrage_resource_project_id = Column(String(64), index=True)
|
||||||
|
payload = Column(JSONEncodedDict())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return \
|
||||||
|
"<Alarm(" \
|
||||||
|
"vitrage_id='%s', " \
|
||||||
|
"start_timestamp='%s', " \
|
||||||
|
"end_timestamp='%s'," \
|
||||||
|
"name='%s'," \
|
||||||
|
"vitrage_type='%s'," \
|
||||||
|
"vitrage_aggregated_severity='%s'," \
|
||||||
|
"project_id='%s'," \
|
||||||
|
"vitrage_resource_type='%s'," \
|
||||||
|
"vitrage_resource_id='%s'," \
|
||||||
|
"vitrage_resource_project_id='%s'," \
|
||||||
|
"payload='%s')>" % \
|
||||||
|
(
|
||||||
|
self.vitrage_id,
|
||||||
|
self.start_timestamp,
|
||||||
|
self.end_timestamp,
|
||||||
|
self.name,
|
||||||
|
self.vitrage_type,
|
||||||
|
self.vitrage_aggregated_severity,
|
||||||
|
self.project_id,
|
||||||
|
self.vitrage_resource_type,
|
||||||
|
self.vitrage_resource_id,
|
||||||
|
self.vitrage_resource_project_id,
|
||||||
|
self.payload
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Edge(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'edges'
|
||||||
|
|
||||||
|
source_id = Column(String(128),
|
||||||
|
ForeignKey('alarms.vitrage_id', ondelete='CASCADE'),
|
||||||
|
primary_key=True)
|
||||||
|
target_id = Column(String(128),
|
||||||
|
ForeignKey('alarms.vitrage_id', ondelete='CASCADE'),
|
||||||
|
primary_key=True)
|
||||||
|
label = Column(String(64), nullable=False)
|
||||||
|
start_timestamp = Column(DateTime, nullable=False)
|
||||||
|
end_timestamp = Column(DateTime, nullable=False, default=DEFAULT_END_TIME)
|
||||||
|
payload = Column(JSONEncodedDict())
|
||||||
|
|
||||||
|
source = relationship("Alarm", foreign_keys=[source_id])
|
||||||
|
target = relationship("Alarm", foreign_keys=[target_id])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return \
|
||||||
|
"<Edge(" \
|
||||||
|
"source_id='%s', " \
|
||||||
|
"target_id='%s', " \
|
||||||
|
"label='%s', " \
|
||||||
|
"start_timestamp='%s'," \
|
||||||
|
"end_timestamp='%s',"\
|
||||||
|
"payload='%s)>" % \
|
||||||
|
(
|
||||||
|
self.source_id,
|
||||||
|
self.target_id,
|
||||||
|
self.label,
|
||||||
|
self.start_timestamp,
|
||||||
|
self.end_timestamp,
|
||||||
|
self.payload
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Change(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'changes'
|
||||||
|
|
||||||
|
id = Column(MagicBigInt, primary_key=True, autoincrement=True)
|
||||||
|
vitrage_id = Column(String(128),
|
||||||
|
ForeignKey('alarms.vitrage_id', ondelete='CASCADE'),
|
||||||
|
index=True, nullable=False)
|
||||||
|
timestamp = Column(DateTime, index=True, nullable=False)
|
||||||
|
severity = Column(String(64), index=True, nullable=False)
|
||||||
|
payload = Column(JSONEncodedDict())
|
||||||
|
|
||||||
|
alarm_id = relationship("Alarm", foreign_keys=[vitrage_id])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return \
|
||||||
|
"<Change(" \
|
||||||
|
"id='%s', " \
|
||||||
|
"vitrage_id='%s', " \
|
||||||
|
"timestamp='%s', " \
|
||||||
|
"severity='%s'," \
|
||||||
|
"payload='%s')>" % \
|
||||||
|
(
|
||||||
|
self.id,
|
||||||
|
self.vitrage_id,
|
||||||
|
self.timestamp,
|
||||||
|
self.severity,
|
||||||
|
self.payload
|
||||||
|
)
|
||||||
|
@ -16,11 +16,14 @@ import json
|
|||||||
|
|
||||||
from testtools import matchers
|
from testtools import matchers
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from vitrage.api_handler.apis.alarm import AlarmApis
|
from vitrage.api_handler.apis.alarm import AlarmApis
|
||||||
from vitrage.api_handler.apis.rca import RcaApis
|
from vitrage.api_handler.apis.rca import RcaApis
|
||||||
from vitrage.api_handler.apis.resource import ResourceApis
|
from vitrage.api_handler.apis.resource import ResourceApis
|
||||||
from vitrage.api_handler.apis.topology import TopologyApis
|
from vitrage.api_handler.apis.topology import TopologyApis
|
||||||
from vitrage.common.constants import EdgeLabel
|
from vitrage.common.constants import EdgeLabel
|
||||||
|
from vitrage.common.constants import EdgeProperties
|
||||||
from vitrage.common.constants import EntityCategory
|
from vitrage.common.constants import EntityCategory
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.datasources import NOVA_HOST_DATASOURCE
|
from vitrage.datasources import NOVA_HOST_DATASOURCE
|
||||||
@ -30,18 +33,29 @@ from vitrage.datasources.transformer_base \
|
|||||||
import create_cluster_placeholder_vertex
|
import create_cluster_placeholder_vertex
|
||||||
from vitrage.entity_graph.mappings.operational_alarm_severity import \
|
from vitrage.entity_graph.mappings.operational_alarm_severity import \
|
||||||
OperationalAlarmSeverity
|
OperationalAlarmSeverity
|
||||||
|
from vitrage.entity_graph.processor.notifier import PersistNotifier
|
||||||
|
from vitrage.graph.driver.networkx_graph import edge_copy
|
||||||
from vitrage.graph.driver.networkx_graph import NXGraph
|
from vitrage.graph.driver.networkx_graph import NXGraph
|
||||||
import vitrage.graph.utils as graph_utils
|
import vitrage.graph.utils as graph_utils
|
||||||
|
from vitrage.persistency.service import VitragePersistorEndpoint
|
||||||
from vitrage.tests.base import IsEmpty
|
from vitrage.tests.base import IsEmpty
|
||||||
|
from vitrage.tests.functional.test_configuration import TestConfiguration
|
||||||
from vitrage.tests.unit.entity_graph.base import TestEntityGraphUnitBase
|
from vitrage.tests.unit.entity_graph.base import TestEntityGraphUnitBase
|
||||||
|
from vitrage.utils.datetime import utcnow
|
||||||
|
|
||||||
|
|
||||||
class TestApis(TestEntityGraphUnitBase):
|
class TestApis(TestEntityGraphUnitBase, TestConfiguration):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestApis, cls).setUpClass()
|
||||||
|
cls.conf = cfg.ConfigOpts()
|
||||||
|
cls.add_db(cls.conf)
|
||||||
|
|
||||||
def test_get_alarms_with_admin_project(self):
|
def test_get_alarms_with_admin_project(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = AlarmApis(graph, None)
|
apis = AlarmApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_1', 'is_admin': True}
|
ctx = {'tenant': 'project_1', 'is_admin': True}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -55,7 +69,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
def test_get_alarms_with_not_admin_project(self):
|
def test_get_alarms_with_not_admin_project(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = AlarmApis(graph, None)
|
apis = AlarmApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -69,7 +83,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
def test_get_alarm_counts_with_not_admin_project(self):
|
def test_get_alarm_counts_with_not_admin_project(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = AlarmApis(graph, None)
|
apis = AlarmApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -86,7 +100,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
def test_get_alarms_with_all_tenants(self):
|
def test_get_alarms_with_all_tenants(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = AlarmApis(graph, None)
|
apis = AlarmApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_1', 'is_admin': False}
|
ctx = {'tenant': 'project_1', 'is_admin': False}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -100,7 +114,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
def test_get_alarm_counts_with_all_tenants(self):
|
def test_get_alarm_counts_with_all_tenants(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = AlarmApis(graph, None)
|
apis = AlarmApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_1', 'is_admin': False}
|
ctx = {'tenant': 'project_1', 'is_admin': False}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -117,7 +131,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
def test_get_rca_with_admin_project(self):
|
def test_get_rca_with_admin_project(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = RcaApis(graph, None)
|
apis = RcaApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_1', 'is_admin': True}
|
ctx = {'tenant': 'project_1', 'is_admin': True}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -131,7 +145,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
def test_get_rca_with_not_admin_project(self):
|
def test_get_rca_with_not_admin_project(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = RcaApis(graph, None)
|
apis = RcaApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -147,7 +161,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
def test_get_rca_with_not_admin_bla_project(self):
|
def test_get_rca_with_not_admin_bla_project(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = RcaApis(graph, None)
|
apis = RcaApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_2', 'is_admin': False}
|
ctx = {'tenant': 'project_2', 'is_admin': False}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -161,7 +175,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
def test_get_rca_with_all_tenants(self):
|
def test_get_rca_with_all_tenants(self):
|
||||||
# Setup
|
# Setup
|
||||||
graph = self._create_graph()
|
graph = self._create_graph()
|
||||||
apis = RcaApis(graph, None)
|
apis = RcaApis(graph, self.conf, self._db)
|
||||||
ctx = {'tenant': 'project_1', 'is_admin': False}
|
ctx = {'tenant': 'project_1', 'is_admin': False}
|
||||||
|
|
||||||
# Action
|
# Action
|
||||||
@ -433,6 +447,7 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
|
|
||||||
def _create_graph(self):
|
def _create_graph(self):
|
||||||
graph = NXGraph('Multi tenancy graph')
|
graph = NXGraph('Multi tenancy graph')
|
||||||
|
self._add_alarm_persistency_subscription(graph)
|
||||||
|
|
||||||
# create vertices
|
# create vertices
|
||||||
cluster_vertex = create_cluster_placeholder_vertex()
|
cluster_vertex = create_cluster_placeholder_vertex()
|
||||||
@ -459,40 +474,54 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
VProps.NAME: 'host_1',
|
VProps.NAME: 'host_1',
|
||||||
VProps.RESOURCE_ID: 'host_1',
|
VProps.RESOURCE_ID: 'host_1',
|
||||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||||
|
OperationalAlarmSeverity.SEVERE,
|
||||||
|
VProps.VITRAGE_AGGREGATED_SEVERITY:
|
||||||
OperationalAlarmSeverity.SEVERE})
|
OperationalAlarmSeverity.SEVERE})
|
||||||
alarm_on_instance_1_vertex = self._create_alarm(
|
alarm_on_instance_1_vertex = self._create_alarm(
|
||||||
'alarm_on_instance_1',
|
'alarm_on_instance_1',
|
||||||
'deduced_alarm',
|
'deduced_alarm',
|
||||||
project_id='project_1',
|
project_id='project_1',
|
||||||
|
vitrage_resource_project_id='project_1',
|
||||||
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||||
VProps.NAME: 'instance_1',
|
VProps.NAME: 'instance_1',
|
||||||
VProps.RESOURCE_ID: 'sdg7849ythksjdg',
|
VProps.RESOURCE_ID: 'sdg7849ythksjdg',
|
||||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||||
|
OperationalAlarmSeverity.SEVERE,
|
||||||
|
VProps.VITRAGE_AGGREGATED_SEVERITY:
|
||||||
OperationalAlarmSeverity.SEVERE})
|
OperationalAlarmSeverity.SEVERE})
|
||||||
alarm_on_instance_2_vertex = self._create_alarm(
|
alarm_on_instance_2_vertex = self._create_alarm(
|
||||||
'alarm_on_instance_2',
|
'alarm_on_instance_2',
|
||||||
'deduced_alarm',
|
'deduced_alarm',
|
||||||
|
vitrage_resource_project_id='project_1',
|
||||||
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||||
VProps.NAME: 'instance_2',
|
VProps.NAME: 'instance_2',
|
||||||
VProps.RESOURCE_ID: 'nbfhsdugf',
|
VProps.RESOURCE_ID: 'nbfhsdugf',
|
||||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||||
|
OperationalAlarmSeverity.WARNING,
|
||||||
|
VProps.VITRAGE_AGGREGATED_SEVERITY:
|
||||||
OperationalAlarmSeverity.WARNING})
|
OperationalAlarmSeverity.WARNING})
|
||||||
alarm_on_instance_3_vertex = self._create_alarm(
|
alarm_on_instance_3_vertex = self._create_alarm(
|
||||||
'alarm_on_instance_3',
|
'alarm_on_instance_3',
|
||||||
'deduced_alarm',
|
'deduced_alarm',
|
||||||
project_id='project_2',
|
project_id='project_2',
|
||||||
|
vitrage_resource_project_id='project_2',
|
||||||
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||||
VProps.NAME: 'instance_3',
|
VProps.NAME: 'instance_3',
|
||||||
VProps.RESOURCE_ID: 'nbffhsdasdugf',
|
VProps.RESOURCE_ID: 'nbffhsdasdugf',
|
||||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||||
|
OperationalAlarmSeverity.CRITICAL,
|
||||||
|
VProps.VITRAGE_AGGREGATED_SEVERITY:
|
||||||
OperationalAlarmSeverity.CRITICAL})
|
OperationalAlarmSeverity.CRITICAL})
|
||||||
alarm_on_instance_4_vertex = self._create_alarm(
|
alarm_on_instance_4_vertex = self._create_alarm(
|
||||||
'alarm_on_instance_4',
|
'alarm_on_instance_4',
|
||||||
'deduced_alarm',
|
'deduced_alarm',
|
||||||
|
vitrage_resource_project_id='project_2',
|
||||||
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
metadata={VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||||
VProps.NAME: 'instance_4',
|
VProps.NAME: 'instance_4',
|
||||||
VProps.RESOURCE_ID: 'ngsuy76hgd87f',
|
VProps.RESOURCE_ID: 'ngsuy76hgd87f',
|
||||||
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
VProps.VITRAGE_OPERATIONAL_SEVERITY:
|
||||||
|
OperationalAlarmSeverity.WARNING,
|
||||||
|
VProps.VITRAGE_AGGREGATED_SEVERITY:
|
||||||
OperationalAlarmSeverity.WARNING})
|
OperationalAlarmSeverity.WARNING})
|
||||||
|
|
||||||
# create links
|
# create links
|
||||||
@ -500,63 +529,78 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
cluster_vertex.vertex_id,
|
cluster_vertex.vertex_id,
|
||||||
zone_vertex.vertex_id,
|
zone_vertex.vertex_id,
|
||||||
EdgeLabel.CONTAINS))
|
EdgeLabel.CONTAINS,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
zone_vertex.vertex_id,
|
zone_vertex.vertex_id,
|
||||||
host_vertex.vertex_id,
|
host_vertex.vertex_id,
|
||||||
EdgeLabel.CONTAINS))
|
EdgeLabel.CONTAINS,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
host_vertex.vertex_id,
|
host_vertex.vertex_id,
|
||||||
instance_1_vertex.vertex_id,
|
instance_1_vertex.vertex_id,
|
||||||
EdgeLabel.CONTAINS))
|
EdgeLabel.CONTAINS,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
host_vertex.vertex_id,
|
host_vertex.vertex_id,
|
||||||
instance_2_vertex.vertex_id,
|
instance_2_vertex.vertex_id,
|
||||||
EdgeLabel.CONTAINS))
|
EdgeLabel.CONTAINS,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
host_vertex.vertex_id,
|
host_vertex.vertex_id,
|
||||||
instance_3_vertex.vertex_id,
|
instance_3_vertex.vertex_id,
|
||||||
EdgeLabel.CONTAINS))
|
EdgeLabel.CONTAINS,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
host_vertex.vertex_id,
|
host_vertex.vertex_id,
|
||||||
instance_4_vertex.vertex_id,
|
instance_4_vertex.vertex_id,
|
||||||
EdgeLabel.CONTAINS))
|
EdgeLabel.CONTAINS,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_host_vertex.vertex_id,
|
alarm_on_host_vertex.vertex_id,
|
||||||
host_vertex.vertex_id,
|
host_vertex.vertex_id,
|
||||||
EdgeLabel.ON))
|
EdgeLabel.ON,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_instance_1_vertex.vertex_id,
|
alarm_on_instance_1_vertex.vertex_id,
|
||||||
instance_1_vertex.vertex_id,
|
instance_1_vertex.vertex_id,
|
||||||
EdgeLabel.ON))
|
EdgeLabel.ON,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_instance_2_vertex.vertex_id,
|
alarm_on_instance_2_vertex.vertex_id,
|
||||||
instance_2_vertex.vertex_id,
|
instance_2_vertex.vertex_id,
|
||||||
EdgeLabel.ON))
|
EdgeLabel.ON,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_instance_3_vertex.vertex_id,
|
alarm_on_instance_3_vertex.vertex_id,
|
||||||
instance_3_vertex.vertex_id,
|
instance_3_vertex.vertex_id,
|
||||||
EdgeLabel.ON))
|
EdgeLabel.ON,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_instance_4_vertex.vertex_id,
|
alarm_on_instance_4_vertex.vertex_id,
|
||||||
instance_4_vertex.vertex_id,
|
instance_4_vertex.vertex_id,
|
||||||
EdgeLabel.ON))
|
EdgeLabel.ON,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_host_vertex.vertex_id,
|
alarm_on_host_vertex.vertex_id,
|
||||||
alarm_on_instance_1_vertex.vertex_id,
|
alarm_on_instance_1_vertex.vertex_id,
|
||||||
EdgeLabel.CAUSES))
|
EdgeLabel.CAUSES,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_host_vertex.vertex_id,
|
alarm_on_host_vertex.vertex_id,
|
||||||
alarm_on_instance_2_vertex.vertex_id,
|
alarm_on_instance_2_vertex.vertex_id,
|
||||||
EdgeLabel.CAUSES))
|
EdgeLabel.CAUSES,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_host_vertex.vertex_id,
|
alarm_on_host_vertex.vertex_id,
|
||||||
alarm_on_instance_3_vertex.vertex_id,
|
alarm_on_instance_3_vertex.vertex_id,
|
||||||
EdgeLabel.CAUSES))
|
EdgeLabel.CAUSES,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
edges.append(graph_utils.create_edge(
|
edges.append(graph_utils.create_edge(
|
||||||
alarm_on_host_vertex.vertex_id,
|
alarm_on_host_vertex.vertex_id,
|
||||||
alarm_on_instance_4_vertex.vertex_id,
|
alarm_on_instance_4_vertex.vertex_id,
|
||||||
EdgeLabel.CAUSES))
|
EdgeLabel.CAUSES,
|
||||||
|
update_timestamp=str(utcnow())))
|
||||||
|
|
||||||
# add vertices to graph
|
# add vertices to graph
|
||||||
graph.add_vertex(cluster_vertex)
|
graph.add_vertex(cluster_vertex)
|
||||||
@ -577,3 +621,26 @@ class TestApis(TestEntityGraphUnitBase):
|
|||||||
graph.add_edge(edge)
|
graph.add_edge(edge)
|
||||||
|
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
|
def _add_alarm_persistency_subscription(self, graph):
|
||||||
|
|
||||||
|
self._db.alarms.delete()
|
||||||
|
self._db.changes.delete()
|
||||||
|
self._db.edges.delete()
|
||||||
|
persistor_endpoint = VitragePersistorEndpoint(self._db)
|
||||||
|
|
||||||
|
def callback(before, curr, is_vertex, graph):
|
||||||
|
notification_types = PersistNotifier._get_notification_type(
|
||||||
|
before, curr, is_vertex)
|
||||||
|
if not is_vertex:
|
||||||
|
curr = edge_copy(
|
||||||
|
curr.source_id, curr.target_id, curr.label,
|
||||||
|
curr.properties)
|
||||||
|
curr.properties[EdgeProperties.SOURCE_ID] = curr.source_id
|
||||||
|
curr.properties[EdgeProperties.TARGET_ID] = curr.target_id
|
||||||
|
|
||||||
|
for notification_type in notification_types:
|
||||||
|
persistor_endpoint.process_event(notification_type,
|
||||||
|
curr.properties)
|
||||||
|
|
||||||
|
graph.subscribe(callback)
|
||||||
|
@ -31,6 +31,7 @@ from vitrage.opts import register_opts
|
|||||||
from vitrage.tests import base
|
from vitrage.tests import base
|
||||||
from vitrage.tests.mocks import mock_driver as mock_sync
|
from vitrage.tests.mocks import mock_driver as mock_sync
|
||||||
from vitrage.tests.mocks import utils
|
from vitrage.tests.mocks import utils
|
||||||
|
from vitrage.utils.datetime import utcnow
|
||||||
|
|
||||||
|
|
||||||
class TestEntityGraphUnitBase(base.BaseTest):
|
class TestEntityGraphUnitBase(base.BaseTest):
|
||||||
@ -153,17 +154,23 @@ class TestEntityGraphUnitBase(base.BaseTest):
|
|||||||
return events_list[0]
|
return events_list[0]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_alarm(vitrage_id, alarm_type, project_id=None, metadata=None):
|
def _create_alarm(vitrage_id,
|
||||||
|
alarm_type,
|
||||||
|
project_id=None,
|
||||||
|
vitrage_resource_project_id=None,
|
||||||
|
metadata=None):
|
||||||
return graph_utils.create_vertex(
|
return graph_utils.create_vertex(
|
||||||
vitrage_id,
|
vitrage_id,
|
||||||
vitrage_category=EntityCategory.ALARM,
|
vitrage_category=EntityCategory.ALARM,
|
||||||
vitrage_type=alarm_type,
|
vitrage_type=alarm_type,
|
||||||
vitrage_sample_timestamp=None,
|
vitrage_sample_timestamp=None,
|
||||||
|
update_timestamp=str(utcnow()),
|
||||||
vitrage_is_deleted=False,
|
vitrage_is_deleted=False,
|
||||||
vitrage_is_placeholder=False,
|
vitrage_is_placeholder=False,
|
||||||
entity_id=vitrage_id,
|
entity_id=vitrage_id,
|
||||||
entity_state='active',
|
entity_state='active',
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
|
vitrage_resource_project_id=vitrage_resource_project_id,
|
||||||
metadata=metadata
|
metadata=metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -174,6 +181,7 @@ class TestEntityGraphUnitBase(base.BaseTest):
|
|||||||
vitrage_category=EntityCategory.RESOURCE,
|
vitrage_category=EntityCategory.RESOURCE,
|
||||||
vitrage_type=resource_type,
|
vitrage_type=resource_type,
|
||||||
vitrage_sample_timestamp=None,
|
vitrage_sample_timestamp=None,
|
||||||
|
update_timestamp=str(utcnow()),
|
||||||
vitrage_is_deleted=False,
|
vitrage_is_deleted=False,
|
||||||
vitrage_is_placeholder=False,
|
vitrage_is_placeholder=False,
|
||||||
entity_id=vitrage_id,
|
entity_id=vitrage_id,
|
||||||
|
@ -25,7 +25,7 @@ from vitrage.common.constants import EntityCategory
|
|||||||
from vitrage.common.constants import NotifierEventTypes as NType
|
from vitrage.common.constants import NotifierEventTypes as NType
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
||||||
from vitrage.entity_graph.processor.notifier import _get_notification_type
|
from vitrage.entity_graph.processor.notifier import GraphNotifier as GN
|
||||||
from vitrage.evaluator.actions import evaluator_event_transformer as evaluator
|
from vitrage.evaluator.actions import evaluator_event_transformer as evaluator
|
||||||
from vitrage.graph import Vertex
|
from vitrage.graph import Vertex
|
||||||
from vitrage.tests import base
|
from vitrage.tests import base
|
||||||
@ -90,59 +90,59 @@ class GraphTest(base.BaseTest):
|
|||||||
return lst[0] if len(lst) > 0 else None
|
return lst[0] if len(lst) > 0 else None
|
||||||
|
|
||||||
def test_notification_type_new_alarm(self):
|
def test_notification_type_new_alarm(self):
|
||||||
ret = _get_notification_type(None, deduced_alarm, True)
|
ret = GN._get_notification_type(None, deduced_alarm, True)
|
||||||
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
|
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
'new alarm should notify activate')
|
'new alarm should notify activate')
|
||||||
|
|
||||||
ret = _get_notification_type(None, non_deduced_alarm, True)
|
ret = GN._get_notification_type(None, non_deduced_alarm, True)
|
||||||
self.assertIsNone(self.get_first(ret),
|
self.assertIsNone(self.get_first(ret),
|
||||||
'alarm that is not a deduced alarm')
|
'alarm that is not a deduced alarm')
|
||||||
|
|
||||||
def test_notification_type_deleted_alarm(self):
|
def test_notification_type_deleted_alarm(self):
|
||||||
ret = _get_notification_type(deduced_alarm, deleted_alarm, True)
|
ret = GN._get_notification_type(deduced_alarm, deleted_alarm, True)
|
||||||
self.assertEqual(NType.DEACTIVATE_DEDUCED_ALARM_EVENT,
|
self.assertEqual(NType.DEACTIVATE_DEDUCED_ALARM_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
'deleted alarm should notify deactivate')
|
'deleted alarm should notify deactivate')
|
||||||
|
|
||||||
def test_notification_type_resource_vertex(self):
|
def test_notification_type_resource_vertex(self):
|
||||||
ret = _get_notification_type(None, resource, True)
|
ret = GN._get_notification_type(None, resource, True)
|
||||||
self.assertIsNone(self.get_first(ret),
|
self.assertIsNone(self.get_first(ret),
|
||||||
'any non alarm vertex should be ignored')
|
'any non alarm vertex should be ignored')
|
||||||
|
|
||||||
def test_notification_type_updated_alarm(self):
|
def test_notification_type_updated_alarm(self):
|
||||||
ret = _get_notification_type(deduced_alarm, deduced_alarm, True)
|
ret = GN._get_notification_type(deduced_alarm, deduced_alarm, True)
|
||||||
self.assertIsNone(self.get_first(ret),
|
self.assertIsNone(self.get_first(ret),
|
||||||
'A not new alarm vertex should be ignored')
|
'A not new alarm vertex should be ignored')
|
||||||
|
|
||||||
ret = _get_notification_type(deleted_alarm, deduced_alarm, True)
|
ret = GN._get_notification_type(deleted_alarm, deduced_alarm, True)
|
||||||
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
|
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
'old alarm become not deleted should notify activate')
|
'old alarm become not deleted should notify activate')
|
||||||
|
|
||||||
ret = _get_notification_type(placeholder_alarm, deduced_alarm, True)
|
ret = GN._get_notification_type(placeholder_alarm, deduced_alarm, True)
|
||||||
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
|
self.assertEqual(NType.ACTIVATE_DEDUCED_ALARM_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
'placeholder become active should notify activate')
|
'placeholder become active should notify activate')
|
||||||
|
|
||||||
def test_notification_type_placeholder_alarm(self):
|
def test_notification_type_placeholder_alarm(self):
|
||||||
ret = _get_notification_type(None, placeholder_alarm, True)
|
ret = GN._get_notification_type(None, placeholder_alarm, True)
|
||||||
self.assertIsNone(self.get_first(ret),
|
self.assertIsNone(self.get_first(ret),
|
||||||
'A not new alarm vertex should be ignored')
|
'A not new alarm vertex should be ignored')
|
||||||
|
|
||||||
def test_notification_type_new_host(self):
|
def test_notification_type_new_host(self):
|
||||||
ret = _get_notification_type(None, forced_down_host, True)
|
ret = GN._get_notification_type(None, forced_down_host, True)
|
||||||
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
|
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
'new host with forced_down should notify activate')
|
'new host with forced_down should notify activate')
|
||||||
|
|
||||||
ret = _get_notification_type(None, host, True)
|
ret = GN._get_notification_type(None, host, True)
|
||||||
self.assertIsNone(self.get_first(ret), 'host without forced_down')
|
self.assertIsNone(self.get_first(ret), 'host without forced_down')
|
||||||
|
|
||||||
def test_notification_type_deleted_host(self):
|
def test_notification_type_deleted_host(self):
|
||||||
deleted_host = copy.deepcopy(forced_down_host)
|
deleted_host = copy.deepcopy(forced_down_host)
|
||||||
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
||||||
ret = _get_notification_type(forced_down_host, deleted_host, True)
|
ret = GN._get_notification_type(forced_down_host, deleted_host, True)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
NType.DEACTIVATE_MARK_DOWN_EVENT,
|
NType.DEACTIVATE_MARK_DOWN_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
@ -150,7 +150,7 @@ class GraphTest(base.BaseTest):
|
|||||||
|
|
||||||
deleted_host = copy.deepcopy(host)
|
deleted_host = copy.deepcopy(host)
|
||||||
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
||||||
ret = _get_notification_type(forced_down_host, deleted_host, True)
|
ret = GN._get_notification_type(forced_down_host, deleted_host, True)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
NType.DEACTIVATE_MARK_DOWN_EVENT,
|
NType.DEACTIVATE_MARK_DOWN_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
@ -158,32 +158,34 @@ class GraphTest(base.BaseTest):
|
|||||||
|
|
||||||
deleted_host = copy.deepcopy(host)
|
deleted_host = copy.deepcopy(host)
|
||||||
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
||||||
ret = _get_notification_type(host, deleted_host, True)
|
ret = GN._get_notification_type(host, deleted_host, True)
|
||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
'deleted host without forced_down should not notify')
|
'deleted host without forced_down should not notify')
|
||||||
|
|
||||||
def test_notification_type_updated_host(self):
|
def test_notification_type_updated_host(self):
|
||||||
ret = _get_notification_type(forced_down_host, forced_down_host, True)
|
ret = GN._get_notification_type(
|
||||||
|
forced_down_host, forced_down_host, True)
|
||||||
self.assertIsNone(self.get_first(ret),
|
self.assertIsNone(self.get_first(ret),
|
||||||
'A not new host should be ignored')
|
'A not new host should be ignored')
|
||||||
|
|
||||||
deleted_host = copy.deepcopy(forced_down_host)
|
deleted_host = copy.deepcopy(forced_down_host)
|
||||||
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
||||||
ret = _get_notification_type(deleted_host, forced_down_host, True)
|
ret = GN._get_notification_type(deleted_host, forced_down_host, True)
|
||||||
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
|
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
'old host become not deleted should notify activate')
|
'old host become not deleted should notify activate')
|
||||||
|
|
||||||
deleted_host = copy.deepcopy(forced_down_host)
|
deleted_host = copy.deepcopy(forced_down_host)
|
||||||
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
deleted_host[VProps.VITRAGE_IS_DELETED] = True
|
||||||
ret = _get_notification_type(deleted_host, host, True)
|
ret = GN._get_notification_type(deleted_host, host, True)
|
||||||
self.assertIsNone(self.get_first(ret),
|
self.assertIsNone(self.get_first(ret),
|
||||||
'old host become not deleted should not notify')
|
'old host become not deleted should not notify')
|
||||||
|
|
||||||
placeholder_host = copy.deepcopy(forced_down_host)
|
placeholder_host = copy.deepcopy(forced_down_host)
|
||||||
placeholder_host[VProps.VITRAGE_IS_PLACEHOLDER] = True
|
placeholder_host[VProps.VITRAGE_IS_PLACEHOLDER] = True
|
||||||
ret = _get_notification_type(placeholder_host, forced_down_host, True)
|
ret = GN._get_notification_type(
|
||||||
|
placeholder_host, forced_down_host, True)
|
||||||
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
|
self.assertEqual(NType.ACTIVATE_MARK_DOWN_EVENT,
|
||||||
self.get_first(ret),
|
self.get_first(ret),
|
||||||
'placeholder become active should notify activate')
|
'placeholder become active should notify activate')
|
||||||
@ -191,6 +193,6 @@ class GraphTest(base.BaseTest):
|
|||||||
def test_notification_type_placeholder_host(self):
|
def test_notification_type_placeholder_host(self):
|
||||||
placeholder_host = copy.deepcopy(forced_down_host)
|
placeholder_host = copy.deepcopy(forced_down_host)
|
||||||
placeholder_host[VProps.VITRAGE_IS_PLACEHOLDER] = True
|
placeholder_host[VProps.VITRAGE_IS_PLACEHOLDER] = True
|
||||||
ret = _get_notification_type(None, placeholder_host, True)
|
ret = GN._get_notification_type(None, placeholder_host, True)
|
||||||
self.assertIsNone(self.get_first(ret),
|
self.assertIsNone(self.get_first(ret),
|
||||||
'A not new host vertex should be ignored')
|
'A not new host vertex should be ignored')
|
||||||
|
Loading…
Reference in New Issue
Block a user