Tempest test for Vitrage integration with Mistral
Change-Id: I0021442ce0b426282fc22e4af39dcb001a45e7e0 Depends-On: Ie892482f1dede7487fbd690aff85febb17d17462 Implements: blueprint integration-with-mistral
This commit is contained in:
parent
cf8782b453
commit
43f7ea634c
@ -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 heat git://git.openstack.org/openstack/heat'
|
||||||
DEVSTACK_LOCAL_CONFIG+=$'\nenable_plugin ceilometer git://git.openstack.org/openstack/ceilometer'
|
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 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 ceilometer-alarm-evaluator,ceilometer-alarm-notifier'
|
||||||
DEVSTACK_LOCAL_CONFIG+=$'\ndisable_service n-net'
|
DEVSTACK_LOCAL_CONFIG+=$'\ndisable_service n-net'
|
||||||
DEVSTACK_LOCAL_CONFIG+=$'\ndisable_service s-account s-container s-object s-proxy'
|
DEVSTACK_LOCAL_CONFIG+=$'\ndisable_service s-account s-container s-object s-proxy'
|
||||||
@ -62,6 +64,10 @@ driver = messagingv2
|
|||||||
topics = notifications, vitrage_notifications
|
topics = notifications, vitrage_notifications
|
||||||
|
|
||||||
[[post-config|\$VITRAGE_CONF]]
|
[[post-config|\$VITRAGE_CONF]]
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
notifiers = mistral
|
||||||
|
|
||||||
[static_physical]
|
[static_physical]
|
||||||
changes_interval = 5
|
changes_interval = 5
|
||||||
|
|
||||||
|
@ -196,6 +196,8 @@ function configure_vitrage {
|
|||||||
# copy datasources
|
# copy datasources
|
||||||
cp $VITRAGE_DIR/etc/vitrage/datasources_values/*.yaml $VITRAGE_CONF_DIR/datasources_values
|
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
|
configure_auth_token_middleware $VITRAGE_CONF vitrage $VITRAGE_AUTH_CACHE_DIR
|
||||||
|
|
||||||
|
@ -13,21 +13,18 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
DEVSTACK_PATH="$BASE/new"
|
DEVSTACK_PATH="$BASE/new"
|
||||||
|
|
||||||
|
|
||||||
if [ "$1" = "api" ]; then
|
if [ "$1" = "api" ]; then
|
||||||
TESTS="topology"
|
TESTS="topology"
|
||||||
elif [ "$1" = "datasources" ]; then
|
elif [ "$1" = "datasources" ]; then
|
||||||
TESTS="datasources|test_events"
|
TESTS="datasources|test_events|notifiers"
|
||||||
else
|
else
|
||||||
TESTS="topology"
|
TESTS="topology"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd $DEVSTACK_PATH/
|
cd $DEVSTACK_PATH/
|
||||||
sudo cp -rf vitrage/vitrage_tempest_tests/tests/resources/static_physical/static_physical_configuration.yaml /etc/vitrage/
|
sudo cp -rf vitrage/vitrage_tempest_tests/tests/resources/static_physical/static_physical_configuration.yaml /etc/vitrage/
|
||||||
sudo cp -rf vitrage/vitrage_tempest_tests/tests/resources/templates/api/* /etc/vitrage/templates/
|
|
||||||
sudo cp -rf vitrage/vitrage_tempest_tests/tests/resources/heat/heat_template.yaml /etc/vitrage/
|
sudo cp -rf vitrage/vitrage_tempest_tests/tests/resources/heat/heat_template.yaml /etc/vitrage/
|
||||||
sudo cp -rf vitrage/vitrage_tempest_tests/tests/resources/heat/policy.json-tempest /etc/heat/
|
sudo cp -rf vitrage/vitrage_tempest_tests/tests/resources/heat/policy.json-tempest /etc/heat/
|
||||||
|
|
||||||
|
@ -155,13 +155,7 @@ def heat_client(conf):
|
|||||||
def mistral_client(conf):
|
def mistral_client(conf):
|
||||||
"""Get an instance of Mistral client"""
|
"""Get an instance of Mistral client"""
|
||||||
try:
|
try:
|
||||||
auth = v2.Password(
|
session = keystone_client.get_session(conf)
|
||||||
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)
|
|
||||||
|
|
||||||
endpoint = session.get_endpoint(service_type='workflowv2',
|
endpoint = session.get_endpoint(service_type='workflowv2',
|
||||||
endpoint_type='internalURL')
|
endpoint_type='internalURL')
|
||||||
args = {
|
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 datetime import datetime
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslotest import base
|
|
||||||
|
|
||||||
from vitrage.common.constants import EntityCategory
|
from vitrage.common.constants import EntityCategory
|
||||||
from vitrage.common.constants import EventProperties as EventProps
|
from vitrage.common.constants import EventProperties as EventProps
|
||||||
from vitrage.common.constants import VertexProperties as VProps
|
from vitrage.common.constants import VertexProperties as VProps
|
||||||
from vitrage import keystone_client
|
from vitrage_tempest_tests.tests.api.event.base import BaseTestEvents
|
||||||
from vitrage import service
|
from vitrage_tempest_tests.tests.api.event.base import DOWN
|
||||||
from vitrage_tempest_tests.tests.utils import wait_for_answer
|
from vitrage_tempest_tests.tests.utils import wait_for_answer
|
||||||
from vitrageclient import client as v_client
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TestEvents(base.BaseTestCase):
|
class TestEvents(BaseTestEvents):
|
||||||
"""Test class for Vitrage event API"""
|
"""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):
|
def test_send_doctor_event_without_resource_id(self):
|
||||||
"""Sending an event in Doctor format should result in an alarm"""
|
"""Sending an event in Doctor format should result in an alarm"""
|
||||||
details = {
|
self._test_send_doctor_event(
|
||||||
'hostname': 'host123',
|
self._create_doctor_event_details('host123', DOWN))
|
||||||
'source': 'sample_monitor',
|
|
||||||
'cause': 'another alarm',
|
|
||||||
'severity': 'critical',
|
|
||||||
'status': 'down',
|
|
||||||
'monitor_id': 'sample monitor',
|
|
||||||
'monitor_event_id': '456',
|
|
||||||
}
|
|
||||||
self._test_send_doctor_event(details)
|
|
||||||
|
|
||||||
def test_send_doctor_event_without_resource_id_v2(self):
|
def test_send_doctor_event_without_resource_id_v2(self):
|
||||||
"""Sending an event in Doctor format should result in an alarm"""
|
"""Sending an event in Doctor format should result in an alarm"""
|
||||||
details = {
|
self._test_send_doctor_event(
|
||||||
'hostname': 'host457',
|
self._create_doctor_event_details('host457', DOWN))
|
||||||
'source': 'sample_monitor',
|
|
||||||
'cause': 'another alarm',
|
|
||||||
'severity': 'critical',
|
|
||||||
'status': 'down',
|
|
||||||
'monitor_id': 'sample monitor',
|
|
||||||
'monitor_event_id': '103',
|
|
||||||
}
|
|
||||||
self._test_send_doctor_event(details)
|
|
||||||
|
|
||||||
def _test_send_doctor_event(self, details):
|
def _test_send_doctor_event(self, details):
|
||||||
try:
|
try:
|
||||||
@ -98,13 +73,6 @@ class TestEvents(base.BaseTestCase):
|
|||||||
finally:
|
finally:
|
||||||
LOG.warning('done')
|
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):
|
def _check_alarm(self, alarm, event_time, event_type, details):
|
||||||
self.assertEqual(EntityCategory.ALARM, alarm[VProps.VITRAGE_CATEGORY])
|
self.assertEqual(EntityCategory.ALARM, alarm[VProps.VITRAGE_CATEGORY])
|
||||||
self.assertEqual(event_type, alarm[VProps.NAME])
|
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