Add kapacitor datasource
Enabling Kapacitor alerts and clusters in Vitrage Entity Graph. Goal is integrate TICK monitor system with vitrage Change-Id: I275135378a63b70c6859e5dfe36bdef9cb1b9c4b Story: 2005428 Task: 30461
This commit is contained in:
parent
1a789c12f0
commit
bc54094285
@ -28,6 +28,7 @@ Datasources
|
||||
k8s_datasource
|
||||
nova-config
|
||||
prometheus-datasource
|
||||
kapacitor-datasource
|
||||
|
||||
Notifiers
|
||||
---------
|
||||
|
112
doc/source/contributor/kapacitor-datasource.rst
Normal file
112
doc/source/contributor/kapacitor-datasource.rst
Normal file
@ -0,0 +1,112 @@
|
||||
Kapacitor-Vitrage
|
||||
=================
|
||||
|
||||
Kapacitor will send alert to vitrage by using [ exec-handle ], send to message queue topic of vitrage.
|
||||
https://docs.influxdata.com/kapacitor/v1.5/working/alerts/
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Copy the 'https://github.com/openstack/vitrage/tree/master/vitrage/datasources/kapacitor/auxliary/kapacitor_vitrage.py' script into the Kapacitor servers.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cp kapacitor_vitrage.py /etc/kapacitor/kapacitor_vitrage.py
|
||||
$ chmod 755 /etc/kapacitor/kapacitor_vitrage.py
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
|
||||
|
||||
1. Define topic , which use for alert publish to. Create file ``forward_to_vitrage.yaml``:
|
||||
|
||||
|
||||
| topic: forward_to_vitrage
|
||||
| id: forward_to_vitrage
|
||||
| kind: exec
|
||||
| options:
|
||||
| prog: '/usr/bin/python'
|
||||
| args: ['/etc/kapacitor/kapacitor_vitrage.py','rabbit://<rabbit_user>:<rabbit_pass>@controller']
|
||||
|
||||
**Note:** rabbit://<rabbit_user>:<rabbit_pass>@controller is Vitrage message bus url, ``rabbit_user:rabbit_pass`` for devstack rabbitmq is ``stackrabbit/secret``
|
||||
|
||||
Run command to define topic
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ kapacitor define-topic-handler ./forward_to_vitrage.yaml
|
||||
|
||||
|
||||
2. Assign your Task to topic, in Tick script define that alert, add in "alert()" step:
|
||||
|
||||
| ...
|
||||
| alert()
|
||||
| ...
|
||||
| .topic('forward_to_vitrage')
|
||||
|
||||
In case your Task already in topic and you don't want to add another, you only need to do: append 'exec handler' to TICK script which define it.
|
||||
|
||||
| ...
|
||||
| alert()
|
||||
| ...
|
||||
| .exec('/usr/bin/python', '/etc/kapacitor/kapacitor_vitrage.py', 'rabbit://<rabbit_user>:<rabbit_pass>@controller')
|
||||
|
||||
Run command define your task:
|
||||
|
||||
.. code::
|
||||
|
||||
$ kapacitor define <task_name> -tick <tick_script>
|
||||
|
||||
|
||||
Vitrage configuration:
|
||||
|
||||
1. Add kapacitor to list of datasources in ``/etc/vitrage/vitrage.conf``
|
||||
|
||||
.. code::
|
||||
|
||||
[datasources]
|
||||
types = kapacitor,zabbix,nova.host,nova.instance,nova.zone,static_physical,aodh,cinder.volume,neutron.network,neutron.port,heat.stack
|
||||
|
||||
2. Add section to ``/etc/vitrage/vitrage.conf``
|
||||
|
||||
.. code::
|
||||
|
||||
[kapacitor]
|
||||
config_file = /etc/vitrage/kapacitor_conf.yaml
|
||||
|
||||
3. Create ``/etc/vitrage/kapacitor_conf.yaml`` with this content
|
||||
|
||||
.. code ::
|
||||
|
||||
kapacitor:
|
||||
- alert:
|
||||
host: cloud.compute1 # hostname of host been raised alarm
|
||||
vitrage_resource:
|
||||
type: nova.host # resource type of enity vitrage
|
||||
name: compute-1 # resource name of enity vitrage
|
||||
- alert:
|
||||
host: compute-(.*)
|
||||
vitrage_resource:
|
||||
type: nova.host
|
||||
name: ${kapacitor_host}
|
||||
- alert:
|
||||
host: (.*)
|
||||
vitrage_resource:
|
||||
type: nova.instance
|
||||
name: ${kapacitor_host}
|
||||
|
||||
In example:
|
||||
alarm on host have hostname `cloud.compute1` will map to resource name `compute-1`,
|
||||
|
||||
alarm on host have hostname `compute-99` will map to resource name `compute-99`
|
||||
|
||||
Another alarm, like alarm on instance will map with resource type ``nova.instance`` and name equal with hostname of instance
|
||||
|
||||
4. Restart vitrage service in devstack/openstack
|
||||
|
||||
DONE
|
||||
----
|
||||
|
18
etc/vitrage/datasources_values/kapacitor.yaml
Normal file
18
etc/vitrage/datasources_values/kapacitor.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
category: ALARM
|
||||
values:
|
||||
- aggregated values:
|
||||
priority: 40
|
||||
original values:
|
||||
- name: critical
|
||||
operational_value: CRITICAL
|
||||
- aggregated values:
|
||||
priority: 20
|
||||
original values:
|
||||
- name: warning
|
||||
operational_value: WARNING
|
||||
- aggregated values:
|
||||
priority: 10
|
||||
original values:
|
||||
- name: OK
|
||||
operational_value: OK
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- A new ``Kapacitor Datasource`` was added, to handle alerts coming
|
||||
from Kapacitor. Kapacitor is an alarming engine in the TICK Stack.
|
||||
It is build on an Open Source core, processing metric of host or
|
||||
instance store in InfluxDB to export alerts.
|
42
vitrage/datasources/kapacitor/__init__.py
Normal file
42
vitrage/datasources/kapacitor/__init__.py
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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_config import cfg
|
||||
|
||||
from vitrage.common.constants import DatasourceOpts as DSOpts
|
||||
from vitrage.common.constants import UpdateMethod
|
||||
|
||||
KAPACITOR_DATASOURCE = 'kapacitor'
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt(DSOpts.TRANSFORMER,
|
||||
default='vitrage.datasources.kapacitor.transformer.'
|
||||
'KapacitorTransformer',
|
||||
help='Kapacitor transformer class path',
|
||||
required=True),
|
||||
cfg.StrOpt(DSOpts.DRIVER,
|
||||
default='vitrage.datasources.kapacitor.driver.'
|
||||
'KapacitorDriver',
|
||||
help='Kapacitor driver class path',
|
||||
required=True),
|
||||
cfg.StrOpt(DSOpts.UPDATE_METHOD,
|
||||
default=UpdateMethod.PUSH,
|
||||
help='None: updates only via Vitrage periodic snapshots.'
|
||||
'Pull: updates periodically.'
|
||||
'Push: updates by getting notifications from the'
|
||||
' datasource itself.',
|
||||
required=True),
|
||||
cfg.StrOpt(DSOpts.CONFIG_FILE, default='/etc/vitrage/kapacitor_conf.yaml',
|
||||
help='Kapacitor configuration file')
|
||||
]
|
109
vitrage/datasources/kapacitor/auxiliary/kapacitor_vitrage.py
Normal file
109
vitrage/datasources/kapacitor/auxiliary/kapacitor_vitrage.py
Normal file
@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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 argparse
|
||||
import json
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging as messaging
|
||||
from oslo_utils import uuidutils
|
||||
import socket
|
||||
import sys
|
||||
|
||||
'''
|
||||
Expected input:
|
||||
Send To: rabbit://userrabbit:passrabbit@rabbit_host:5672/
|
||||
EVENT_TYPE: {ALARM.STATUS} || kapacitor.alarm.critical warning info or ok
|
||||
Alarm:
|
||||
id: mem high-host=controller
|
||||
message: mem high
|
||||
details: {{ .Level }} {{alarm_name}}...
|
||||
times: 2019-04-10T12:18:00Z
|
||||
duration: 0
|
||||
priority: CRITICAL
|
||||
previousLevel: OK
|
||||
host: host1
|
||||
'''
|
||||
|
||||
|
||||
LOG_FILE = '/var/log/kapacitor/kapacitor_vitrage.log'
|
||||
LOG_MAX_SIZE = 10000000
|
||||
LOG_FORMAT = '%(asctime)s.%(msecs).03d %(name)s[%(process)d] %(threadName)s %' \
|
||||
'(levelname)s - %(message)s'
|
||||
LOG_DATE_FMT = '%Y.%m.%d %H:%M:%S'
|
||||
KAPACITOR_EVENT_TYPE = 'kapacitor.alarm'
|
||||
|
||||
debug = False
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('sendto', help='url')
|
||||
args = parser.parse_args()
|
||||
data = sys.stdin.readlines()[0]
|
||||
transport_url = args.sendto
|
||||
transport = messaging.get_notification_transport(cfg.CONF, transport_url)
|
||||
|
||||
driver = 'messagingv2'
|
||||
publisher = 'kapacitor_%s' % socket.gethostname()
|
||||
notifier = messaging.Notifier(transport,
|
||||
driver=driver,
|
||||
publisher_id=publisher,
|
||||
topics=['vitrage_notifications'])
|
||||
alarm = json.loads(data)
|
||||
host = alarm['data']['series'][0]['tags']['host']
|
||||
priority = alarm['level'].lower()
|
||||
alarm.update({'host': host,
|
||||
'priority': priority})
|
||||
alarm.pop('data', None)
|
||||
alarm_status = alarm['level'].lower()
|
||||
event_type = '%s.%s' % (KAPACITOR_EVENT_TYPE, alarm_status)
|
||||
logging.info('Send to: %s', transport_url)
|
||||
logging.info('BODY:\n----\n%s\n', data)
|
||||
logging.info('PUBLISHER: %s', publisher)
|
||||
logging.info('EVENT_TYPE: %s', event_type)
|
||||
logging.info('\nALARM:\n%s', alarm)
|
||||
notifier.info(ctxt={'message_id': uuidutils.generate_uuid(),
|
||||
'publisher_id': publisher},
|
||||
event_type=event_type,
|
||||
payload=alarm)
|
||||
logging.info('MESSAGE SENT..')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
log = logging.getLogger()
|
||||
|
||||
if debug:
|
||||
log.setLevel(logging.DEBUG)
|
||||
else:
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
handler = RotatingFileHandler(filename=LOG_FILE,
|
||||
maxBytes=LOG_MAX_SIZE,
|
||||
backupCount=3)
|
||||
fmt = logging.Formatter(LOG_FORMAT, LOG_DATE_FMT)
|
||||
handler.setFormatter(fmt)
|
||||
log.addHandler(handler)
|
||||
|
||||
logging.info('***----------Script start-----------***')
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
logging.exception('MESSAGE WAS NOT SENT - %s' % e)
|
91
vitrage/datasources/kapacitor/config.py
Normal file
91
vitrage/datasources/kapacitor/config.py
Normal file
@ -0,0 +1,91 @@
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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 re
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from vitrage.common.constants import DatasourceOpts as DSOpts
|
||||
from vitrage.utils import file as file_utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
KAPACITOR_HOST = 'kapacitor_host'
|
||||
KAPACITOR = 'kapacitor'
|
||||
HOST = 'host'
|
||||
TYPE = 'type'
|
||||
NAME = 'name'
|
||||
ALERT = 'alert'
|
||||
VITRAGE_RESOURCE = 'vitrage_resource'
|
||||
|
||||
|
||||
class KapacitorConfig(object):
|
||||
def __init__(self, conf):
|
||||
try:
|
||||
kapacitor_config_file = conf.kapacitor[DSOpts.CONFIG_FILE]
|
||||
kapacitor_config = file_utils.load_yaml_file(kapacitor_config_file)
|
||||
kapacitor = kapacitor_config[KAPACITOR]
|
||||
|
||||
self.mappings = [self._create_mapping(config)
|
||||
for config in kapacitor]
|
||||
except Exception:
|
||||
LOG.exception('Failed in init.')
|
||||
self.mappings = []
|
||||
|
||||
@staticmethod
|
||||
def _create_mapping(config):
|
||||
return KapacitorHostMapping(config[ALERT][HOST],
|
||||
config[VITRAGE_RESOURCE][TYPE],
|
||||
config[VITRAGE_RESOURCE][NAME])
|
||||
|
||||
def get_vitrage_resource(self, kapacitor_host):
|
||||
"""Get Resource type and name for the given kapacitor host name
|
||||
|
||||
Go over the configuration mappings one by one, and return the resource
|
||||
by the first mapping that applies to kapacitor host name.
|
||||
|
||||
:param kapacitor_host: kapacitor host name
|
||||
:return: Vitrage (resource type, resource name)
|
||||
"""
|
||||
for mapping in self.mappings:
|
||||
mapped_resource = mapping.map(kapacitor_host)
|
||||
if mapped_resource:
|
||||
return mapped_resource
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class KapacitorHostMapping(object):
|
||||
KAPACITOR_HOST_NAME = '${' + KAPACITOR_HOST + '}'
|
||||
|
||||
def __init__(self, kapacitor_host_regexp, resource_type, resource_name):
|
||||
self.kapacitor_host_regexp = re.compile(kapacitor_host_regexp)
|
||||
self.resource_type = resource_type
|
||||
self.resource_name = resource_name
|
||||
|
||||
def map(self, kapacitor_host):
|
||||
"""Check if the mapping applies to this service
|
||||
|
||||
:param kapacitor_host: kapacitor host name
|
||||
:return: a tuple of (resource type, resource name)
|
||||
In case kapacitor_host_regexp is ${kapacitor_host},
|
||||
return kapacitor host name as the resource name
|
||||
"""
|
||||
|
||||
if kapacitor_host and self.kapacitor_host_regexp.match(kapacitor_host):
|
||||
resource_name = \
|
||||
kapacitor_host if self.resource_name == self.KAPACITOR_HOST_NAME \
|
||||
else self.resource_name
|
||||
return self.resource_type, resource_name
|
||||
else:
|
||||
return None
|
94
vitrage/datasources/kapacitor/driver.py
Normal file
94
vitrage/datasources/kapacitor/driver.py
Normal file
@ -0,0 +1,94 @@
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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 collections import namedtuple
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from vitrage.common.constants import DatasourceAction
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.datasources.alarm_driver_base import AlarmDriverBase
|
||||
from vitrage.datasources.kapacitor.config import KapacitorConfig
|
||||
from vitrage.datasources.kapacitor import KAPACITOR_DATASOURCE
|
||||
from vitrage.datasources.kapacitor.properties import KapacitorProperties \
|
||||
as KProps
|
||||
from vitrage.datasources.kapacitor.properties import KapacitorState
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class KapacitorDriver(AlarmDriverBase):
|
||||
ServiceKey = namedtuple('ServiceKey', ['hostname', 'alarmid'])
|
||||
conf_map = None
|
||||
|
||||
def __init__(self, conf):
|
||||
super(KapacitorDriver, self).__init__()
|
||||
self.cfg = conf
|
||||
|
||||
if not KapacitorDriver.conf_map:
|
||||
self.conf_map = KapacitorConfig(conf)
|
||||
self._client = None
|
||||
|
||||
@staticmethod
|
||||
def get_event_types():
|
||||
return ['kapacitor.alarm.ok',
|
||||
'kapacitor.alarm.info',
|
||||
'kapacitor.alarm.warning',
|
||||
'kapacitor.alarm.critical']
|
||||
|
||||
def _vitrage_type(self):
|
||||
return KAPACITOR_DATASOURCE
|
||||
|
||||
def _alarm_key(self, alarm):
|
||||
return self.ServiceKey(hostname=alarm[KProps.RESOURCE_NAME],
|
||||
alarmid=alarm[KProps.ID])
|
||||
|
||||
def _enrich_alarms(self, alarms):
|
||||
"""Enrich kapacitor alarm using kapacitor configuration file
|
||||
|
||||
Converting Kapacitor host name to Vitrage resource type and name
|
||||
It is function of get_all for pulling method
|
||||
Not implement yet
|
||||
"""
|
||||
pass
|
||||
|
||||
def enrich_event(self, event, event_type):
|
||||
event[DSProps.EVENT_TYPE] = event_type
|
||||
|
||||
kapacitor_host = event[KProps.HOST]
|
||||
vitrage_resource = self.conf_map.get_vitrage_resource(kapacitor_host)
|
||||
event[KProps.RESOURCE_TYPE] = \
|
||||
vitrage_resource[0] if vitrage_resource else None
|
||||
event[KProps.RESOURCE_NAME] = \
|
||||
vitrage_resource[1] if vitrage_resource else None
|
||||
return KapacitorDriver.make_pickleable([event], KAPACITOR_DATASOURCE,
|
||||
DatasourceAction.UPDATE)[0]
|
||||
|
||||
def _is_erroneous(self, alarm):
|
||||
return alarm and alarm[KProps.PRIORITY] != KapacitorState.OK
|
||||
|
||||
def _status_changed(self, new_alarm, old_alarm):
|
||||
return new_alarm and old_alarm and \
|
||||
not new_alarm[KProps.PRIORITY] == old_alarm[KProps.PRIORITY]
|
||||
|
||||
def _is_valid(self, alarm):
|
||||
return alarm[KProps.RESOURCE_TYPE] is not None and \
|
||||
alarm[KProps.RESOURCE_NAME] is not None
|
||||
|
||||
def _get_alarms(self):
|
||||
"""Query all alarm and send to vitrage
|
||||
|
||||
Not implement yet
|
||||
"""
|
||||
return []
|
32
vitrage/datasources/kapacitor/properties.py
Normal file
32
vitrage/datasources/kapacitor/properties.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class KapacitorProperties(object):
|
||||
ID = 'id'
|
||||
RESOURCE_TYPE = 'resource_type'
|
||||
RESOURCE_NAME = 'resource_name'
|
||||
DETAILS = 'details'
|
||||
STATUS = 'status'
|
||||
HOST = 'host'
|
||||
PRIORITY = 'priority'
|
||||
TIME = 'time'
|
||||
MESSAGE = 'message'
|
||||
|
||||
|
||||
class KapacitorState(object):
|
||||
OK = 'ok'
|
||||
INFO = 'info'
|
||||
WARNING = 'warning'
|
||||
CRITICAL = 'critical'
|
110
vitrage/datasources/kapacitor/transformer.py
Normal file
110
vitrage/datasources/kapacitor/transformer.py
Normal file
@ -0,0 +1,110 @@
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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 datetime import datetime
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import EdgeLabel
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.datasources.alarm_transformer_base import \
|
||||
AlarmTransformerBase
|
||||
from vitrage.datasources.kapacitor import KAPACITOR_DATASOURCE
|
||||
from vitrage.datasources.kapacitor.properties import KapacitorProperties \
|
||||
as KProps
|
||||
from vitrage.datasources.kapacitor.properties import KapacitorState
|
||||
from vitrage.datasources import transformer_base as tbase
|
||||
import vitrage.graph.utils as graph_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KapacitorTransformer(AlarmTransformerBase):
|
||||
|
||||
def __init__(self, transformers, conf):
|
||||
super(KapacitorTransformer, self).__init__(transformers, conf)
|
||||
|
||||
def _create_snapshot_entity_vertex(self, entity_event):
|
||||
return self._create_vertex(entity_event)
|
||||
|
||||
def _create_update_entity_vertex(self, entity_event):
|
||||
return self._create_vertex(entity_event)
|
||||
|
||||
def _create_snapshot_neighbors(self, entity_event):
|
||||
return self._create_kapacitor_neighbors(entity_event)
|
||||
|
||||
def _create_update_neighbors(self, entity_event):
|
||||
return self._create_kapacitor_neighbors(entity_event)
|
||||
|
||||
def _create_entity_key(self, entity_event):
|
||||
"""the unique key of this entity"""
|
||||
entity_type = entity_event[DSProps.ENTITY_TYPE]
|
||||
alarm_id = entity_event[KProps.ID]
|
||||
resource_name = entity_event[KProps.RESOURCE_NAME]
|
||||
return tbase.build_key((EntityCategory.ALARM,
|
||||
entity_type,
|
||||
resource_name,
|
||||
alarm_id))
|
||||
|
||||
@staticmethod
|
||||
def get_vitrage_type():
|
||||
return KAPACITOR_DATASOURCE
|
||||
|
||||
def _create_vertex(self, entity_event):
|
||||
update_timestamp = str(datetime.strptime(
|
||||
entity_event[KProps.TIME], tbase.TIMESTAMP_FORMAT))
|
||||
|
||||
vitrage_sample_timestamp = entity_event[DSProps.SAMPLE_DATE]
|
||||
|
||||
metadata = {
|
||||
VProps.NAME: entity_event[KProps.MESSAGE],
|
||||
VProps.SEVERITY: entity_event[KProps.PRIORITY],
|
||||
VProps.RAWTEXT: entity_event[KProps.DETAILS],
|
||||
VProps.RESOURCE_NAME: entity_event[KProps.RESOURCE_NAME],
|
||||
}
|
||||
|
||||
return graph_utils.create_vertex(
|
||||
self._create_entity_key(entity_event),
|
||||
vitrage_category=EntityCategory.ALARM,
|
||||
vitrage_type=entity_event[DSProps.ENTITY_TYPE],
|
||||
vitrage_sample_timestamp=vitrage_sample_timestamp,
|
||||
update_timestamp=update_timestamp,
|
||||
entity_state=self._get_alarm_state(entity_event),
|
||||
metadata=metadata)
|
||||
|
||||
def _ok_status(self, entity_event):
|
||||
return entity_event[KProps.PRIORITY] == KapacitorState.OK
|
||||
|
||||
def _create_kapacitor_neighbors(self, entity_event):
|
||||
graph_neighbors = entity_event.get(self.QUERY_RESULT, [])
|
||||
return [self._create_neighbor(
|
||||
entity_event,
|
||||
graph_neighbor[VProps.ID],
|
||||
graph_neighbor[VProps.VITRAGE_TYPE],
|
||||
EdgeLabel.ON,
|
||||
neighbor_category=EntityCategory.RESOURCE)
|
||||
for graph_neighbor in graph_neighbors]
|
||||
|
||||
@staticmethod
|
||||
def get_enrich_query(event):
|
||||
resource_type = event.get(KProps.RESOURCE_TYPE)
|
||||
resource_name = event.get(KProps.RESOURCE_NAME)
|
||||
|
||||
if resource_type and resource_name:
|
||||
return {VProps.NAME: resource_name,
|
||||
VProps.VITRAGE_TYPE: resource_type}
|
||||
|
||||
return None
|
@ -618,3 +618,24 @@ def simple_k8s_nodes_generators(nodes_num, snapshot_events=0):
|
||||
}
|
||||
)
|
||||
return tg.get_trace_generators(test_entity_spec_list)
|
||||
|
||||
|
||||
def simple_kapacitor_alarm_generators(update_vals=None):
|
||||
"""A function for returning Kapacitor alarm event generators.
|
||||
|
||||
Returns generators for a given number of Kapacitor alarms.
|
||||
|
||||
:param update_vals: preset values for ALL update events
|
||||
:return: generators for alarms as specified
|
||||
"""
|
||||
|
||||
test_entity_spec_list = [({
|
||||
tg.DYNAMIC_INFO_FKEY: tg.DRIVER_KAPACITOR_UPDATE_D,
|
||||
tg.STATIC_INFO_FKEY: None,
|
||||
tg.EXTERNAL_INFO_KEY: update_vals,
|
||||
tg.MAPPING_KEY: None,
|
||||
tg.NAME_KEY: 'Kapacitor alarm generator',
|
||||
tg.NUM_EVENTS: 1
|
||||
})]
|
||||
|
||||
return tg.get_trace_generators(test_entity_spec_list)
|
||||
|
@ -214,6 +214,18 @@ def simple_collectd_alarm_generators(update_vals=None):
|
||||
tg.TRANS_COLLECTD_UPDATE_D, update_vals)
|
||||
|
||||
|
||||
def simple_kapacitor_alarm_generators(update_vals=None):
|
||||
"""A function for returning Kapacitor alarm event generators.
|
||||
|
||||
Returns generators for a given number of Kapacitor alarms.
|
||||
|
||||
:param update_vals: preset values for ALL update events
|
||||
:return: generators for alarms as specified
|
||||
"""
|
||||
return _simple_alarm_generators('Kapacitor',
|
||||
tg.TRANS_KAPACITOR_UPDATE_D, update_vals)
|
||||
|
||||
|
||||
def simple_prometheus_alarm_generators(update_vals=None):
|
||||
"""A function for returning Prometheus alert event generators.
|
||||
|
||||
|
@ -50,6 +50,7 @@ MOCK_DRIVER_PATH = '%s/mock_configurations/driver' % \
|
||||
DRIVER_AODH_UPDATE_D = 'driver_aodh_update_dynamic.json'
|
||||
DRIVER_DOCTOR_UPDATE_D = 'driver_doctor_update_dynamic.json'
|
||||
DRIVER_COLLECTD_UPDATE_D = 'driver_collectd_update_dynamic.json'
|
||||
DRIVER_KAPACITOR_UPDATE_D = 'driver_kapacitor_update_dynamic.json'
|
||||
DRIVER_HOST_SNAPSHOT_D = 'driver_host_snapshot_dynamic.json'
|
||||
DRIVER_INST_SNAPSHOT_D = 'driver_inst_snapshot_dynamic.json'
|
||||
DRIVER_INST_SNAPSHOT_S = 'driver_inst_snapshot_static.json'
|
||||
@ -80,6 +81,7 @@ TRANS_AODH_SNAPSHOT_D = 'transformer_aodh_snapshot_dynamic.json'
|
||||
TRANS_AODH_UPDATE_D = 'transformer_aodh_update_dynamic.json'
|
||||
TRANS_DOCTOR_UPDATE_D = 'transformer_doctor_update_dynamic.json'
|
||||
TRANS_COLLECTD_UPDATE_D = 'transformer_collectd_update_dynamic.json'
|
||||
TRANS_KAPACITOR_UPDATE_D = 'transformer_kapacitor_update_dynamic.json'
|
||||
TRANS_PROMETHEUS_UPDATE_D = 'transformer_prometheus_update_dynamic.json'
|
||||
TRANS_INST_SNAPSHOT_D = 'transformer_inst_snapshot_dynamic.json'
|
||||
TRANS_HOST_SNAPSHOT_D = 'transformer_host_snapshot_dynamic.json'
|
||||
@ -124,6 +126,7 @@ class EventTraceGenerator(object):
|
||||
{DRIVER_AODH_UPDATE_D: _get_aodh_alarm_update_driver_values,
|
||||
DRIVER_DOCTOR_UPDATE_D: _get_simple_update_driver_values,
|
||||
DRIVER_COLLECTD_UPDATE_D: _get_simple_update_driver_values,
|
||||
DRIVER_KAPACITOR_UPDATE_D: _get_simple_update_driver_values,
|
||||
DRIVER_KUBE_SNAPSHOT_D: _get_k8s_node_snapshot_driver_values,
|
||||
DRIVER_INST_SNAPSHOT_D: _get_vm_snapshot_driver_values,
|
||||
DRIVER_INST_UPDATE_LEGACY_D: _get_vm_update_legacy_driver_values,
|
||||
@ -149,6 +152,7 @@ class EventTraceGenerator(object):
|
||||
TRANS_AODH_UPDATE_D: _get_trans_aodh_alarm_snapshot_values,
|
||||
TRANS_DOCTOR_UPDATE_D: _get_simple_trans_alarm_update_values,
|
||||
TRANS_COLLECTD_UPDATE_D: _get_simple_trans_alarm_update_values,
|
||||
TRANS_KAPACITOR_UPDATE_D: _get_simple_trans_alarm_update_values,
|
||||
TRANS_PROMETHEUS_UPDATE_D: _get_simple_trans_alarm_update_values,
|
||||
TRANS_INST_SNAPSHOT_D: _get_trans_vm_snapshot_values,
|
||||
TRANS_HOST_SNAPSHOT_D: _get_trans_host_snapshot_values,
|
||||
|
16
vitrage/tests/resources/kapacitor/kapacitor_conf.yaml
Normal file
16
vitrage/tests/resources/kapacitor/kapacitor_conf.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
kapacitor:
|
||||
- alert:
|
||||
host: cloud.compute1
|
||||
vitrage_resource:
|
||||
type: nova.host
|
||||
name: compute-1
|
||||
- alert:
|
||||
host: compute-(.*)
|
||||
vitrage_resource:
|
||||
type: nova.host
|
||||
name: ${kapacitor_host}
|
||||
- alert:
|
||||
host: (.*)
|
||||
vitrage_resource:
|
||||
type: nova.instance
|
||||
name: ${kapacitor_host}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"level": "CRITICAL",
|
||||
"priority": "critical",
|
||||
"host": "compute-1",
|
||||
"details": "DETAIL INFO :: LEVEL: CRITICAL - ID: mem high-host=compute-1 - NAME : mem - VALUE: 9.746241910865896 - TASKNAME: chronograf-v1-2d1fad8a-1c52-4eca-8721-c7af36faf7c - GROUP: host=compute-1 - TAGS: map[host:compute-1] - TIME: 2019-04-22 08:11:00 +0000 UTC",
|
||||
"time": "2019-04-22T08:11:00Z",
|
||||
"duration": 0,
|
||||
"message": "mem high",
|
||||
"id": "mem high-host=compute-1",
|
||||
"recoverable": "True",
|
||||
"previousLevel": "OK"
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"level": "CRITICAL",
|
||||
"priority": "critical",
|
||||
"host": "compute-1",
|
||||
"vitrage_entity_type" : "kapacitor",
|
||||
"vitrage_datasource_name": "kapacitor",
|
||||
"vitrage_datasource_action" : "update",
|
||||
"vitrage_sample_date": "2019-04-22T06:31:50.094836",
|
||||
"resource_type": "nova.host",
|
||||
"resource_name": "compute-1",
|
||||
"details": "DETAIL INFO :: LEVEL: CRITICAL - ID: mem high-host=compute-1 - NAME : mem - VALUE: 9.746241910865896 - TASKNAME: chronograf-v1-2d1fad8a-1c52-4eca-8721-c7af36faf7c - GROUP: host=compute-1 - TAGS: map[host:compute-1] - TIME: 2019-04-22 08:11:00 +0000 UTC",
|
||||
"time": "2019-04-22T08:11:00Z",
|
||||
"duration": 0,
|
||||
"message": "mem high",
|
||||
"id": "mem high-host=compute-1",
|
||||
"recoverable": "True",
|
||||
"previousLevel": "OK"
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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_config import cfg
|
||||
|
||||
from vitrage.common.constants import DatasourceOpts as DSOpts
|
||||
from vitrage.datasources.kapacitor.config import KapacitorConfig
|
||||
from vitrage.datasources.kapacitor import KAPACITOR_DATASOURCE
|
||||
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
||||
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
||||
from vitrage.tests import base
|
||||
from vitrage.tests.mocks import utils
|
||||
|
||||
|
||||
class TestKapacitorConfig(base.BaseTest):
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt(DSOpts.TRANSFORMER,
|
||||
default='vitrage.datasources.kapacitor.transformer.'
|
||||
'KapacitorTransformer',
|
||||
help='Kapacitor data source transformer class path',
|
||||
required=True),
|
||||
cfg.StrOpt(DSOpts.DRIVER,
|
||||
default='vitrage.datasources.kapacitor.driver.'
|
||||
'KapacitorDriver',
|
||||
help='Kapacitor driver class path',
|
||||
required=True),
|
||||
cfg.StrOpt(DSOpts.CONFIG_FILE,
|
||||
help='Kapacitor configuration file',
|
||||
default=utils.get_resources_dir()
|
||||
+ '/kapacitor/kapacitor_conf.yaml'),
|
||||
]
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestKapacitorConfig, cls).setUpClass()
|
||||
cls.conf = cfg.ConfigOpts()
|
||||
cls.conf.register_opts(cls.OPTS, group=KAPACITOR_DATASOURCE)
|
||||
|
||||
def test_get_vitrage_resource(self):
|
||||
"""Test the resource returned after processing a list of mappings
|
||||
|
||||
:return:
|
||||
"""
|
||||
# Action
|
||||
kapacitor_conf = KapacitorConfig(self.conf)
|
||||
|
||||
# Test assertions
|
||||
mapped_resource = kapacitor_conf.get_vitrage_resource(None)
|
||||
self.assertIsNone(mapped_resource, 'expected None')
|
||||
|
||||
mapped_resource = kapacitor_conf.get_vitrage_resource('')
|
||||
self.assertIsNone(mapped_resource, 'expected None')
|
||||
|
||||
mapped_resource = kapacitor_conf.get_vitrage_resource('cloud.compute1')
|
||||
self.assertIsNotNone(mapped_resource, 'expected Not None')
|
||||
self.assertEqual(NOVA_HOST_DATASOURCE, mapped_resource[0])
|
||||
self.assertEqual('compute-1', mapped_resource[1])
|
||||
|
||||
mapped_resource = kapacitor_conf.get_vitrage_resource('compute-2')
|
||||
self.assertIsNotNone(mapped_resource, 'expected Not None')
|
||||
self.assertEqual(NOVA_HOST_DATASOURCE, mapped_resource[0])
|
||||
self.assertEqual('compute-2', mapped_resource[1])
|
||||
|
||||
mapped_resource = kapacitor_conf.get_vitrage_resource('instance-1')
|
||||
self.assertIsNotNone(mapped_resource, 'expected Not None')
|
||||
self.assertEqual(NOVA_INSTANCE_DATASOURCE, mapped_resource[0])
|
||||
self.assertEqual('instance-1', mapped_resource[1])
|
||||
|
||||
@staticmethod
|
||||
def _assert_equals(mapping1, mapping2):
|
||||
return mapping1.kapacitor_host_regexp.pattern == \
|
||||
mapping2.kapacitor_host_regexp.pattern and \
|
||||
mapping1.resource_type == mapping2.resource_type and \
|
||||
mapping1.resource_name == mapping2.resource_name
|
@ -0,0 +1,120 @@
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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_config import cfg
|
||||
|
||||
from vitrage.common.constants import DatasourceOpts as DSOpts
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.datasources.kapacitor.driver import KapacitorDriver
|
||||
from vitrage.datasources.kapacitor import KAPACITOR_DATASOURCE
|
||||
from vitrage.datasources.kapacitor.properties \
|
||||
import KapacitorProperties as KProps
|
||||
from vitrage.datasources.kapacitor.properties \
|
||||
import KapacitorState as KState
|
||||
from vitrage.tests import base
|
||||
from vitrage.tests.mocks import mock_driver
|
||||
from vitrage.tests.mocks import utils
|
||||
|
||||
# notification alarm input
|
||||
HOST = 'compute-1'
|
||||
ALARM_PRIntORIY = 'critical'
|
||||
ALARM_EVENT_TYPE = 'kapacitor.alarm.critical'
|
||||
|
||||
# match result
|
||||
EXPECTED_RESOURCE_TYPE = 'nova.host'
|
||||
EXPECTED_RESOURCE_NAME = 'compute1'
|
||||
EXPECTED_EVENT_PRIORIY = 'critical'
|
||||
EXPECTED_EVENT_TYPE = 'kapacitor.alarm.critical'
|
||||
|
||||
|
||||
class TestKapacitorDriver(base.BaseTest):
|
||||
OPTS = [
|
||||
cfg.StrOpt(DSOpts.CONFIG_FILE,
|
||||
help='Kapacitor configuration file',
|
||||
default=utils.get_resources_dir()
|
||||
+ '/kapacitor/kapacitor_conf.yaml'),
|
||||
]
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestKapacitorDriver, cls).setUpClass()
|
||||
cls.conf = cfg.ConfigOpts()
|
||||
cls.conf.register_opts(cls.OPTS, group=KAPACITOR_DATASOURCE)
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def setUp(self):
|
||||
super(TestKapacitorDriver, self).setUp()
|
||||
self.driver = KapacitorDriver(self.conf)
|
||||
|
||||
def test_enrich_event(self):
|
||||
# Test event on host
|
||||
# Setup
|
||||
input_data = {KProps.HOST: 'compute-1',
|
||||
KProps.PRIORITY: 'CPU utilization',
|
||||
ALARM_EVENT_TYPE: KState.CRITICAL}
|
||||
expected_data = {DSProps.EVENT_TYPE: KState.CRITICAL,
|
||||
KProps.RESOURCE_NAME: 'compute-1',
|
||||
KProps.RESOURCE_TYPE: 'nova.host',
|
||||
KProps.PRIORITY: 'CPU utilization'}
|
||||
event = self._generate_event(input_data[KProps.HOST],
|
||||
input_data[KProps.PRIORITY])
|
||||
# Action
|
||||
event = self.driver.enrich_event(event,
|
||||
input_data[ALARM_EVENT_TYPE])
|
||||
# Test assertions
|
||||
self._assert_event_equal(event, expected_data)
|
||||
|
||||
# Test event on instance
|
||||
# Setup
|
||||
input_data = {KProps.HOST: 'node1-vm',
|
||||
KProps.PRIORITY: 'CPU utilization',
|
||||
ALARM_EVENT_TYPE: KState.CRITICAL}
|
||||
expected_data = {DSProps.EVENT_TYPE: KState.CRITICAL,
|
||||
KProps.RESOURCE_NAME: 'node1-vm',
|
||||
KProps.RESOURCE_TYPE: 'nova.instance',
|
||||
KProps.PRIORITY: 'CPU utilization'}
|
||||
event = self._generate_event(input_data[KProps.HOST],
|
||||
input_data[KProps.PRIORITY])
|
||||
# Action
|
||||
event = self.driver.enrich_event(event,
|
||||
input_data[ALARM_EVENT_TYPE])
|
||||
# Test assertions
|
||||
self._assert_event_equal(event, expected_data)
|
||||
|
||||
@staticmethod
|
||||
def _generate_event(hostname, priority):
|
||||
update_vals = {}
|
||||
if hostname:
|
||||
update_vals[KProps.HOST] = hostname
|
||||
if priority:
|
||||
update_vals[KProps.PRIORITY] = priority
|
||||
|
||||
generators = mock_driver.simple_kapacitor_alarm_generators(
|
||||
update_vals=update_vals)
|
||||
|
||||
return mock_driver.generate_sequential_events_list(generators)[0]
|
||||
|
||||
def _assert_event_equal(self,
|
||||
event1,
|
||||
event2):
|
||||
self.assertIsNotNone(event1, 'No event returned')
|
||||
self.assertEqual(event1[DSProps.EVENT_TYPE],
|
||||
event2[DSProps.EVENT_TYPE])
|
||||
self.assertEqual(event1[KProps.RESOURCE_NAME],
|
||||
event2[KProps.RESOURCE_NAME])
|
||||
self.assertEqual(event1[KProps.RESOURCE_TYPE],
|
||||
event2[KProps.RESOURCE_TYPE])
|
||||
self.assertEqual(event1[KProps.PRIORITY],
|
||||
event2[KProps.PRIORITY])
|
@ -0,0 +1,175 @@
|
||||
# Copyright 2019 - Viettel
|
||||
#
|
||||
# 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 uuid
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from vitrage.common.constants import DatasourceOpts as DSOpts
|
||||
from vitrage.common.constants import DatasourceProperties as DSProps
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import UpdateMethod
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage.datasources.kapacitor import KAPACITOR_DATASOURCE
|
||||
from vitrage.datasources.kapacitor.properties import KapacitorProperties \
|
||||
as KProps
|
||||
from vitrage.datasources.kapacitor.properties import KapacitorState \
|
||||
as KState
|
||||
from vitrage.datasources.kapacitor.transformer import KapacitorTransformer
|
||||
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
|
||||
from vitrage.datasources.nova.host.transformer import HostTransformer
|
||||
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
|
||||
from vitrage.datasources.nova.instance.transformer import InstanceTransformer
|
||||
from vitrage.datasources.transformer_base import TransformerBase
|
||||
from vitrage.tests.mocks import mock_transformer
|
||||
from vitrage.tests.unit.datasources.test_alarm_transformer_base import \
|
||||
BaseAlarmTransformerTest
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class TestKapacitorTransformer(BaseAlarmTransformerTest):
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt(DSOpts.UPDATE_METHOD,
|
||||
default=UpdateMethod.PUSH),
|
||||
]
|
||||
|
||||
# noinspection PyAttributeOutsideInit,PyPep8Naming
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestKapacitorTransformer, cls).setUpClass()
|
||||
cls.transformers = {}
|
||||
cls.conf = cfg.ConfigOpts()
|
||||
cls.conf.register_opts(cls.OPTS, group=KAPACITOR_DATASOURCE)
|
||||
cls.transformers[KAPACITOR_DATASOURCE] = \
|
||||
KapacitorTransformer(cls.transformers, cls.conf)
|
||||
cls.transformers[NOVA_INSTANCE_DATASOURCE] = \
|
||||
InstanceTransformer(cls.transformers, cls.conf)
|
||||
cls.transformers[NOVA_HOST_DATASOURCE] = \
|
||||
HostTransformer(cls.transformers, cls.conf)
|
||||
|
||||
def test_create_entity_key(self):
|
||||
LOG.debug('Test get key from nova host transformer')
|
||||
|
||||
# Test setup
|
||||
host = 'compute-1'
|
||||
resource_name = 'compute-1'
|
||||
resource_type = 'nova.host'
|
||||
update_vals = {KProps.HOST: host,
|
||||
KProps.RESOURCE_TYPE: resource_type,
|
||||
KProps.RESOURCE_NAME: resource_name}
|
||||
|
||||
event = self._generate_event(update_vals)
|
||||
transformer = KapacitorTransformer(self.transformers, self.conf)
|
||||
self.assertIsNotNone(event)
|
||||
|
||||
# Test action
|
||||
observed_key = transformer._create_entity_key(event)
|
||||
|
||||
# Test assertions
|
||||
observed_key_fields = observed_key.split(
|
||||
TransformerBase.KEY_SEPARATOR)
|
||||
|
||||
self.assertEqual(EntityCategory.ALARM, observed_key_fields[0])
|
||||
self.assertEqual(event[DSProps.ENTITY_TYPE], observed_key_fields[1])
|
||||
self.assertEqual(event[KProps.RESOURCE_NAME],
|
||||
observed_key_fields[2])
|
||||
self.assertEqual(event[KProps.ID],
|
||||
observed_key_fields[3])
|
||||
|
||||
def test_create_update_entity_vertex(self):
|
||||
# Test setup
|
||||
host1 = 'host1'
|
||||
instance_id = uuid.uuid4().hex
|
||||
event_on_host = self._generate_event_on_host(host1)
|
||||
event_on_instance = self._generate_event_on_instance(host1,
|
||||
instance_id)
|
||||
self.assertIsNotNone(event_on_host)
|
||||
self.assertIsNotNone(event_on_instance)
|
||||
|
||||
# Test action
|
||||
transformer = self.transformers[KAPACITOR_DATASOURCE]
|
||||
wrapper_for_host = transformer.transform(event_on_host)
|
||||
wrapper_for_instance = transformer.transform(event_on_instance)
|
||||
|
||||
# Test assertions
|
||||
self._validate_vertex_props(wrapper_for_host.vertex, event_on_host)
|
||||
self._validate_vertex_props(wrapper_for_instance.vertex,
|
||||
event_on_instance)
|
||||
|
||||
# Validate the neighbors: only one valid host neighbor
|
||||
host_entity_key = transformer._create_entity_key(event_on_host)
|
||||
host_entity_uuid = \
|
||||
transformer.uuid_from_deprecated_vitrage_id(host_entity_key)
|
||||
|
||||
instance_entity_key = transformer._create_entity_key(event_on_instance)
|
||||
instance_entity_uuid = \
|
||||
transformer.uuid_from_deprecated_vitrage_id(instance_entity_key)
|
||||
|
||||
self._validate_host_neighbor(wrapper_for_host,
|
||||
host_entity_uuid,
|
||||
host1)
|
||||
|
||||
self._validate_instance_neighbor(wrapper_for_instance,
|
||||
instance_entity_uuid,
|
||||
instance_id)
|
||||
|
||||
# Validate the expected action on the graph - update or delete
|
||||
self._validate_graph_action(wrapper_for_host)
|
||||
self._validate_graph_action(wrapper_for_instance)
|
||||
|
||||
def _validate_vertex_props(self, vertex, event):
|
||||
self._validate_alarm_vertex_props(
|
||||
vertex, event[KProps.MESSAGE],
|
||||
KAPACITOR_DATASOURCE, event[DSProps.SAMPLE_DATE])
|
||||
|
||||
@staticmethod
|
||||
def _generate_event(update_vals):
|
||||
generators = mock_transformer.simple_kapacitor_alarm_generators(
|
||||
update_vals=update_vals)
|
||||
|
||||
return mock_transformer.generate_random_events_list(generators)[0]
|
||||
|
||||
def _generate_event_on_host(self, hostname):
|
||||
# fake query result to be used by the transformer for determining
|
||||
# the neighbor
|
||||
update_vals = {}
|
||||
query_result = [{VProps.VITRAGE_TYPE: NOVA_HOST_DATASOURCE,
|
||||
VProps.ID: hostname}]
|
||||
|
||||
update_vals[KProps.HOST] = hostname
|
||||
update_vals[KProps.RESOURCE_TYPE] = NOVA_HOST_DATASOURCE
|
||||
update_vals[KProps.RESOURCE_NAME] = hostname
|
||||
update_vals[TransformerBase.QUERY_RESULT] = query_result
|
||||
|
||||
return self._generate_event(update_vals)
|
||||
|
||||
def _generate_event_on_instance(self, hostname, instance_id):
|
||||
# fake query result to be used by the transformer for determining
|
||||
# the neighbor
|
||||
update_vals = {}
|
||||
query_result = [{VProps.VITRAGE_TYPE: NOVA_INSTANCE_DATASOURCE,
|
||||
VProps.ID: instance_id}]
|
||||
|
||||
update_vals[KProps.HOST] = hostname
|
||||
update_vals[KProps.RESOURCE_TYPE] = NOVA_INSTANCE_DATASOURCE
|
||||
update_vals[KProps.RESOURCE_NAME] = hostname
|
||||
update_vals[TransformerBase.QUERY_RESULT] = query_result
|
||||
|
||||
return self._generate_event(update_vals)
|
||||
|
||||
def _is_erroneous(self, vertex):
|
||||
return vertex[VProps.SEVERITY] != KState.OK
|
Loading…
x
Reference in New Issue
Block a user