From 8e4026855560e42e3d4c00af29efcf2954ba0074 Mon Sep 17 00:00:00 2001 From: hparekh Date: Wed, 16 Dec 2015 17:40:53 +0900 Subject: [PATCH] Added base.py to tempest plugin. Change-Id: I6270f7176f8b652642e9ea91d045eec866436c93 Partially-Implements: blueprint mistral-tempest-plugin --- mistral_tempest_tests/services/base.py | 313 ++++++++++++++++++ .../tests/api/v2/test_mistral_basic_v2.py | 2 +- .../actions/v2/test_openstack_actions.py | 2 +- .../engine/actions/v2/test_ssh_actions.py | 2 +- 4 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 mistral_tempest_tests/services/base.py diff --git a/mistral_tempest_tests/services/base.py b/mistral_tempest_tests/services/base.py new file mode 100644 index 00000000..815333c0 --- /dev/null +++ b/mistral_tempest_tests/services/base.py @@ -0,0 +1,313 @@ +# Copyright 2013 Mirantis, Inc. 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. + +import json +import os +import time + +import mock +import six + +from tempest import clients +from tempest.common import credentials_factory as creds +from tempest import config +from tempest import test as test +from tempest_lib import auth +from tempest_lib.common import rest_client +from tempest_lib import exceptions + + +CONF = config.CONF + + +def get_resource(path): + main_package = 'mistral' + dir_path = __file__[0:__file__.find(main_package) + len(main_package) + 1] + + return open(dir_path + 'mistral/tests/resources/' + path).read() + + +def find_items(items, **props): + def _matches(item, **props): + for prop_name, prop_val in six.iteritems(props): + if item[prop_name] != prop_val: + return False + + return True + + filtered = list(filter(lambda item: _matches(item, **props), items)) + + if len(filtered) == 1: + return filtered[0] + + return filtered + + +class MistralClientBase(rest_client.RestClient): + def __init__(self, auth_provider, service_type): + super(MistralClientBase, self).__init__( + auth_provider=auth_provider, + service=service_type, + region=CONF.identity.region + ) + + if service_type not in ('workflow', 'workflowv2'): + msg = "Invalid parameter 'service_type'. " + raise exceptions.UnprocessableEntity(msg) + + self.endpoint_url = 'publicURL' + + self.workbooks = [] + self.executions = [] + self.workflows = [] + self.triggers = [] + self.actions = [] + self.action_executions = [] + + def get_list_obj(self, name): + resp, body = self.get(name) + + return resp, json.loads(body) + + def delete_obj(self, obj, name): + return self.delete('{obj}/{name}'.format(obj=obj, name=name)) + + def get_object(self, obj, id): + resp, body = self.get('{obj}/{id}'.format(obj=obj, id=id)) + + return resp, json.loads(body) + + def wait_execution_success(self, ex_body, timeout=180, url='executions'): + return self.wait_execution(ex_body, timeout=timeout, url=url) + + def wait_execution(self, ex_body, timeout=180, url='executions', + target_state='SUCCESS'): + start_time = time.time() + + expected_states = [target_state, 'RUNNING'] + + while ex_body['state'] != target_state: + if time.time() - start_time > timeout: + msg = ("Execution exceeds timeout {0} " + "to change state to {1}. " + "Execution: {2}".format(timeout, target_state, ex_body)) + raise exceptions.TimeoutException(msg) + + _, ex_body = self.get_object(url, ex_body['id']) + + if ex_body['state'] not in expected_states: + msg = ("Execution state %s is not in expected " + "states: %s" % (ex_body['state'], expected_states)) + raise exceptions.TempestException(msg) + + time.sleep(1) + + return ex_body + + +class MistralClientV2(MistralClientBase): + + def post_request(self, url, file_name): + headers = {"headers": "Content-Type:text/plain"} + + return self.post(url, get_resource(file_name), headers=headers) + + def post_json(self, url, obj): + headers = {"Content-Type": "application/json"} + + return self.post(url, json.dumps(obj), headers=headers) + + def update_request(self, url, file_name): + headers = {"headers": "Content-Type:text/plain"} + + resp, body = self.put(url, get_resource(file_name), headers=headers) + + return resp, json.loads(body) + + def get_definition(self, item, name): + resp, body = self.get("%s/%s" % (item, name)) + + return resp, json.loads(body)['definition'] + + def create_workbook(self, yaml_file): + resp, body = self.post_request('workbooks', yaml_file) + + wb_name = json.loads(body)['name'] + self.workbooks.append(wb_name) + + _, wfs = self.get_list_obj('workflows') + + for wf in wfs['workflows']: + if wf['name'].startswith(wb_name): + self.workflows.append(wf['name']) + + return resp, json.loads(body) + + def create_workflow(self, yaml_file, scope=None): + if scope: + resp, body = self.post_request('workflows?scope=public', yaml_file) + else: + resp, body = self.post_request('workflows', yaml_file) + + for wf in json.loads(body)['workflows']: + self.workflows.append(wf['name']) + + return resp, json.loads(body) + + def create_execution(self, wf_name, wf_input=None, params=None): + body = {"workflow_name": "%s" % wf_name} + + if wf_input: + body.update({'input': json.dumps(wf_input)}) + if params: + body.update({'params': json.dumps(params)}) + + resp, body = self.post('executions', json.dumps(body)) + + self.executions.append(json.loads(body)['id']) + + return resp, json.loads(body) + + def update_execution(self, execution_id, put_body): + resp, body = self.put('executions/%s' % execution_id, put_body) + + return resp, json.loads(body) + + def create_cron_trigger(self, name, wf_name, wf_input=None, pattern=None, + first_time=None, count=None): + post_body = { + 'name': name, + 'workflow_name': wf_name, + 'pattern': pattern, + 'remaining_executions': count, + 'first_execution_time': first_time + } + + if wf_input: + post_body.update({'workflow_input': json.dumps(wf_input)}) + + rest, body = self.post('cron_triggers', json.dumps(post_body)) + + self.triggers.append(name) + + return rest, json.loads(body) + + def create_action(self, yaml_file): + resp, body = self.post_request('actions', yaml_file) + + self.actions.extend( + [action['name'] for action in json.loads(body)['actions']]) + + return resp, json.loads(body) + + def get_wf_tasks(self, wf_name): + all_tasks = self.get_list_obj('tasks')[1]['tasks'] + + return [t for t in all_tasks if t['workflow_name'] == wf_name] + + def create_action_execution(self, request_body): + resp, body = self.post_json('action_executions', request_body) + + params = json.loads(request_body.get('params', '{}')) + if params.get('save_result', False): + self.action_executions.append(json.loads(body)['id']) + + return resp, json.loads(body) + + +class AuthProv(auth.KeystoneV2AuthProvider): + def __init__(self): + self.alt_part = None + + def auth_request(self, method, url, *args, **kwargs): + req_url, headers, body = super(AuthProv, self).auth_request( + method, url, *args, **kwargs) + return 'http://localhost:8989/{0}/{1}'.format( + os.environ['VERSION'], url), headers, body + + def get_auth(self): + return 'mock_str', 'mock_str' + + def base_url(self, *args, **kwargs): + return '' + + +class TestCase(test.BaseTestCase): + @classmethod + def resource_setup(cls): + """Client authentication. + + This method allows to initialize authentication before + each test case and define parameters of Mistral API Service. + """ + super(TestCase, cls).resource_setup() + + if 'WITHOUT_AUTH' in os.environ: + cls.mgr = mock.MagicMock() + cls.mgr.auth_provider = AuthProv() + else: + cls.creds = creds.get_configured_credentials( + credential_type='user' + ) + cls.mgr = clients.Manager(cls.creds) + + cls.alt_creds = creds.get_configured_credentials( + credential_type='alt_user' + ) + cls.alt_mgr = clients.Manager(cls.alt_creds) + + if cls._service == 'workflowv2': + cls.client = MistralClientV2( + cls.mgr.auth_provider, cls._service) + cls.alt_client = MistralClientV2( + cls.alt_mgr.auth_provider, cls._service) + + def setUp(self): + super(TestCase, self).setUp() + + def tearDown(self): + super(TestCase, self).tearDown() + + for wb in self.client.workbooks: + self.client.delete_obj('workbooks', wb) + + self.client.workbooks = [] + + +class TestCaseAdvanced(TestCase): + @classmethod + def resource_setup(cls): + super(TestCaseAdvanced, cls).resource_setup() + + cls.server_client = clients.ServersClient( + cls.mgr.auth_provider, + "compute", + region=CONF.identity.region + ) + + cls.image_ref = CONF.compute.image_ref + cls.flavor_ref = CONF.compute.flavor_ref + + def tearDown(self): + for wb in self.client.workbooks: + self.client.delete_obj('workbooks', wb) + + self.client.workbooks = [] + + for ex in self.client.executions: + self.client.delete_obj('executions', ex) + + self.client.executions = [] + + super(TestCaseAdvanced, self).tearDown() diff --git a/mistral_tempest_tests/tests/api/v2/test_mistral_basic_v2.py b/mistral_tempest_tests/tests/api/v2/test_mistral_basic_v2.py index 1319fb41..f546e955 100644 --- a/mistral_tempest_tests/tests/api/v2/test_mistral_basic_v2.py +++ b/mistral_tempest_tests/tests/api/v2/test_mistral_basic_v2.py @@ -21,8 +21,8 @@ from tempest import test from tempest_lib import decorators from tempest_lib import exceptions -from mistral.tests.functional import base from mistral import utils +from mistral_tempest_tests.services import base LOG = logging.getLogger(__name__) diff --git a/mistral_tempest_tests/tests/scenario/engine/actions/v2/test_openstack_actions.py b/mistral_tempest_tests/tests/scenario/engine/actions/v2/test_openstack_actions.py index b7cfe26f..69f10693 100644 --- a/mistral_tempest_tests/tests/scenario/engine/actions/v2/test_openstack_actions.py +++ b/mistral_tempest_tests/tests/scenario/engine/actions/v2/test_openstack_actions.py @@ -14,7 +14,7 @@ from tempest import test -from mistral.tests.functional import base +from mistral_tempest_tests.services import base class OpenStackActionsTestsV2(base.TestCase): diff --git a/mistral_tempest_tests/tests/scenario/engine/actions/v2/test_ssh_actions.py b/mistral_tempest_tests/tests/scenario/engine/actions/v2/test_ssh_actions.py index 63e24a85..933af012 100644 --- a/mistral_tempest_tests/tests/scenario/engine/actions/v2/test_ssh_actions.py +++ b/mistral_tempest_tests/tests/scenario/engine/actions/v2/test_ssh_actions.py @@ -22,9 +22,9 @@ from paramiko import ssh_exception from tempest import config from tempest import test -from mistral.tests.functional import base from mistral import utils from mistral.utils import ssh_utils +from mistral_tempest_tests.services import base LOG = logging.getLogger(__name__)