Tempest test for Vitrage integration with Mistral
Change-Id: Ieed633ea1e9c29da8fb3f94f17087fb387341078 Implements: blueprint integration-with-mistral
This commit is contained in:
parent
3a90745853
commit
e8c8d540f8
@ -27,6 +27,8 @@ export KEEP_LOCALRC=1
|
||||
DEVSTACK_LOCAL_CONFIG+=$'\nenable_plugin heat git://git.openstack.org/openstack/heat'
|
||||
DEVSTACK_LOCAL_CONFIG+=$'\nenable_plugin ceilometer git://git.openstack.org/openstack/ceilometer'
|
||||
DEVSTACK_LOCAL_CONFIG+=$'\nenable_plugin aodh git://git.openstack.org/openstack/aodh'
|
||||
DEVSTACK_LOCAL_CONFIG+=$'\nenable_plugin mistral git://git.openstack.org/openstack/mistral'
|
||||
|
||||
DEVSTACK_LOCAL_CONFIG+=$'\ndisable_service ceilometer-alarm-evaluator,ceilometer-alarm-notifier'
|
||||
DEVSTACK_LOCAL_CONFIG+=$'\ndisable_service n-net'
|
||||
|
||||
@ -60,6 +62,10 @@ driver = messagingv2
|
||||
topics = notifications, vitrage_notifications
|
||||
|
||||
[[post-config|\$VITRAGE_CONF]]
|
||||
|
||||
[DEFAULT]
|
||||
notifiers = mistral
|
||||
|
||||
[static_physical]
|
||||
changes_interval = 5
|
||||
|
||||
|
@ -196,6 +196,8 @@ function configure_vitrage {
|
||||
# copy datasources
|
||||
cp $VITRAGE_DIR/etc/vitrage/datasources_values/*.yaml $VITRAGE_CONF_DIR/datasources_values
|
||||
|
||||
# copy templates
|
||||
cp -rf $VITRAGE_DIR/vitrage_tempest_tests/tests/resources/templates/api/* $VITRAGE_CONF_DIR/templates/
|
||||
|
||||
configure_auth_token_middleware $VITRAGE_CONF vitrage $VITRAGE_AUTH_CACHE_DIR
|
||||
|
||||
|
@ -21,7 +21,7 @@ DEVSTACK_PATH="$BASE/new"
|
||||
if [ "$1" = "api" ]; then
|
||||
TESTS="topology"
|
||||
elif [ "$1" = "datasources" ]; then
|
||||
TESTS="datasources\|test_events"
|
||||
TESTS="datasources\|test_events\|notifiers"
|
||||
else
|
||||
TESTS="topology"
|
||||
fi
|
||||
@ -29,7 +29,6 @@ fi
|
||||
#(cd $DEVSTACK_PATH/tempest/; sudo pip install -r requirements.txt -r test-requirements.txt)
|
||||
|
||||
(cd $DEVSTACK_PATH/; sudo sh -c 'cp -rf vitrage/vitrage_tempest_tests/tests/resources/static_physical/static_physical_configuration.yaml /etc/vitrage/')
|
||||
(cd $DEVSTACK_PATH/; sudo sh -c 'cp -rf vitrage/vitrage_tempest_tests/tests/resources/templates/api/* /etc/vitrage/templates/')
|
||||
(cd $DEVSTACK_PATH/; sudo sh -c 'cp -rf vitrage/vitrage_tempest_tests/tests/resources/heat/heat_template.yaml /etc/vitrage/')
|
||||
(cd $DEVSTACK_PATH/; sudo sh -c 'cp -rf vitrage/vitrage_tempest_tests/tests/resources/heat/policy.json-tempest /etc/heat/')
|
||||
|
||||
|
@ -12,8 +12,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import keystoneauth1.identity.v2 as v2
|
||||
import keystoneauth1.session as kssession
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_utils import importutils as utils
|
||||
@ -150,13 +148,7 @@ def heat_client(conf):
|
||||
def mistral_client(conf):
|
||||
"""Get an instance of Mistral client"""
|
||||
try:
|
||||
auth = v2.Password(
|
||||
auth_url=conf.service_credentials.auth_url + '/v2.0',
|
||||
username=conf.service_credentials.username,
|
||||
password=conf.service_credentials.password,
|
||||
tenant_name=conf.service_credentials.project_name)
|
||||
session = kssession.Session(auth=auth)
|
||||
|
||||
session = keystone_client.get_session(conf)
|
||||
endpoint = session.get_endpoint(service_type='workflowv2',
|
||||
endpoint_type='internalURL')
|
||||
args = {
|
||||
|
62
vitrage_tempest_tests/tests/api/event/base.py
Normal file
62
vitrage_tempest_tests/tests/api/event/base.py
Normal file
@ -0,0 +1,62 @@
|
||||
# Copyright 2017 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 datetime import datetime
|
||||
from oslo_log import log as logging
|
||||
|
||||
from vitrage import keystone_client
|
||||
from vitrage import service
|
||||
from vitrage_tempest_tests.tests.api.base import BaseApiTest
|
||||
from vitrageclient import client as v_client
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
DOWN = 'down'
|
||||
UP = 'up'
|
||||
|
||||
|
||||
class BaseTestEvents(BaseApiTest):
|
||||
"""Test class for Vitrage event API"""
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.conf = service.prepare_service([])
|
||||
cls.vitrage_client = \
|
||||
v_client.Client('1', session=keystone_client.get_session(cls.conf))
|
||||
|
||||
def _check_alarms(self):
|
||||
api_alarms = self.vitrage_client.alarm.list(vitrage_id='all',
|
||||
all_tenants=True)
|
||||
if api_alarms:
|
||||
return True, api_alarms
|
||||
return False, api_alarms
|
||||
|
||||
def _post_event(self, details):
|
||||
event_time = datetime.now()
|
||||
event_time_iso = event_time.isoformat()
|
||||
event_type = 'compute.host.down'
|
||||
self.vitrage_client.event.post(event_time_iso, event_type, details)
|
||||
|
||||
@staticmethod
|
||||
def _create_doctor_event_details(hostname, status):
|
||||
return {
|
||||
'hostname': hostname,
|
||||
'source': 'sample_monitor',
|
||||
'cause': 'another alarm',
|
||||
'severity': 'critical',
|
||||
'status': status,
|
||||
'monitor_id': 'sample monitor',
|
||||
'monitor_event_id': '456',
|
||||
}
|
@ -16,55 +16,30 @@ import six
|
||||
|
||||
from datetime import datetime
|
||||
from oslo_log import log as logging
|
||||
from oslotest import base
|
||||
|
||||
from vitrage.common.constants import EntityCategory
|
||||
from vitrage.common.constants import EventProperties as EventProps
|
||||
from vitrage.common.constants import VertexProperties as VProps
|
||||
from vitrage import keystone_client
|
||||
from vitrage import service
|
||||
from vitrage_tempest_tests.tests.api.event.base import BaseTestEvents
|
||||
from vitrage_tempest_tests.tests.api.event.base import DOWN
|
||||
from vitrage_tempest_tests.tests.utils import wait_for_answer
|
||||
from vitrageclient import client as v_client
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestEvents(base.BaseTestCase):
|
||||
class TestEvents(BaseTestEvents):
|
||||
"""Test class for Vitrage event API"""
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.conf = service.prepare_service([])
|
||||
cls.vitrage_client = \
|
||||
v_client.Client('1', session=keystone_client.get_session(cls.conf))
|
||||
|
||||
def test_send_doctor_event_without_resource_id(self):
|
||||
"""Sending an event in Doctor format should result in an alarm"""
|
||||
details = {
|
||||
'hostname': 'host123',
|
||||
'source': 'sample_monitor',
|
||||
'cause': 'another alarm',
|
||||
'severity': 'critical',
|
||||
'status': 'down',
|
||||
'monitor_id': 'sample monitor',
|
||||
'monitor_event_id': '456',
|
||||
}
|
||||
self._test_send_doctor_event(details)
|
||||
self._test_send_doctor_event(
|
||||
self._create_doctor_event_details('host123', DOWN))
|
||||
|
||||
def test_send_doctor_event_without_resource_id_v2(self):
|
||||
"""Sending an event in Doctor format should result in an alarm"""
|
||||
details = {
|
||||
'hostname': 'host457',
|
||||
'source': 'sample_monitor',
|
||||
'cause': 'another alarm',
|
||||
'severity': 'critical',
|
||||
'status': 'down',
|
||||
'monitor_id': 'sample monitor',
|
||||
'monitor_event_id': '103',
|
||||
}
|
||||
self._test_send_doctor_event(details)
|
||||
self._test_send_doctor_event(
|
||||
self._create_doctor_event_details('host457', DOWN))
|
||||
|
||||
def _test_send_doctor_event(self, details):
|
||||
try:
|
||||
@ -98,13 +73,6 @@ class TestEvents(base.BaseTestCase):
|
||||
finally:
|
||||
LOG.warning('done')
|
||||
|
||||
def _check_alarms(self):
|
||||
api_alarms = self.vitrage_client.alarm.list(vitrage_id='all',
|
||||
all_tenants=True)
|
||||
if api_alarms:
|
||||
return True, api_alarms
|
||||
return False, api_alarms
|
||||
|
||||
def _check_alarm(self, alarm, event_time, event_type, details):
|
||||
self.assertEqual(EntityCategory.ALARM, alarm[VProps.VITRAGE_CATEGORY])
|
||||
self.assertEqual(event_type, alarm[VProps.NAME])
|
||||
|
15
vitrage_tempest_tests/tests/notifiers/__init__.py
Normal file
15
vitrage_tempest_tests/tests/notifiers/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright 2017 - Nokia
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
__author__ = 'stack'
|
130
vitrage_tempest_tests/tests/notifiers/test_mistral_notifier.py
Normal file
130
vitrage_tempest_tests/tests/notifiers/test_mistral_notifier.py
Normal file
@ -0,0 +1,130 @@
|
||||
# Copyright 2017 - Nokia
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from vitrage import os_clients
|
||||
from vitrage_tempest_tests.tests.api.event.base import BaseTestEvents
|
||||
from vitrage_tempest_tests.tests.api.event.base import DOWN
|
||||
from vitrage_tempest_tests.tests.api.event.base import UP
|
||||
from vitrage_tempest_tests.tests import utils
|
||||
from vitrage_tempest_tests.tests.utils import wait_for_answer
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
WF_NAME = 'wf_for_tempest_test_1234'
|
||||
|
||||
WF_DEFINITION = """
|
||||
---
|
||||
version: '2.0'
|
||||
|
||||
wf_for_tempest_test_1234:
|
||||
type: direct
|
||||
input:
|
||||
- farewell
|
||||
|
||||
tasks:
|
||||
goodbye:
|
||||
action: std.echo output="<% $.farewell %>, Tempest Test!"
|
||||
"""
|
||||
|
||||
|
||||
class TestMistralNotifier(BaseTestEvents):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestMistralNotifier, cls).setUpClass()
|
||||
cls.mistral_client = os_clients.mistral_client(cls.conf)
|
||||
|
||||
@utils.tempest_logger
|
||||
def test_execute_mistral(self):
|
||||
hostname = self._get_host()['name']
|
||||
|
||||
workflows = self.mistral_client.workflows.list()
|
||||
self.assertIsNotNone(workflows)
|
||||
num_workflows = len(workflows)
|
||||
|
||||
executions = self.mistral_client.executions.list()
|
||||
self.assertIsNotNone(executions)
|
||||
num_executions = len(executions)
|
||||
|
||||
alarms = wait_for_answer(2, 0.5, self._check_alarms)
|
||||
self.assertIsNotNone(alarms)
|
||||
num_alarms = len(alarms)
|
||||
|
||||
try:
|
||||
# Create a Mistral workflow
|
||||
self.mistral_client.workflows.create(WF_DEFINITION)
|
||||
|
||||
# Validate the workflow creation
|
||||
workflows = self.mistral_client.workflows.list()
|
||||
self.assertIsNotNone(workflows)
|
||||
self.assertThat(workflows, HasLength(num_workflows + 1))
|
||||
|
||||
# Send a Doctor event that should generate an alarm. According to
|
||||
# execute_mistral.yaml template, the alarm should cause execution
|
||||
# of the workflow
|
||||
details = self._create_doctor_event_details(hostname, DOWN)
|
||||
self._post_event(details)
|
||||
|
||||
# Wait for the alarm to be raised
|
||||
self.assertTrue(
|
||||
self._wait_for_status(10,
|
||||
self._check_num_vitrage_alarms,
|
||||
num_alarms=num_alarms + 1))
|
||||
|
||||
# Wait for the Mistral workflow execution
|
||||
self.assertTrue(
|
||||
self._wait_for_status(20,
|
||||
self._check_mistral_workflow_execution,
|
||||
num_executions=num_executions + 1))
|
||||
|
||||
except Exception as e:
|
||||
self._handle_exception(e)
|
||||
raise
|
||||
finally:
|
||||
self._rollback_to_default(WF_NAME, num_workflows,
|
||||
hostname, num_alarms)
|
||||
pass
|
||||
|
||||
def _rollback_to_default(self, workflow_name, num_workflows,
|
||||
hostname, num_alarms):
|
||||
# Delete the workflow
|
||||
self.mistral_client.workflows.delete(workflow_name)
|
||||
|
||||
workflows = self.mistral_client.workflows.list()
|
||||
self.assertIsNotNone(workflows)
|
||||
self.assertThat(workflows, HasLength(num_workflows))
|
||||
|
||||
# Clear the host down event and wait for the alarm to be deleted
|
||||
details = self._create_doctor_event_details(hostname, UP)
|
||||
self._post_event(details)
|
||||
|
||||
self.assertTrue(
|
||||
self._wait_for_status(10,
|
||||
self._check_num_vitrage_alarms,
|
||||
num_alarms=num_alarms))
|
||||
|
||||
def _check_num_vitrage_alarms(self, num_alarms):
|
||||
if len(self.vitrage_client.alarm.list(vitrage_id='all',
|
||||
all_tenants=True)) == num_alarms:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _check_mistral_workflow_execution(self, num_executions):
|
||||
if len(self.mistral_client.executions.list()) == num_executions:
|
||||
return True
|
||||
return False
|
@ -0,0 +1,28 @@
|
||||
metadata:
|
||||
name: execute_mistral
|
||||
description: execute mistral
|
||||
definitions:
|
||||
entities:
|
||||
- entity:
|
||||
category: ALARM
|
||||
name: compute.host.down
|
||||
template_id: host_down_alarm
|
||||
- entity:
|
||||
category: RESOURCE
|
||||
type: nova.host
|
||||
template_id: host
|
||||
relationships:
|
||||
- relationship:
|
||||
source: host_down_alarm
|
||||
relationship_type: on
|
||||
target: host
|
||||
template_id : host_down_alarm_on_host
|
||||
scenarios:
|
||||
- scenario:
|
||||
condition: host_down_alarm_on_host
|
||||
actions:
|
||||
- action:
|
||||
action_type: execute_mistral
|
||||
properties:
|
||||
workflow: wf_for_tempest_test_1234
|
||||
farewell: Hello and Goodbye
|
Loading…
Reference in New Issue
Block a user