diff --git a/congress_tempest_plugin/tests/scenario/congress_datasources/test_mistral.py b/congress_tempest_plugin/tests/scenario/congress_datasources/test_mistral.py new file mode 100644 index 0000000..6b7c224 --- /dev/null +++ b/congress_tempest_plugin/tests/scenario/congress_datasources/test_mistral.py @@ -0,0 +1,77 @@ +# Copyright 2017 VMware Corporation. All rights reserved. +# All Rights Reserved. +# +# 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 tempest import config +from tempest.lib import decorators + +from congress_tempest_plugin.tests.scenario import manager_congress + + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class TestMistralDriver(manager_congress.DatasourceDriverTestBase): + + @classmethod + def skip_checks(cls): + super(TestMistralDriver, cls).skip_checks() + if not getattr(CONF.service_available, 'mistral', False): + msg = ("%s skipped because mistral service is not configured" % + cls.__class__.__name__) + raise cls.skipException(msg) + + def setUp(self): + super(TestMistralDriver, self).setUp() + self.datasource_name = 'mistral' + self.datasource_id = manager_congress.get_datasource_id( + self.os_admin.congress_client, self.datasource_name) + + @decorators.attr(type='smoke') + def test_mistral_workflows_table(self): + table_name = 'workflows' + service_data_fetch_func = ( + lambda: self.os_admin.mistral_client.get_list_obj( + 'workflows')[1]['workflows']) + self.check_service_data_against_congress_table( + table_name, service_data_fetch_func, + missing_attributes_allowed=['description', 'updated_at']) + + @decorators.attr(type='smoke') + def test_mistral_actions_table(self): + table_name = 'actions' + service_data_fetch_func = ( + lambda: self.os_admin.mistral_client.get_list_obj( + 'actions')[1]['actions']) + self.check_service_data_against_congress_table( + table_name, service_data_fetch_func) + + # FIXME(ekcs): enable when we figure out how to use admin project in + # tempest test setup to populate executions with dummy data. + # @decorators.attr(type='smoke') + # def test_mistral_workflow_executions_table(self): + # table_name = 'workflow_executions' + # service_data_fetch_func = lambda: self.service_client.get_list_obj( + # 'executions')[1]['executions'] + # self.check_service_data_against_congress_table( + # table_name, service_data_fetch_func) + # + # @decorators.attr(type='smoke') + # def test_mistral_action_executions_table(self): + # table_name = 'action_executions' + # service_data_fetch_func = lambda: self.service_client.get_list_obj( + # 'action_executions')[1]['action_executions'] + # self.check_service_data_against_congress_table( + # table_name, service_data_fetch_func) diff --git a/congress_tempest_plugin/tests/scenario/manager_congress.py b/congress_tempest_plugin/tests/scenario/manager_congress.py index 0938b5d..22a1dc4 100755 --- a/congress_tempest_plugin/tests/scenario/manager_congress.py +++ b/congress_tempest_plugin/tests/scenario/manager_congress.py @@ -13,6 +13,7 @@ # 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 collections import random import re @@ -22,6 +23,9 @@ from oslo_log import log as logging from tempest.common import credentials_factory as credentials from tempest import config from tempest.lib.common.utils import data_utils +from tempest.lib.common.utils import test_utils +from tempest.lib import decorators +from tempest.lib import exceptions from tempest import manager as tempestmanager from congress_tempest_plugin.services.congress_network import qos_client @@ -76,6 +80,13 @@ class ScenarioPolicyBase(manager.NetworkScenarioTest): CONF.alarming_plugin.catalog_type, CONF.identity.region, CONF.alarming_plugin.endpoint_type)) + # Get mistral client + if getattr(CONF.service_available, 'mistral', False): + import mistral_tempest_tests.services.\ + v2.mistral_client as mistral_client + cls.os_admin.mistral_client = mistral_client.MistralClientV2( + auth_prov, 'workflowv2') + def _setup_network_and_servers(self): self.security_group = self._create_security_group() self.network, self.subnet, self.router = self.create_networks() @@ -275,3 +286,123 @@ class ScenarioPolicyBase(manager.NetworkScenarioTest): else: raise Exception('Failed to create policy rule (%s, %s)' % (policy_name, rule)) + + +class DatasourceDriverTestBase(ScenarioPolicyBase): + + def check_service_data_against_congress_table( + self, table_name, service_data_fetch_func, check_nonempty=True, + missing_attributes_allowed=None): + if missing_attributes_allowed is None: + missing_attributes_allowed = [] + table_schema = ( + self.os_admin.congress_client.show_datasource_table_schema( + self.datasource_id, table_name)['columns']) + table_id_col = next(i for i, c in enumerate(table_schema) + if c['name'] == 'id') + + def _check_data(): + # Fetch data each time, because test may go before service has data + service_data = service_data_fetch_func() + + if check_nonempty and len(service_data) == 0: + LOG.debug('Congress %s table source service data is empty. ' + 'Unable to check data.', table_name) + return False + LOG.debug('Congress %s table source service data: %s', + table_name, service_data) + table_data = ( + self.os_admin.congress_client.list_datasource_rows( + self.datasource_id, table_name)['results']) + LOG.debug('Congress %s table data: %s', table_name, table_data) + + # check same cardinality + if len(service_data) != len(table_data): + LOG.debug('Cardinality mismatch between congress %s ' + 'table and service data', table_name) + return False + + # construct map from id to service data items + service_data_map = {} + for data_item in service_data: + service_data_map[data_item['id']] = data_item + + for row in table_data: + try: + service_item = service_data_map[row['data'][table_id_col]] + except KeyError: + return False + for index in range(len(table_schema)): + # case: key is not present in service_item, allow + # if it is expected (sometimes an objects won't have key + # when the value is not present, e.g. description not set) + if (str(row['data'][index]) == 'None' and + table_schema[index][ + 'name'] in missing_attributes_allowed and + table_schema[index]['name'] not in service_item): + return True + # normal case: service_item value must equal + # congress table value + if (str(row['data'][index]) != + str(service_item[table_schema[index]['name']])): + return False + return True + + if not test_utils.call_until_true( + func=_check_data, duration=100, sleep_for=4): + raise exceptions.TimeoutException("Data did not converge in time " + "or failure in server") + + def check_service_data_against_congress_subtable( + self, table_name, service_data_fetch_func, + service_subdata_attribute): + def _check_data(): + # Fetch data each time, because test may go before service has data + service_data = service_data_fetch_func() + + LOG.debug('Congress %s table source service data: %s', + table_name, service_data) + table_data = ( + self.os_admin.congress_client.list_datasource_rows( + self.datasource_id, table_name)['results']) + LOG.debug('Congress %s table data: %s', table_name, table_data) + + # construct map from id to service data items + service_data_map = {} + for data_item in service_data: + service_data_map[data_item['id']] = data_item[ + service_subdata_attribute] + + expected_number_of_rows = 0 + + for row in table_data: + row_id, row_data = row['data'][0], row['data'][1] + service_subdata = service_data_map.get(row_id) + if not service_subdata or row_data not in service_subdata: + # congress table has item not in service data. + LOG.debug('Congress %s table has row (%s, %s) not in ' + 'service data', table_name, row_id, row_data) + return False + expected_number_of_rows += len(service_subdata) + + # check cardinality + if expected_number_of_rows != len(table_data): + LOG.debug('Cardinality mismatch between congress %s ' + 'table and service data', table_name) + return False + return True + + if not test_utils.call_until_true( + func=_check_data, + duration=100, sleep_for=5): + raise exceptions.TimeoutException("Data did not converge in time " + "or failure in server") + + @decorators.attr(type='smoke') + def test_update_no_error(self): + if not test_utils.call_until_true( + func=lambda: self.check_datasource_no_error( + self.datasource_name), + duration=30, sleep_for=5): + raise exceptions.TimeoutException('Datasource could not poll ' + 'without error.')