Merge "rca"

This commit is contained in:
Jenkins 2016-05-25 13:39:18 +00:00 committed by Gerrit Code Review
commit eafde06dbc
10 changed files with 376 additions and 207 deletions

View File

@ -0,0 +1,106 @@
# Copyright 2016 Nokia
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import random
import time
from oslo_log import log as logging
from vitrage import clients
from vitrage.common.constants import EntityCategory
from vitrage.common.constants import VertexProperties
from vitrage_tempest_tests.tests.api.base import BaseApiTest
import vitrage_tempest_tests.tests.utils as utils
LOG = logging.getLogger(__name__)
TEMPLATES_RESOURCES_PATH = 'resources/templates/'
TEMPLATES_SOURCES_PATH = '/etc/vitrage/templates/'
class BaseAlarmsTest(BaseApiTest):
"""Topology test class for Vitrage API tests."""
@classmethod
def setUpClass(cls):
super(BaseAlarmsTest, cls).setUpClass()
cls.ceilometer_client = clients.ceilometer_client(cls.conf)
@staticmethod
def _filter_alarms_by_parameter(alarms_list,
keys, values):
filtered_alarms_list = []
for item in alarms_list:
verification = 0
category = utils.uni2str(item[VertexProperties.CATEGORY])
for index in range(len(keys)):
key = utils.uni2str(item[keys[index]])
if category == EntityCategory.ALARM \
and key == values[index]:
verification += 1
else:
break
if verification == len(keys):
filtered_alarms_list.append(item)
return filtered_alarms_list
def _create_ceilometer_alarm(self, resource_id=None,
name=None, unic=True):
if not name:
name = '%s-%s' % ('test_', random.randrange(0, 100000, 1))
elif unic:
name = '%s-%s' % (name, random.randrange(0, 100000, 1))
aodh_request = self._aodh_request(resource_id=resource_id, name=name)
self.ceilometer_client.alarms.create(**aodh_request)
self._wait_for_status(20,
self._check_num_alarms,
num_alarms=1)
time.sleep(25)
def _delete_ceilometer_alarms(self):
alarms = self.ceilometer_client.alarms.list()
for alarm in alarms:
self.ceilometer_client.alarms.delete(alarm.alarm_id)
self._wait_for_status(20,
self._check_num_alarms,
num_alarms=0)
time.sleep(25)
@staticmethod
def _aodh_request(resource_id=None, name=None):
query = []
if resource_id:
query = [
dict(
field=u'resource_id',
type='',
op=u'eq',
value=resource_id)
]
return dict(
name=name,
description=u'test alarm',
event_rule=dict(query=query),
severity='low',
state='alarm',
type=u'event')
def _check_num_alarms(self, num_alarms=0, state=''):
if len(self.ceilometer_client.alarms.list()) != num_alarms:
return False
return all(alarm.state.upper() == state.upper()
for alarm in self.ceilometer_client.alarms.list())

View File

@ -14,80 +14,42 @@
import json
from oslo_log import log as logging
from vitrage.datasources.aodh import AODH_DATASOURCE
from vitrage.datasources.nova.host import NOVA_HOST_DATASOURCE
from vitrage.datasources.nova.instance import NOVA_INSTANCE_DATASOURCE
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
from vitrage_tempest_tests.tests.api.base import BaseApiTest
import vitrage_tempest_tests.tests.utils as utils
LOG = logging.getLogger(__name__)
class BaseAlarmsTest(BaseApiTest):
class TestAlarms(BaseAlarmsTest):
"""Alarms test class for Vitrage API tests."""
@classmethod
def setUpClass(cls):
super(BaseAlarmsTest, cls).setUpClass()
super(TestAlarms, cls).setUpClass()
'''' Nova instances have alarms due to deduced alarm template: '''''
'''' nova_alarm_for_every_host.yaml '''''
'''' nova_alarm_for_every_instance.yaml '''''
'''' Nagios alarm template is nagios_alarm.yaml '''''
@staticmethod
def copy_alarms_templates_files():
utils.run_from_terminal(
"cp " +
"vitrage_tempest_tests/tests/resources/templates/"
+ "*_alarm* /etc/vitrage/templates/.")
@staticmethod
def delete_alarms_templates_files():
utils.run_from_terminal(
"rm /etc/vitrage/templates/*_alarm*")
def test_compare_alarms(self):
def test_compare_cli_vs_api_alarms(self):
"""Wrapper that returns a test graph."""
self._create_instances(num_instances=3)
api_alarms = self.vitrage_client.alarms.list(vitrage_id=None)
cli_alarms = utils.run_vitrage_command('vitrage alarms list',
self.conf)
self.assertEqual(True,
self._compare_alarms_lists(api_alarms, cli_alarms))
self._delete_instances()
try:
resources = self._create_instances(num_instances=1)
self._create_ceilometer_alarm(resource_id=resources[0].id,
name='tempest_aodh_test')
def test_nova_alarms(self):
"""Wrapper that returns test nova alarms."""
self._create_instances(num_instances=4)
resources = self.nova_client.servers.list()
api_alarms = self.vitrage_client.alarms.list(vitrage_id=None)
cli_alarms = utils.run_vitrage_command(
'vitrage alarms list', self.conf)
self.assertTrue(self._compare_alarms_lists(
api_alarms, cli_alarms, AODH_DATASOURCE,
utils.uni2str(resources[0].id)))
alarms = self.vitrage_client.alarms.list(vitrage_id=None)
nova_alarms = self._filter_alarms_by_resource_type(
alarms, NOVA_INSTANCE_DATASOURCE)
self.assertEqual(True, self._validate_alarms_correctness(nova_alarms,
resources))
self._delete_instances()
finally:
self._delete_ceilometer_alarms()
self._delete_instances()
# def test_nagios_alarms(self):
# """Wrapper that returns test nagios alarms."""
# alarms = self.vitrage_client.alarms.list()
# nagios_alarms = self._filter_alarms_by_resource_type(alarms,
# 'nagios')
# self.assertEqual(True, self._validate_alarms_correctness(
# nagios_alarms, 'nagios'))
# def test_aodh_alarms(self):
# """Wrapper that returns test aodh alarms."""
# # self.create_alarms_per_component('aodh')
# alarms = self.vitrage_client.alarms.list()
# aodh_alarms = self._filter_alarms_by_resource_type(alarms,
# 'aodh')
# self.assertEqual(True, self._validate_alarms_correctness(
# aodh_alarms, 'aodh'))
def _compare_alarms_lists(self, api_alarms, cli_alarms):
def _compare_alarms_lists(self, api_alarms, cli_alarms,
resource_type, resource_id):
"""Validate alarm existence """
if not api_alarms:
LOG.error("The alarms list taken from api is empty")
@ -100,48 +62,15 @@ class BaseAlarmsTest(BaseApiTest):
LOG.debug("The alarms list taken by api is : %s",
json.dumps(api_alarms))
cli_items = cli_alarms.count('vitrage')
nova_instance_alarms = \
self._filter_alarms_by_resource_type(api_alarms,
NOVA_INSTANCE_DATASOURCE)
nova_instances = cli_alarms.count(NOVA_INSTANCE_DATASOURCE)
nova_host_alarms = \
self._filter_alarms_by_resource_type(api_alarms,
NOVA_HOST_DATASOURCE)
nova_hosts = cli_alarms.count(NOVA_HOST_DATASOURCE)
return (cli_items == len(api_alarms) and
nova_instances == len(nova_instance_alarms) and
nova_hosts == len(nova_host_alarms))
cli_items = cli_alarms.splitlines()
@staticmethod
def _validate_alarms_correctness(alarms, resources):
"""Validate alarm existence """
if not alarms:
LOG.error("The alarms list is empty")
return False
if not resources:
LOG.error("The resources list is empty")
return False
api_by_type = self._filter_alarms_by_parameter(
api_alarms, ['type'], [resource_type])
cli_by_type = cli_alarms.count(' ' + resource_type + ' ')
count = 0
for resource in resources:
LOG.info("______________________")
LOG.info("The resource id is %s", resource.id)
for item in alarms:
LOG.info("The alarms resource id is %s", item["resource_id"])
if item["resource_id"] == resource.id:
count += 1
LOG.info("The resources list size is %s", len(resources))
LOG.info("The common items list size is %s", count)
return count == len(resources)
@staticmethod
def _filter_alarms_by_resource_type(alarms_list, alarm_type):
filtered_alarms_list = []
for item in alarms_list:
if item["category"] == "ALARM" \
and item["resource_type"] == alarm_type:
filtered_alarms_list.append(item)
return filtered_alarms_list
api_by_id = self._filter_alarms_by_parameter(
api_alarms, ['resource_id'], [resource_id])
cli_by_id = cli_alarms.count(resource_id)
return (len(cli_items) - 4 == len(api_alarms) and
cli_by_type == len(api_by_type) and
cli_by_id == len(api_by_id))

View File

@ -11,7 +11,6 @@
# 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 time
from oslo_log import log as logging
@ -32,9 +31,11 @@ from vitrage.graph import NXGraph
from vitrage.graph import Vertex
from vitrage import keystone_client
from vitrage_tempest_tests.tests import OPTS
import vitrage_tempest_tests.tests.utils as utils
from vitrageclient import client as v_client
import vitrage_tempest_tests.tests.utils as utils
LOG = logging.getLogger(__name__)
@ -50,6 +51,7 @@ class BaseApiTest(base.BaseTestCase):
super(BaseApiTest, cls).setUpClass()
cls.conf = utils.get_conf()
cls.conf.register_opts(list(OPTS), group='keystone_authtoken')
cls.vitrage_client = \
v_client.Client('1', session=keystone_client.get_session(cls.conf))
cls.nova_client = clients.nova_client(cls.conf)
@ -72,6 +74,13 @@ class BaseApiTest(base.BaseTestCase):
return volume
def _get_host(self):
topology = self.vitrage_client.topology.get()
for item in topology['nodes']:
if item[VProps.TYPE] == NOVA_HOST_DATASOURCE:
return item
return None
def _create_instances(self, num_instances):
flavors_list = self.nova_client.flavors.list()
images_list = self.nova_client.images.list()

View File

@ -11,19 +11,15 @@
# 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 random
import time
from oslo_log import log as logging
from vitrage import clients
from vitrage_tempest_tests.tests.api.base import BaseApiTest
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
LOG = logging.getLogger(__name__)
class TestAodhAlarm(BaseApiTest):
class TestAodhAlarm(BaseAlarmsTest):
@classmethod
def setUpClass(cls):
@ -63,51 +59,6 @@ class TestAodhAlarm(BaseApiTest):
finally:
self._delete_ceilometer_alarms()
def _create_ceilometer_alarm(self, resource_id=None):
aodh_request = self._aodh_request(resource_id=resource_id)
self.ceilometer_client.alarms.create(**aodh_request)
self._wait_for_status(30,
self._check_num_alarms,
num_alarms=1,
state='alarm')
time.sleep(25)
def _delete_ceilometer_alarms(self):
alarms = self.ceilometer_client.alarms.list()
for alarm in alarms:
self.ceilometer_client.alarms.delete(alarm.alarm_id)
self._wait_for_status(30,
self._check_num_alarms,
num_alarms=0)
time.sleep(25)
def _check_num_alarms(self, num_alarms=0, state=''):
if len(self.ceilometer_client.alarms.list()) != num_alarms:
return False
return all(alarm.__dict__['state'].upper() == state.upper()
for alarm in self.ceilometer_client.alarms.list())
def _aodh_request(self, resource_id=None):
query = []
if resource_id:
query = [
dict(
field=u'resource_id',
type='',
op=u'eq',
value=resource_id)
]
random_name = '%s-%s' % ('test', random.randrange(0, 100000, 1))
return dict(
name=random_name,
description=u'test alarm',
event_rule=dict(query=query),
severity='low',
state='alarm', # ok/alarm/insufficient data
type=u'event')
def _find_instance_resource_id(self):
servers = self.nova_client.servers.list()
return servers[0].id

View File

@ -11,5 +11,149 @@
# 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
__author__ = 'stack'
from oslo_log import log as logging
from vitrage.common.constants import VertexProperties
from vitrage.datasources import AODH_DATASOURCE
from vitrage.datasources import NOVA_HOST_DATASOURCE
from vitrage.datasources import NOVA_INSTANCE_DATASOURCE
from vitrage_tempest_tests.tests.api.alarms.base import BaseAlarmsTest
import vitrage_tempest_tests.tests.utils as utils
LOG = logging.getLogger(__name__)
RCA_ALARM_NAME = 'rca_test_host_alarm'
VITRAGE_ALARM_NAME = 'instance_deduce'
VITRAGE_DATASOURCE = 'vitrage'
class TestRca(BaseAlarmsTest):
"""RCA test class for Vitrage API tests."""
@classmethod
def setUpClass(cls):
super(TestRca, cls).setUpClass()
def test_compare_cil_and_api(self):
try:
vitrage_id = self._get_alarm_id(
resource_type=NOVA_INSTANCE_DATASOURCE,
alarm_name='instance_rca_alarm', unic=True)
api_rca = self.vitrage_client.rca.get(alarm_id=vitrage_id)
cli_rca = utils.run_vitrage_command(
'vitrage rca show ' + vitrage_id, self.conf)
self.assertTrue(self._compare_rca(api_rca, cli_rca))
finally:
self._delete_ceilometer_alarms()
self._delete_instances()
def test_validate_rca(self):
try:
vitrage_id = self._get_alarm_id(resource_type=NOVA_HOST_DATASOURCE,
alarm_name=RCA_ALARM_NAME,
unic=False)
resources = self._create_instances(2)
api_rca = self.vitrage_client.rca.get(alarm_id=vitrage_id)
api_alarms = self.vitrage_client.alarms.list(vitrage_id=None)
self.assertTrue(self._validate_rca(rca=api_rca['nodes']))
self.assertTrue(self._validate_deduce_alarms(alarms=api_alarms,
resources=resources))
finally:
self._delete_ceilometer_alarms()
self._delete_instances()
def _get_alarm_id(self, resource_type, alarm_name, unic):
if resource_type is NOVA_INSTANCE_DATASOURCE:
resource = self._create_instances(num_instances=1)
resource_id = utils.uni2str(resource[0].id)
else:
resource = self._get_host()
resource_id = utils.uni2str(resource[VertexProperties.ID])
self._create_ceilometer_alarm(resource_id=resource_id,
name=alarm_name, unic=unic)
list_alarms = self.vitrage_client.alarms.list(vitrage_id=None)
expected_alarm = self._filter_alarms_by_parameter(
list_alarms, ['resource_id', 'type'],
[resource_id, AODH_DATASOURCE])
return utils.uni2str(
expected_alarm[0][VertexProperties.VITRAGE_ID])
def _compare_rca(self, api_rca, cli_rca):
"""Validate alarm existence """
if not api_rca:
LOG.error("The rca taken from api is empty")
return False
if cli_rca is None:
LOG.error("The rca taken from cli is empty")
return False
LOG.debug("The rca taken from cli is : %s", cli_rca)
LOG.debug("The rca taken by api is : %s",
json.dumps(api_rca))
parsed_rca = json.loads(cli_rca)
sorted_cli_graph = self._clean_timestamps(sorted(parsed_rca.items()))
sorted_api_graph = self._clean_timestamps(sorted(api_rca.items()))
return sorted_cli_graph == sorted_api_graph
def _validate_rca(self, rca):
"""Validate alarm existence """
if not rca:
LOG.error("The alarms list is empty")
return False
LOG.debug("The rca alarms list is : %s",
json.dumps(rca))
resource_alarm = self._filter_alarms_by_parameter(
rca, ['type', 'name'],
[AODH_DATASOURCE, RCA_ALARM_NAME])
deduce_alarms = self._filter_alarms_by_parameter(
rca, ['type', 'name'],
[VITRAGE_DATASOURCE, VITRAGE_ALARM_NAME])
return (len(resource_alarm) == 1 and
len(deduce_alarms) == 2)
def _validate_deduce_alarms(self, alarms, resources):
"""Validate alarm existence """
if not alarms:
LOG.error("The alarms list is empty")
return False
LOG.debug("The alarms list is : %s",
json.dumps(alarms))
deduce_alarms_1 = self._filter_alarms_by_parameter(
alarms,
['type', 'name', 'resource_type', 'resource_id'],
[VITRAGE_DATASOURCE, VITRAGE_ALARM_NAME,
NOVA_INSTANCE_DATASOURCE,
utils.uni2str(resources[0].id)])
deduce_alarms_2 = self._filter_alarms_by_parameter(
alarms,
['type', 'name', 'resource_type', 'resource_id'],
[VITRAGE_DATASOURCE, VITRAGE_ALARM_NAME,
NOVA_INSTANCE_DATASOURCE,
utils.uni2str(resources[1].id)])
return (len(deduce_alarms_1) == 1 and
len(deduce_alarms_2) == 1)
@staticmethod
def _clean_timestamps(alist):
try:
del alist[5][1][0][VertexProperties.SAMPLE_TIMESTAMP]
del alist[5][1][0][VertexProperties.UPDATE_TIMESTAMP]
except Exception:
pass
return alist

View File

@ -0,0 +1,72 @@
metadata:
id: host_aodh_alarm
definitions:
entities:
- entity:
category: ALARM
type: aodh
name: 'rca_test_host_alarm'
template_id: host_alarm
- entity:
category: RESOURCE
type: nova.host
template_id: host
- entity:
category: RESOURCE
type: nova.instance
template_id: instance
- entity:
category: ALARM
type: vitrage
name: instance_deduce
template_id: instance_alarm
relationships:
- relationship:
source: host_alarm
target: host
relationship_type: on
template_id : alarm_on_host
- relationship:
source: instance_alarm
target: instance
relationship_type: on
template_id : alarm_on_instance
- relationship:
source: host
target: instance
relationship_type: contains
template_id: host_contains_instance
scenarios:
- scenario:
condition: alarm_on_host
actions:
- action:
action_type: set_state
action_target:
target: host
properties:
state: ERROR
- scenario:
condition: alarm_on_host and host_contains_instance
actions:
- action:
action_type: raise_alarm
action_target:
target: instance
properties:
alarm_name: instance_deduce
severity: WARNING
- action:
action_type: set_state
action_target:
target: instance
properties:
state: SUBOPTIMAL
- scenario:
condition: alarm_on_host and host_contains_instance and alarm_on_instance
actions:
- action:
action_type: add_causal_relationship
action_target:
source: host_alarm
target: instance_alarm

View File

@ -1,20 +0,0 @@
metadata:
id: free alarms for hosts
definitions:
entities:
- entity:
category: RESOURCE
type: nova.host
template_id: host
scenarios:
- scenario:
condition: host
actions:
- action:
action_type: raise_alarm
action_target:
target: host
properties:
alarm_name: host_alarm
severity: so_so

View File

@ -1,20 +0,0 @@
metadata:
id: free alarms for instances
definitions:
entities:
- entity:
category: RESOURCE
type: nova.instance
template_id: vm
scenarios:
- scenario:
condition: vm
actions:
- action:
action_type: raise_alarm
action_target:
target: vm
properties:
alarm_name: vm_alarm
severity: so_so

View File

@ -67,14 +67,18 @@ def run_vitrage_command(command, conf):
project_name_param, auth_url_param)
LOG.info('Full command: %s', full_command)
p = subprocess.Popen(full_command,
shell=True,
executable="/bin/bash",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
return stdout
if stderr != '':
LOG.error("The command output error is : " + stderr)
if stdout != '':
LOG.debug("The command output is : \n" + stdout)
return stdout
return None
def get_property_value(environment_name, conf_name, default_value, conf):
@ -114,16 +118,6 @@ def get_client():
return oslo_messaging.RPCClient(transport, target)
def get_regex_from_array(pattern, lines_arr):
p = re.compile(pattern)
for line in lines_arr:
m = p.search(line)
if m:
LOG.debug("The field value is " + m.group(1))
return m.group(1)
return None
def get_regex_result(pattern, text):
p = re.compile(pattern)
m = p.search(text)
@ -131,3 +125,7 @@ def get_regex_result(pattern, text):
LOG.debug("The regex value is " + m.group(1))
return m.group(1)
return None
def uni2str(text):
return text.encode('ascii', 'ignore')