Remove intree mistral tempest plugin
* https://review.openstack.org/524869 moves the intree mistral tempest plugin to a new home openstack/mistral-tempest-plugin let's use it. * It also removed intree mistral tempest plugin as well as fix the jobs to use the same. Change-Id: I28ba8408452637aa093b343441f3715dc4754a3f
This commit is contained in:
parent
c6e086d9b2
commit
4f57c90d69
@ -13,6 +13,7 @@
|
|||||||
- openstack/python-mistralclient
|
- openstack/python-mistralclient
|
||||||
- openstack/zaqar
|
- openstack/zaqar
|
||||||
- openstack/python-zaqarclient
|
- openstack/python-zaqarclient
|
||||||
|
- openstack/mistral-tempest-plugin
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: mistral-devstack-dsvm-kombu
|
name: mistral-devstack-dsvm-kombu
|
||||||
@ -29,6 +30,7 @@
|
|||||||
- openstack/python-mistralclient
|
- openstack/python-mistralclient
|
||||||
- openstack/zaqar
|
- openstack/zaqar
|
||||||
- openstack/python-zaqarclient
|
- openstack/python-zaqarclient
|
||||||
|
- openstack/mistral-tempest-plugin
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: mistral-devstack-dsvm-non-apache
|
name: mistral-devstack-dsvm-non-apache
|
||||||
@ -45,6 +47,7 @@
|
|||||||
- openstack/python-mistralclient
|
- openstack/python-mistralclient
|
||||||
- openstack/zaqar
|
- openstack/zaqar
|
||||||
- openstack/python-zaqarclient
|
- openstack/python-zaqarclient
|
||||||
|
- openstack/mistral-tempest-plugin
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: mistral-docker-buildimage
|
name: mistral-docker-buildimage
|
||||||
|
@ -30,5 +30,4 @@ sudo cp $BASE/new/tempest/etc/logging.conf.sample $BASE/new/tempest/etc/logging.
|
|||||||
(cd $BASE/new/mistral/; sudo python setup.py install)
|
(cd $BASE/new/mistral/; sudo python setup.py install)
|
||||||
|
|
||||||
export TOX_TESTENV_PASSENV=ZUUL_PROJECT
|
export TOX_TESTENV_PASSENV=ZUUL_PROJECT
|
||||||
(cd $BASE/new/tempest/; sudo -E testr init)
|
(cd $BASE/new/tempest/; sudo -E tox -evenv-tempest -- tempest run -r mistral)
|
||||||
(cd $BASE/new/tempest/; sudo -E tox -eall-plugin mistral)
|
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
==============================
|
|
||||||
Tempest Integration of Mistral
|
|
||||||
==============================
|
|
||||||
|
|
||||||
This directory contains Tempest tests to cover the mistral project.
|
|
||||||
|
|
||||||
To list all Mistral tempest cases, go to tempest directory, then run::
|
|
||||||
|
|
||||||
$ testr list-tests mistral
|
|
||||||
|
|
||||||
To run only these tests in tempest, go to tempest directory, then run::
|
|
||||||
|
|
||||||
$ ./run_tempest.sh -N -- mistral
|
|
||||||
|
|
||||||
To run a single test case, go to tempest directory, then run with test case name, e.g.::
|
|
||||||
|
|
||||||
$ ./run_tempest.sh -N -- mistral_tempest_tests.tests.api.v2.test_mistral_basic_v2.WorkbookTestsV2.test_get_workbook
|
|
||||||
|
|
||||||
Alternatively, to run mistral tempest plugin tests using tox, go to tempest directory, then run::
|
|
||||||
|
|
||||||
$ tox -eall-plugin mistral
|
|
||||||
|
|
||||||
And, to run a specific test::
|
|
||||||
|
|
||||||
$ tox -eall-plugin mistral_tempest_tests.tests.api.v2.test_mistral_basic_v2.WorkbookTestsV2.test_get_workbook
|
|
@ -1,21 +0,0 @@
|
|||||||
# Copyright 2016 Catalyst IT Limited
|
|
||||||
# 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_config import cfg
|
|
||||||
|
|
||||||
service_option = cfg.BoolOpt('mistral',
|
|
||||||
default=True,
|
|
||||||
help="Whether or not Mistral is expected to be"
|
|
||||||
"available")
|
|
@ -1,37 +0,0 @@
|
|||||||
# Copyright 2015
|
|
||||||
# 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 os
|
|
||||||
|
|
||||||
from tempest.test_discover import plugins
|
|
||||||
|
|
||||||
from mistral_tempest_tests import config as mistral_config
|
|
||||||
|
|
||||||
|
|
||||||
class MistralTempestPlugin(plugins.TempestPlugin):
|
|
||||||
def load_tests(self):
|
|
||||||
base_path = os.path.split(os.path.dirname(
|
|
||||||
os.path.abspath(__file__)))[0]
|
|
||||||
test_dir = "mistral_tempest_tests/tests/"
|
|
||||||
full_test_dir = os.path.join(base_path, test_dir)
|
|
||||||
return full_test_dir, base_path
|
|
||||||
|
|
||||||
def register_opts(self, conf):
|
|
||||||
conf.register_opt(mistral_config.service_option,
|
|
||||||
group='service_available')
|
|
||||||
|
|
||||||
def get_opt_lists(self):
|
|
||||||
return [('service_available', [mistral_config.service_option])]
|
|
@ -1,132 +0,0 @@
|
|||||||
# Copyright 2013 Mirantis, Inc. All Rights Reserved.
|
|
||||||
# Copyright 2016 NEC Corporation. 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
|
|
||||||
|
|
||||||
|
|
||||||
from tempest import config
|
|
||||||
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_tempest_tests'
|
|
||||||
dir_path = __file__[0:__file__.find(main_package)]
|
|
||||||
|
|
||||||
return open(dir_path +
|
|
||||||
'mistral_tempest_tests/tests/resources/' +
|
|
||||||
path).read()
|
|
||||||
|
|
||||||
|
|
||||||
def find_items(items, **props):
|
|
||||||
def _matches(item, **props):
|
|
||||||
for prop_name, prop_val in props.items():
|
|
||||||
if item[prop_name] != prop_val:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
filtered = list([item for item in items if _matches(item, **props)])
|
|
||||||
|
|
||||||
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,
|
|
||||||
disable_ssl_certificate_validation=True
|
|
||||||
)
|
|
||||||
|
|
||||||
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 = []
|
|
||||||
self.event_triggers = []
|
|
||||||
|
|
||||||
def get_list_obj(self, url_path):
|
|
||||||
resp, body = self.get(url_path)
|
|
||||||
|
|
||||||
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 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 ''
|
|
@ -1,227 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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
|
|
||||||
|
|
||||||
from oslo_utils import uuidutils
|
|
||||||
from tempest import config
|
|
||||||
|
|
||||||
from mistral_tempest_tests.services import base
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class MistralClientV2(base.MistralClientBase):
|
|
||||||
|
|
||||||
def post_request(self, url_path, file_name):
|
|
||||||
headers = {"headers": "Content-Type:text/plain"}
|
|
||||||
|
|
||||||
return self.post(
|
|
||||||
url_path,
|
|
||||||
base.get_resource(file_name),
|
|
||||||
headers=headers
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_request(self, url_path):
|
|
||||||
headers = {"headers": "Content-Type:application/json"}
|
|
||||||
|
|
||||||
return self.get(url_path, headers=headers)
|
|
||||||
|
|
||||||
def post_json(self, url_path, obj, extra_headers={}):
|
|
||||||
headers = {"Content-Type": "application/json"}
|
|
||||||
headers = dict(headers, **extra_headers)
|
|
||||||
return self.post(url_path, json.dumps(obj), headers=headers)
|
|
||||||
|
|
||||||
def update_request(self, url_path, file_name):
|
|
||||||
headers = {"headers": "Content-Type:text/plain"}
|
|
||||||
|
|
||||||
resp, body = self.put(
|
|
||||||
url_path,
|
|
||||||
base.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, namespace=None):
|
|
||||||
url_path = 'workflows?'
|
|
||||||
|
|
||||||
if scope:
|
|
||||||
url_path += 'scope=public&'
|
|
||||||
|
|
||||||
if namespace:
|
|
||||||
url_path += 'namespace=' + namespace
|
|
||||||
|
|
||||||
resp, body = self.post_request(url_path, yaml_file)
|
|
||||||
|
|
||||||
for wf in json.loads(body)['workflows']:
|
|
||||||
identifier = wf['id'] if namespace else wf['name']
|
|
||||||
self.workflows.append(identifier)
|
|
||||||
|
|
||||||
return resp, json.loads(body)
|
|
||||||
|
|
||||||
def get_workflow(self, wf_identifier, namespace=None):
|
|
||||||
|
|
||||||
url_path = 'workflows/' + wf_identifier
|
|
||||||
if namespace:
|
|
||||||
url_path += 'namespace=' + namespace
|
|
||||||
|
|
||||||
resp, body = self.get_request(url_path)
|
|
||||||
|
|
||||||
return resp, json.loads(body)
|
|
||||||
|
|
||||||
def update_workflow(self, file_name, namespace=None):
|
|
||||||
url_path = "workflows?"
|
|
||||||
|
|
||||||
if namespace:
|
|
||||||
url_path += 'namespace=' + namespace
|
|
||||||
|
|
||||||
return self.update_request(url_path, file_name=file_name)
|
|
||||||
|
|
||||||
def get_action_execution(self, action_execution_id):
|
|
||||||
return self.get('action_executions/%s' % action_execution_id)
|
|
||||||
|
|
||||||
def get_action_executions(self, task_id=None):
|
|
||||||
url_path = 'action_executions'
|
|
||||||
if task_id:
|
|
||||||
url_path += '?task_execution_id=%s' % task_id
|
|
||||||
|
|
||||||
return self.get_list_obj(url_path)
|
|
||||||
|
|
||||||
def create_execution(self, identifier, wf_namespace=None, wf_input=None,
|
|
||||||
params=None):
|
|
||||||
if uuidutils.is_uuid_like(identifier):
|
|
||||||
body = {"workflow_id": "%s" % identifier}
|
|
||||||
else:
|
|
||||||
body = {"workflow_name": "%s" % identifier}
|
|
||||||
|
|
||||||
if wf_namespace:
|
|
||||||
body.update({'workflow_namespace': wf_namespace})
|
|
||||||
|
|
||||||
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 get_execution(self, execution_id):
|
|
||||||
return self.get('executions/%s' % execution_id)
|
|
||||||
|
|
||||||
def get_executions(self, task_id):
|
|
||||||
url_path = 'executions'
|
|
||||||
if task_id:
|
|
||||||
url_path += '?task_execution_id=%s' % task_id
|
|
||||||
|
|
||||||
return self.get_list_obj(url_path)
|
|
||||||
|
|
||||||
def get_tasks(self, execution_id=None):
|
|
||||||
url_path = 'tasks'
|
|
||||||
if execution_id:
|
|
||||||
url_path += '?workflow_execution_id=%s' % execution_id
|
|
||||||
|
|
||||||
return self.get_list_obj(url_path)
|
|
||||||
|
|
||||||
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, extra_headers={}):
|
|
||||||
resp, body = self.post_json('action_executions', request_body,
|
|
||||||
extra_headers)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def create_event_trigger(self, wf_id, exchange, topic, event, name='',
|
|
||||||
wf_input=None, wf_params=None):
|
|
||||||
post_body = {
|
|
||||||
'workflow_id': wf_id,
|
|
||||||
'exchange': exchange,
|
|
||||||
'topic': topic,
|
|
||||||
'event': event,
|
|
||||||
'name': name
|
|
||||||
}
|
|
||||||
|
|
||||||
if wf_input:
|
|
||||||
post_body.update({'workflow_input': json.dumps(wf_input)})
|
|
||||||
|
|
||||||
if wf_params:
|
|
||||||
post_body.update({'workflow_params': json.dumps(wf_params)})
|
|
||||||
|
|
||||||
rest, body = self.post('event_triggers', json.dumps(post_body))
|
|
||||||
|
|
||||||
event_trigger = json.loads(body)
|
|
||||||
self.event_triggers.append(event_trigger['id'])
|
|
||||||
|
|
||||||
return rest, event_trigger
|
|
@ -1,254 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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 six
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class ActionExecutionTestsV2(base.TestCase):
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def resource_setup(cls):
|
|
||||||
super(ActionExecutionTestsV2, cls).resource_setup()
|
|
||||||
|
|
||||||
cls.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.echo',
|
|
||||||
'input': '{"output": "Hello, Mistral!"}'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def resource_cleanup(cls):
|
|
||||||
for action_ex in cls.client.action_executions:
|
|
||||||
try:
|
|
||||||
cls.client.delete_obj('action_executions', action_ex)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception(
|
|
||||||
'Exception raised when deleting '
|
|
||||||
'action_executions %s, error message: %s.',
|
|
||||||
action_ex, six.text_type(e)
|
|
||||||
)
|
|
||||||
|
|
||||||
cls.client.action_executions = []
|
|
||||||
|
|
||||||
super(ActionExecutionTestsV2, cls).resource_cleanup()
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('a72603bd-5d49-4d92-9747-8da6322e867d')
|
|
||||||
def test_run_action_execution(self):
|
|
||||||
resp, body = self.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.echo',
|
|
||||||
'input': '{"output": "Hello, Mistral!"}'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
output = json.loads(body['output'])
|
|
||||||
self.assertDictEqual(
|
|
||||||
{'result': 'Hello, Mistral!'},
|
|
||||||
output
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('0623cb62-b20a-45c8-afd9-8da46e1bb3cb')
|
|
||||||
def test_list_action_executions(self):
|
|
||||||
resp, body = self.client.get_list_obj('action_executions')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('cd36ea00-7e22-4c3d-90c3-fb441b93cf12')
|
|
||||||
def test_output_appear_in_response_only_when_needed(self):
|
|
||||||
resp, body = self.client.get_list_obj('action_executions')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
action_execution = body['action_executions'][0]
|
|
||||||
self.assertNotIn("output", action_execution)
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'action_executions?include_output=True'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
action_execution = body['action_executions'][0]
|
|
||||||
self.assertIn("output", action_execution)
|
|
||||||
|
|
||||||
resp, body = self.client.get_action_execution(action_execution['id'])
|
|
||||||
self.assertIn("output", body)
|
|
||||||
|
|
||||||
# Test when passing task execution ID
|
|
||||||
|
|
||||||
resp, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
wf_name = body['workflows'][0]['name']
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
resp, body = self.client.create_execution(wf_name)
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
resp, body = self.client.get_list_obj('tasks')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
task_id = body['tasks'][0]['id']
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'action_executions?include_output=true&task_execution_id=%s' %
|
|
||||||
task_id
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
action_execution = body['action_executions'][0]
|
|
||||||
self.assertIn("output", action_execution)
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'action_executions?&task_execution_id=%s' %
|
|
||||||
task_id
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
action_execution = body['action_executions'][0]
|
|
||||||
self.assertNotIn("output", action_execution)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('dc76aeda-9243-45cf-bfd2-141d3af8b28b')
|
|
||||||
def test_run_action_std_http(self):
|
|
||||||
resp, body = self.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.http',
|
|
||||||
'input': '{"url": "http://wiki.openstack.org"}'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
output = json.loads(body['output'])
|
|
||||||
self.assertTrue(output['result']['status'] in range(200, 307))
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('befa9b1c-01a4-41bc-b060-88cb1b147dfb')
|
|
||||||
def test_run_action_std_http_error(self):
|
|
||||||
resp, body = self.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.http',
|
|
||||||
'input': '{"url": "http://www.google.ru/not-found-test"}'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
output = json.loads(body['output'])
|
|
||||||
self.assertEqual(404, output['result']['status'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.related_bug('1667415')
|
|
||||||
@decorators.idempotent_id('3c73de7a-4af0-4657-90d6-d7ebd3c7da18')
|
|
||||||
def test_run_action_std_http_non_utf8_response(self):
|
|
||||||
resp, body = self.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.http',
|
|
||||||
'input':
|
|
||||||
'{"url": "https://httpbin.org/encoding/utf8"}'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
output = json.loads(body['output'])
|
|
||||||
self.assertEqual(200, output['result']['status'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('d98586bf-fdc4-44f6-9837-700d35b5f889')
|
|
||||||
def test_create_action_execution(self):
|
|
||||||
resp, body = self.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.echo',
|
|
||||||
'input': '{"output": "Hello, Mistral!"}',
|
|
||||||
'params': '{"save_result": true}'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual('RUNNING', body['state'])
|
|
||||||
|
|
||||||
# We must reread action execution in order to get actual
|
|
||||||
# state and output.
|
|
||||||
body = self.client.wait_execution_success(
|
|
||||||
body,
|
|
||||||
url='action_executions'
|
|
||||||
)
|
|
||||||
output = json.loads(body['output'])
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', body['state'])
|
|
||||||
self.assertDictEqual(
|
|
||||||
{'result': 'Hello, Mistral!'},
|
|
||||||
output
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('99f22c17-6fb4-4480-96d3-4a82672916b7')
|
|
||||||
def test_delete_nonexistent_action_execution(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.NotFound,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'action_executions',
|
|
||||||
'nonexist'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('2dbd74ba-4950-4c52-8bd3-070d634dcd05')
|
|
||||||
def test_create_action_execution_sync(self):
|
|
||||||
resp, body = self.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.echo',
|
|
||||||
'input': '{"output": "Hello Tempest"}'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
output = json.loads(body['output'])
|
|
||||||
self.assertEqual("Hello Tempest", output['result'])
|
|
||||||
|
|
||||||
@decorators.idempotent_id('9438e195-031c-4502-b216-6d72941ec281')
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
def test_action_execution_of_workflow_within_namespace(self):
|
|
||||||
|
|
||||||
resp, body = self.client.create_workflow('wf_v2.yaml', namespace='abc')
|
|
||||||
wf_name = "wf"
|
|
||||||
wf_namespace = body['workflows'][0]['namespace']
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
resp, execution = self.client.create_execution(
|
|
||||||
wf_name,
|
|
||||||
wf_namespace=wf_namespace
|
|
||||||
)
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
resp, body = self.client.get_list_obj('tasks')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
task_id = body['tasks'][0]['id']
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'action_executions?include_output=true&task_execution_id=%s' %
|
|
||||||
task_id)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
action_execution = body['action_executions'][0]
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
action_execution = body['action_executions'][0]
|
|
||||||
self.assertEqual(wf_namespace, action_execution['workflow_namespace'])
|
|
@ -1,414 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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 datetime
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
from mistral_tempest_tests.tests import utils
|
|
||||||
|
|
||||||
|
|
||||||
class ActionTestsV2(base.TestCase):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
def get_field_value(self, body, act_name, field):
|
|
||||||
return [body['actions'][i][field]
|
|
||||||
for i in range(len(body['actions']))
|
|
||||||
if body['actions'][i]['name'] == act_name][0]
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
for act in self.client.actions:
|
|
||||||
self.client.delete_obj('actions', act)
|
|
||||||
self.client.actions = []
|
|
||||||
|
|
||||||
super(ActionTestsV2, self).tearDown()
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('2e1a578a-1c27-409a-96be-84b5c41498cd')
|
|
||||||
def test_get_list_actions(self):
|
|
||||||
resp, body = self.client.get_list_obj('actions')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertNotEmpty(body['actions'])
|
|
||||||
self.assertNotIn('next', body)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('786ee85c-c32d-4ac9-8f45-79ab6bc47ef1')
|
|
||||||
def test_get_list_actions_with_pagination(self):
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'actions?limit=1&sort_keys=name&sort_dirs=desc'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(1, len(body['actions']))
|
|
||||||
self.assertIn('next', body)
|
|
||||||
|
|
||||||
name_1 = body['actions'][0].get('name')
|
|
||||||
next = body.get('next')
|
|
||||||
|
|
||||||
param_dict = utils.get_dict_from_string(
|
|
||||||
next.split('?')[1],
|
|
||||||
delimiter='&'
|
|
||||||
)
|
|
||||||
|
|
||||||
# NOTE: 'id' gets included into sort keys automatically with 'desc'
|
|
||||||
# sorting to avoid pagination looping.
|
|
||||||
expected_sub_dict = {
|
|
||||||
'limit': 1,
|
|
||||||
'sort_keys': 'name,id',
|
|
||||||
'sort_dirs': 'desc,asc'
|
|
||||||
}
|
|
||||||
|
|
||||||
self.assertDictContainsSubset(expected_sub_dict, param_dict)
|
|
||||||
|
|
||||||
# Query again using 'next' hint
|
|
||||||
url_param = next.split('/')[-1]
|
|
||||||
resp, body = self.client.get_list_obj(url_param)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(1, len(body['actions']))
|
|
||||||
|
|
||||||
name_2 = body['actions'][0].get('name')
|
|
||||||
|
|
||||||
self.assertGreater(name_1, name_2)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('5148358e-200f-49c7-8e88-1ddeec61c6a9')
|
|
||||||
def test_get_list_actions_nonexist_sort_dirs(self):
|
|
||||||
context = self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'actions?limit=1&sort_keys=id&sort_dirs=nonexist'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
'Unknown sort direction',
|
|
||||||
context.resp_body.get('faultstring')
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('85482ce8-70f4-47a6-9e80-de1ac22b6412')
|
|
||||||
def test_get_list_actions_invalid_limit(self):
|
|
||||||
context = self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'actions?limit=-1&sort_keys=id&sort_dirs=asc'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
'Limit must be positive',
|
|
||||||
context.resp_body.get('faultstring')
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('a203e75b-2013-422f-b9eb-da4375041058')
|
|
||||||
def test_get_list_actions_duplicate_sort_keys(self):
|
|
||||||
context = self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'actions?limit=1&sort_keys=id,id&sort_dirs=asc,asc'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
'Length of sort_keys must be equal or greater than sort_dirs',
|
|
||||||
context.resp_body.get('faultstring')
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('9a53af71-8f1e-4ad5-b572-2c4c621715c0')
|
|
||||||
def test_get_list_actions_equal_to_filter(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('actions?is_system=False')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertNotEmpty(body['actions'])
|
|
||||||
|
|
||||||
for act in body['actions']:
|
|
||||||
self.assertFalse(act['is_system'])
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('3c3d28ce-9490-41ae-a918-c28f843841e1')
|
|
||||||
def test_get_list_actions_not_equal_to_filter(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('actions?is_system=neq:False')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertNotEmpty(body['actions'])
|
|
||||||
|
|
||||||
for act in body['actions']:
|
|
||||||
self.assertTrue(act['is_system'])
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('84823a84-5caa-427d-8a2c-622a1d1893b1')
|
|
||||||
def test_get_list_actions_in_list_filter(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
created_acts = [action['name'] for action in body['actions']]
|
|
||||||
_, body = self.client.get_object('actions', created_acts[0])
|
|
||||||
time = body['created_at']
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'actions?created_at=in:' + time.replace(' ', '%20'))
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
action_names = [action['name'] for action in body['actions']]
|
|
||||||
self.assertListEqual(created_acts, action_names)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('4b05dfcf-ef39-4032-9528-c8422c7329dd')
|
|
||||||
def test_get_list_actions_not_in_list_filter(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
created_acts = [action['name'] for action in body['actions']]
|
|
||||||
_, body = self.client.get_object('actions', created_acts[0])
|
|
||||||
time = body['created_at']
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'actions?created_at=nin:' + time.replace(' ', '%20')
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
action_names = [action['name'] for action in body['actions']]
|
|
||||||
for act in created_acts:
|
|
||||||
self.assertNotIn(act, action_names)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('20b3d527-447d-492b-8cb7-ac5e3757d7d5')
|
|
||||||
def test_get_list_actions_greater_than_filter(self):
|
|
||||||
time = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'actions?created_at=gt:' + time.replace(' ', '%20')
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEmpty(body['actions'])
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('7f598dba-f169-47ec-a487-f0ed31484aff')
|
|
||||||
def test_get_list_actions_greater_than_equal_to_filter(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
created_acts = [action['name'] for action in body['actions']]
|
|
||||||
_, body = self.client.get_object('actions', created_acts[0])
|
|
||||||
time = body['created_at']
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'actions?created_at=gte:' + time.replace(' ', '%20')
|
|
||||||
)
|
|
||||||
|
|
||||||
actions = [action['name'] for action in body['actions']]
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertIn(created_acts[0], actions)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('874fb57d-a762-4dc3-841d-396657510d23')
|
|
||||||
def test_get_list_actions_less_than_filter(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
created_acts = [action['name'] for action in body['actions']]
|
|
||||||
_, body = self.client.get_object('actions', created_acts[0])
|
|
||||||
time = body['created_at']
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'actions?created_at=lt:' + time.replace(' ', '%20')
|
|
||||||
)
|
|
||||||
|
|
||||||
actions = [action['name'] for action in body['actions']]
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertNotIn(created_acts[0], actions)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('1fda6c31-b0c3-4b78-9f67-b920e1f6c973')
|
|
||||||
def test_get_list_actions_less_than_equal_to_filter(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
created_acts = [action['name'] for action in body['actions']]
|
|
||||||
_, body = self.client.get_object('actions', created_acts[0])
|
|
||||||
time = body['created_at']
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'actions?created_at=lte:' + time.replace(' ', '%20')
|
|
||||||
)
|
|
||||||
|
|
||||||
actions = [action['name'] for action in body['actions']]
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertIn(created_acts[0], actions)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('cbb716f1-7fc7-4884-8fa9-6ff2bc35ee29')
|
|
||||||
def test_get_list_actions_multiple_filter(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
created_acts = [action['name'] for action in body['actions']]
|
|
||||||
_, body = self.client.get_object('actions', created_acts[0])
|
|
||||||
time = body['created_at']
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'actions?created_at=lte:' + time.replace(' ', '%20') +
|
|
||||||
'&is_system=False'
|
|
||||||
)
|
|
||||||
|
|
||||||
actions = [action['name'] for action in body['actions']]
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertIn(created_acts[0], actions)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('45fdc1f3-4d89-4035-9b76-08ef94c92628')
|
|
||||||
def test_get_list_actions_invalid_filter(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'actions?is_system<False'
|
|
||||||
)
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'actions?is_system!=False'
|
|
||||||
)
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'actions?created_at>2016-02-23%2008:51:26'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('5dbceaf3-6a32-4a4f-9427-1bbdb6f3c574')
|
|
||||||
def test_create_and_delete_few_actions(self):
|
|
||||||
resp, body = self.client.create_action('action_v2.yaml')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
created_acts = [action['name'] for action in body['actions']]
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('actions')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
actions = [action['name'] for action in body['actions']]
|
|
||||||
|
|
||||||
for act in created_acts:
|
|
||||||
self.assertIn(act, actions)
|
|
||||||
self.client.delete_obj('actions', act)
|
|
||||||
|
|
||||||
_, body = self.client.get_list_obj('actions')
|
|
||||||
actions = [action['name'] for action in body['actions']]
|
|
||||||
|
|
||||||
for act in created_acts:
|
|
||||||
self.assertNotIn(act, actions)
|
|
||||||
self.client.actions.remove(act)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('d7dad5de-6b1f-4813-b789-78f075252639')
|
|
||||||
def test_get_action(self):
|
|
||||||
_, body = self.client.create_action('action_v2.yaml')
|
|
||||||
action_name = body['actions'][0]['name']
|
|
||||||
resp, body = self.client.get_object('actions', action_name)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(action_name, body['name'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('21a031c8-8e2d-421f-8dfe-71a3b5e44381')
|
|
||||||
def test_update_action(self):
|
|
||||||
_, body = self.client.create_action('action_v2.yaml')
|
|
||||||
action = body['actions'][0]['name']
|
|
||||||
|
|
||||||
act_created_at = self.get_field_value(
|
|
||||||
body=body, act_name=action, field='created_at')
|
|
||||||
|
|
||||||
self.assertNotIn('updated at', body['actions'])
|
|
||||||
|
|
||||||
resp, body = self.client.update_request('actions', 'action_v2.yaml')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
actions = [act['name'] for act in body['actions']]
|
|
||||||
self.assertIn(action, actions)
|
|
||||||
|
|
||||||
updated_act_created_at = self.get_field_value(
|
|
||||||
body=body, act_name=action, field='created_at')
|
|
||||||
|
|
||||||
self.assertEqual(act_created_at.split(".")[0], updated_act_created_at)
|
|
||||||
self.assertTrue(all(['updated_at' in item
|
|
||||||
for item in body['actions']]))
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('329b1030-c55c-45f0-8129-cc892bc23dcc')
|
|
||||||
def test_get_action_definition(self):
|
|
||||||
_, body = self.client.create_action('action_v2.yaml')
|
|
||||||
act_name = body['actions'][0]['name']
|
|
||||||
|
|
||||||
resp, body = self.client.get_definition('actions', act_name)
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertIsNotNone(body)
|
|
||||||
self.assertIn(act_name, body)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('c2b5be88-571a-4855-922f-9a338dba6adb')
|
|
||||||
def test_get_nonexistent_action(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.NotFound,
|
|
||||||
self.client.get_object,
|
|
||||||
'actions', 'nonexist'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('fc2fafcb-9bb4-4a18-a507-3f9964f4a08a')
|
|
||||||
def test_double_creation(self):
|
|
||||||
self.client.create_action('action_v2.yaml')
|
|
||||||
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.Conflict,
|
|
||||||
self.client.create_action,
|
|
||||||
'action_v2.yaml'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('0c456a73-9c39-4aeb-b3ca-3ea4338bc9ab')
|
|
||||||
def test_create_action_invalid_def(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.create_action,
|
|
||||||
'wb_v2.yaml'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('469677b5-22ab-4e2a-aee6-5bcc9dac93de')
|
|
||||||
def test_update_action_invalid_def(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.update_request,
|
|
||||||
'actions', 'wb_v2.yaml'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('ab444607-40fc-47cb-982f-83762d5b64c9')
|
|
||||||
def test_delete_nonexistent_action(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.NotFound,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'actions', 'nonexist'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('74d0d480-793a-46ca-b88a-8336c1897f3a')
|
|
||||||
def test_delete_standard_action(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'actions', 'nova.servers_create'
|
|
||||||
)
|
|
@ -1,237 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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_concurrency.fixture import lockutils
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class CronTriggerTestsV2(base.TestCase):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(CronTriggerTestsV2, self).setUp()
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
self.wf_name = body['workflows'][0]['name']
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
|
|
||||||
for tr in self.client.triggers:
|
|
||||||
self.client.delete_obj('cron_triggers', tr)
|
|
||||||
self.client.triggers = []
|
|
||||||
|
|
||||||
for wf in self.client.workflows:
|
|
||||||
self.client.delete_obj('workflows', wf)
|
|
||||||
self.client.workflows = []
|
|
||||||
|
|
||||||
super(CronTriggerTestsV2, self).tearDown()
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('c53b44dd-59b3-4a4b-b22a-21abb4cecea0')
|
|
||||||
def test_get_list_cron_triggers(self):
|
|
||||||
resp, body = self.client.get_list_obj('cron_triggers')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEmpty(body['cron_triggers'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('fbc641fa-8704-45b3-b259-136eb956394c')
|
|
||||||
def test_create_and_delete_cron_triggers(self):
|
|
||||||
tr_name = 'trigger'
|
|
||||||
|
|
||||||
resp, body = self.client.create_cron_trigger(
|
|
||||||
tr_name, self.wf_name, None, '5 * * * *')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(tr_name, body['name'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('cron_triggers')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
trs_names = [tr['name'] for tr in body['cron_triggers']]
|
|
||||||
self.assertIn(tr_name, trs_names)
|
|
||||||
|
|
||||||
self.client.delete_obj('cron_triggers', tr_name)
|
|
||||||
self.client.triggers.remove(tr_name)
|
|
||||||
|
|
||||||
_, body = self.client.get_list_obj('cron_triggers')
|
|
||||||
|
|
||||||
trs_names = [tr['name'] for tr in body['cron_triggers']]
|
|
||||||
self.assertNotIn(tr_name, trs_names)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('b8b9102e-b323-492f-af41-4f5368971e36')
|
|
||||||
def test_create_and_delete_oneshot_cron_triggers(self):
|
|
||||||
tr_name = 'trigger'
|
|
||||||
|
|
||||||
resp, body = self.client.create_cron_trigger(
|
|
||||||
tr_name, self.wf_name, None, None, "4242-12-25 13:37")
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(tr_name, body['name'])
|
|
||||||
self.assertEqual("4242-12-25 13:37:00", body['next_execution_time'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('cron_triggers')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
trs_names = [tr['name'] for tr in body['cron_triggers']]
|
|
||||||
self.assertIn(tr_name, trs_names)
|
|
||||||
|
|
||||||
self.client.delete_obj('cron_triggers', tr_name)
|
|
||||||
self.client.triggers.remove(tr_name)
|
|
||||||
|
|
||||||
_, body = self.client.get_list_obj('cron_triggers')
|
|
||||||
|
|
||||||
trs_names = [tr['name'] for tr in body['cron_triggers']]
|
|
||||||
self.assertNotIn(tr_name, trs_names)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('5224359b-3c31-4fe7-a4eb-dc9da843137e')
|
|
||||||
def test_create_two_cron_triggers_for_one_wf(self):
|
|
||||||
tr_name_1 = 'trigger1'
|
|
||||||
tr_name_2 = 'trigger2'
|
|
||||||
|
|
||||||
resp, body = self.client.create_cron_trigger(
|
|
||||||
tr_name_1, self.wf_name, None, '5 * * * *')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(tr_name_1, body['name'])
|
|
||||||
|
|
||||||
resp, body = self.client.create_cron_trigger(
|
|
||||||
tr_name_2, self.wf_name, None, '15 * * * *')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(tr_name_2, body['name'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('cron_triggers')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
trs_names = [tr['name'] for tr in body['cron_triggers']]
|
|
||||||
self.assertIn(tr_name_1, trs_names)
|
|
||||||
self.assertIn(tr_name_2, trs_names)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('967da6e3-f9a2-430a-9390-0d73f2143aba')
|
|
||||||
def test_get_cron_trigger(self):
|
|
||||||
tr_name = 'trigger'
|
|
||||||
self.client.create_cron_trigger(
|
|
||||||
tr_name, self.wf_name, None, '5 * * * *')
|
|
||||||
|
|
||||||
resp, body = self.client.get_object('cron_triggers', tr_name)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(tr_name, body['name'])
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('d0e4d894-9a50-4919-a008-a9f255b6b6a3')
|
|
||||||
def test_create_cron_trigger_nonexistent_wf(self):
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger', 'nonexist', None, '5 * * * *')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('83f0d420-fd3c-4e75-87b1-854cefb28bda')
|
|
||||||
def test_create_cron_trigger_invalid_count(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger', 'nonexist', None, '5 * * * *', None, "q")
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('4190e0af-3c64-4f57-a0b8-d9d3d41fd323')
|
|
||||||
def test_create_cron_trigger_negative_count(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger', 'nonexist', None, '5 * * * *', None, -1)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('210c37e8-990e-4260-b3b3-93f254e6a4d7')
|
|
||||||
def test_create_cron_trigger_invalid_first_date(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger', 'nonexist', None, '5 * * * *', "q")
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('17990a39-8f66-4748-8ba5-ca87befbb198')
|
|
||||||
def test_create_cron_trigger_count_only(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger', 'nonexist', None, None, None, "42")
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('029e0a1e-2252-4a37-b9bd-cfbe407c6ade')
|
|
||||||
def test_create_cron_trigger_date_and_count_without_pattern(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger', 'nonexist', None, None,
|
|
||||||
"4242-12-25 13:37", "42")
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('54650d60-ec17-44b7-8732-2183852789ae')
|
|
||||||
def test_get_nonexistent_cron_trigger(self):
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.get_object,
|
|
||||||
'cron_triggers', 'trigger')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('c663599e-5cd7-49ff-9c0f-f82a5bcc5fdb')
|
|
||||||
def test_delete_nonexistent_trigger(self):
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'cron_triggers', 'trigger')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('d1328d2b-5dc2-4521-93ec-d734d5fb4df7')
|
|
||||||
def test_create_two_cron_triggers_with_same_name(self):
|
|
||||||
tr_name = 'trigger'
|
|
||||||
self.client.create_cron_trigger(
|
|
||||||
tr_name, self.wf_name, None, '5 * * * *')
|
|
||||||
self.assertRaises(exceptions.Conflict,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
tr_name, self.wf_name, None, '5 * * * *')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('3e51fc44-ce38-4653-9e4e-08b077a1dbc5')
|
|
||||||
def test_create_two_cron_triggers_with_same_pattern(self):
|
|
||||||
self.client.create_cron_trigger(
|
|
||||||
'trigger1',
|
|
||||||
self.wf_name,
|
|
||||||
None,
|
|
||||||
'5 * * * *',
|
|
||||||
"4242-12-25 13:37",
|
|
||||||
"42"
|
|
||||||
)
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.Conflict,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger2',
|
|
||||||
self.wf_name,
|
|
||||||
None,
|
|
||||||
'5 * * * *',
|
|
||||||
"4242-12-25 13:37",
|
|
||||||
"42"
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('6f3c08f3-9498-410e-a44b-4f9c6c971405')
|
|
||||||
def test_invalid_cron_pattern_not_enough_params(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger', self.wf_name, None, '5 *')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('26cb52e7-1ef3-45a2-a870-1baec2382c55')
|
|
||||||
def test_invalid_cron_pattern_out_of_range(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_cron_trigger,
|
|
||||||
'trigger', self.wf_name, None, '88 * * * *')
|
|
@ -1,114 +0,0 @@
|
|||||||
# Copyright 2016 Catalyst IT Limited
|
|
||||||
#
|
|
||||||
# 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 tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
|
|
||||||
EXCHANGE = 'openstack'
|
|
||||||
EVENT_ENGINE_TOPIC = 'mistral_event_engine'
|
|
||||||
EVENT = 'fake.event'
|
|
||||||
|
|
||||||
|
|
||||||
class EventTriggerTestsV2(base.TestCase):
|
|
||||||
"""Test class for event engine function.
|
|
||||||
|
|
||||||
NOTE: This test class doesn't fully test event engine functions, because
|
|
||||||
we can not send real notifications to the internal message queue to
|
|
||||||
trigger the specified workflows.
|
|
||||||
|
|
||||||
So, before notification is supported in Mistral, we can only test the API
|
|
||||||
functions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(EventTriggerTestsV2, self).setUp()
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
self.wf_id = body['workflows'][0]['id']
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
for tr in self.client.event_triggers:
|
|
||||||
self.client.delete_obj('event_triggers', tr)
|
|
||||||
self.client.event_triggers = []
|
|
||||||
|
|
||||||
for wf in self.client.workflows:
|
|
||||||
self.client.delete_obj('workflows', wf)
|
|
||||||
self.client.workflows = []
|
|
||||||
|
|
||||||
super(EventTriggerTestsV2, self).tearDown()
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('cfdf9aee-09ce-49bf-af05-97c5542bc131')
|
|
||||||
def test_create_get_delete_event_trigger(self):
|
|
||||||
name = 'my_event_trigger'
|
|
||||||
|
|
||||||
resp, body = self.client.create_event_trigger(
|
|
||||||
self.wf_id, EXCHANGE, EVENT_ENGINE_TOPIC, EVENT, name)
|
|
||||||
|
|
||||||
trigger_id = body['id']
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(name, body['name'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('event_triggers')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
trs_names = [tr['name'] for tr in body['event_triggers']]
|
|
||||||
self.assertIn(name, trs_names)
|
|
||||||
|
|
||||||
self.client.delete_obj('event_triggers', trigger_id)
|
|
||||||
self.client.event_triggers.remove(trigger_id)
|
|
||||||
|
|
||||||
_, body = self.client.get_list_obj('event_triggers')
|
|
||||||
|
|
||||||
trs_names = [tr['name'] for tr in body['event_triggers']]
|
|
||||||
self.assertNotIn(name, trs_names)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('20e547d6-9a16-4cac-9b1a-f3520c58cdd7')
|
|
||||||
def test_create_event_trigger_without_necessary_param(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_event_trigger,
|
|
||||||
self.wf_id, EXCHANGE, EVENT_ENGINE_TOPIC, '')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('ed02f500-9436-4a7b-a135-f210e1c32b22')
|
|
||||||
def test_create_event_trigger_with_nonexist_wf(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_event_trigger,
|
|
||||||
'nonexist', EXCHANGE, EVENT_ENGINE_TOPIC, EVENT)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('0ab556b6-ab76-492e-8eef-c79955003a93')
|
|
||||||
def test_create_event_trigger_duplicate(self):
|
|
||||||
name = 'my_event_trigger'
|
|
||||||
|
|
||||||
self.client.create_event_trigger(
|
|
||||||
self.wf_id, EXCHANGE, EVENT_ENGINE_TOPIC, EVENT, name)
|
|
||||||
|
|
||||||
self.assertRaises(exceptions.Conflict,
|
|
||||||
self.client.create_event_trigger,
|
|
||||||
self.wf_id, EXCHANGE, EVENT_ENGINE_TOPIC, EVENT)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('56b90a90-9ff3-42f8-a9eb-04a77198710e')
|
|
||||||
def test_get_nonexistent_event_trigger(self):
|
|
||||||
fake_id = '3771c152-d1a7-4a82-8a50-c79d122012dc'
|
|
||||||
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.get_object,
|
|
||||||
'event_triggers', fake_id)
|
|
@ -1,440 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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_concurrency.fixture import lockutils
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
from mistral_tempest_tests.tests import utils
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class ExecutionTestsV2(base.TestCase):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(ExecutionTestsV2, self).setUp()
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
|
|
||||||
self.direct_wf_name = 'wf'
|
|
||||||
self.direct_wf2_name = 'wf2'
|
|
||||||
self.sub_wf_name = 'subwf1'
|
|
||||||
self.direct_wf_id = body['workflows'][0]['id']
|
|
||||||
reverse_wfs = [wf for wf in body['workflows'] if wf['name'] == 'wf1']
|
|
||||||
self.reverse_wf = reverse_wfs[0]
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
for wf in self.client.workflows:
|
|
||||||
self.client.delete_obj('workflows', wf)
|
|
||||||
self.client.workflows = []
|
|
||||||
|
|
||||||
for ex in self.client.executions:
|
|
||||||
self.client.delete_obj('executions', ex)
|
|
||||||
self.client.executions = []
|
|
||||||
|
|
||||||
super(ExecutionTestsV2, self).tearDown()
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('c0b4b658-6f01-4680-b402-2f683b3d78b6')
|
|
||||||
def test_get_list_executions(self):
|
|
||||||
resp, body = self.client.get_list_obj('executions')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertNotIn('next', body)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('0bfcb4b0-b1e4-4499-b81b-0e86c8a2a841')
|
|
||||||
def test_get_list_executions_with_pagination(self):
|
|
||||||
resp, body = self.client.create_execution(self.direct_wf_name)
|
|
||||||
exec_id_1 = body['id']
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, body = self.client.create_execution(self.direct_wf2_name)
|
|
||||||
exec_id_2 = body['id']
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('executions')
|
|
||||||
|
|
||||||
self.assertIn(exec_id_1, [ex['id'] for ex in body['executions']])
|
|
||||||
self.assertIn(exec_id_2, [ex['id'] for ex in body['executions']])
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'executions?limit=1&sort_keys=workflow_name&sort_dirs=asc')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(1, len(body['executions']))
|
|
||||||
self.assertIn('next', body)
|
|
||||||
|
|
||||||
workflow_name_1 = body['executions'][0].get('workflow_name')
|
|
||||||
next = body.get('next')
|
|
||||||
param_dict = utils.get_dict_from_string(
|
|
||||||
next.split('?')[1],
|
|
||||||
delimiter='&'
|
|
||||||
)
|
|
||||||
|
|
||||||
# NOTE: 'id' gets included into sort keys automatically with 'desc'
|
|
||||||
# sorting to avoid pagination looping.
|
|
||||||
expected_dict = {
|
|
||||||
'limit': 1,
|
|
||||||
'sort_keys': 'workflow_name,id',
|
|
||||||
'sort_dirs': 'asc,asc',
|
|
||||||
}
|
|
||||||
|
|
||||||
self.assertTrue(
|
|
||||||
set(expected_dict.items()).issubset(set(param_dict.items()))
|
|
||||||
)
|
|
||||||
|
|
||||||
# Query again using 'next' link
|
|
||||||
url_param = next.split('/')[-1]
|
|
||||||
resp, body = self.client.get_list_obj(url_param)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(1, len(body['executions']))
|
|
||||||
|
|
||||||
workflow_name_2 = body['executions'][0].get('workflow_name')
|
|
||||||
|
|
||||||
self.assertGreater(workflow_name_2, workflow_name_1)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('5d8ebe04-8de6-414d-908f-213af59e4c6a')
|
|
||||||
def test_create_execution_for_direct_wf(self):
|
|
||||||
resp, body = self.client.create_execution(self.direct_wf_name)
|
|
||||||
exec_id = body['id']
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(self.direct_wf_name, body['workflow_name'])
|
|
||||||
self.assertEqual('RUNNING', body['state'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('executions')
|
|
||||||
self.assertIn(exec_id,
|
|
||||||
[ex_id['id'] for ex_id in body['executions']])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('101bfdff-8309-4add-9504-544b15f13d95')
|
|
||||||
def test_create_execution_for_reverse_wf(self):
|
|
||||||
resp, body = self.client.create_execution(
|
|
||||||
self.reverse_wf['name'],
|
|
||||||
wf_input={self.reverse_wf['input']: "Bye"},
|
|
||||||
params={"task_name": "goodbye"})
|
|
||||||
|
|
||||||
exec_id = body['id']
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(self.reverse_wf['name'], body['workflow_name'])
|
|
||||||
self.assertEqual('RUNNING', body['state'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('executions')
|
|
||||||
self.assertIn(exec_id,
|
|
||||||
[ex_id['id'] for ex_id in body['executions']])
|
|
||||||
|
|
||||||
resp, body = self.client.get_object('executions', exec_id)
|
|
||||||
# TODO(nmakhotkin): Fix this loop. It is infinite now.
|
|
||||||
while body['state'] != 'SUCCESS':
|
|
||||||
resp, body = self.client.get_object('executions', exec_id)
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual('SUCCESS', body['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('2df30966-9c45-4a2e-942d-e74bd92cb5aa')
|
|
||||||
def test_create_execution_by_wf_id(self):
|
|
||||||
resp, body = self.client.create_execution(self.direct_wf_id)
|
|
||||||
exec_id = body['id']
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(self.direct_wf_id, body['workflow_id'])
|
|
||||||
self.assertEqual('RUNNING', body['state'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('executions')
|
|
||||||
self.assertIn(
|
|
||||||
exec_id,
|
|
||||||
[ex_id['id'] for ex_id in body['executions']]
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('f7f50198-2dbd-4ca1-af51-d0eadc1108ac')
|
|
||||||
def test_get_execution(self):
|
|
||||||
_, execution = self.client.create_execution(self.direct_wf_name)
|
|
||||||
|
|
||||||
resp, body = self.client.get_object('executions', execution['id'])
|
|
||||||
|
|
||||||
del execution['state']
|
|
||||||
del body['state']
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(execution['id'], body['id'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('2f142ba0-6b88-4d63-8544-05c3dbfe13cc')
|
|
||||||
def test_update_execution_pause(self):
|
|
||||||
_, execution = self.client.create_execution(self.direct_wf_name)
|
|
||||||
resp, body = self.client.update_execution(
|
|
||||||
execution['id'], '{"state": "PAUSED"}')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual('PAUSED', body['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('f0557236-55ab-457d-9197-05bc2ae53e21')
|
|
||||||
def test_update_execution_description(self):
|
|
||||||
_, execution = self.client.create_execution(self.direct_wf_name)
|
|
||||||
resp, body = self.client.update_execution(
|
|
||||||
execution['id'], '{"description": "description"}')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual('description', body['description'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('c54b4d68-b179-4339-bdab-a91cd6e819b7')
|
|
||||||
def test_update_execution_fail(self):
|
|
||||||
_, execution = self.client.create_execution(self.direct_wf_name)
|
|
||||||
resp, body = self.client.update_execution(
|
|
||||||
execution['id'], '{"state": "ERROR", "state_info": "Forced"}')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual('ERROR', body['state'])
|
|
||||||
self.assertEqual('Forced', body['state_info'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('b5ce0d18-7d78-45bb-813e-ed94cea65fd0')
|
|
||||||
def test_update_execution_by_admin(self):
|
|
||||||
_, execution = self.client.create_execution(self.direct_wf_name)
|
|
||||||
resp, body = self.admin_client.update_execution(
|
|
||||||
execution['id'], '{"description": "description set by admin"}')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual('description set by admin', body['description'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_object('executions', execution['id'])
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual("description set by admin", body['description'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('c6247362-a082-49ad-a2c3-aaf12419a477')
|
|
||||||
def test_update_execution_by_other_fail(self):
|
|
||||||
_, execution = self.client.create_execution(self.direct_wf_name)
|
|
||||||
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.NotFound,
|
|
||||||
self.alt_client.update_execution,
|
|
||||||
execution['id'],
|
|
||||||
'{"description": "description set by admin"}'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('d8bde271-6785-4ace-9173-a8a3a01d5eaa')
|
|
||||||
def test_get_nonexistent_execution(self):
|
|
||||||
self.assertRaises(exceptions.NotFound, self.client.get_object,
|
|
||||||
'executions', '1a2b3c')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('e26e31ba-88cf-4b90-8b3a-fd4ecc612252')
|
|
||||||
def test_update_nonexistent_execution(self):
|
|
||||||
put_body = '{"state": "STOPPED"}'
|
|
||||||
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.update_execution,
|
|
||||||
'1a2b3c', put_body)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('b337e270-b3b6-41e2-8de2-05030b06fc37')
|
|
||||||
def test_delete_nonexistent_execution(self):
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'executions', 'nonexist')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('46f7b4b0-7d4a-4bdc-b2b6-46343cdd6f3a')
|
|
||||||
def test_create_ex_for_nonexistent_wf(self):
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.create_execution,
|
|
||||||
'nonexist')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('9d27247e-b4d4-40ab-9181-9986655a6be4')
|
|
||||||
def test_create_execution_for_reverse_wf_invalid_start_task(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.create_execution,
|
|
||||||
self.reverse_wf['name'],
|
|
||||||
{self.reverse_wf['input']: "Bye"},
|
|
||||||
{"task_name": "nonexist"}
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('0d6ac42b-4059-40ef-99d0-a65b3cd1837c')
|
|
||||||
def test_create_execution_forgot_input_params(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.create_execution,
|
|
||||||
self.reverse_wf['name'],
|
|
||||||
params={"task_name": "nonexist"}
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('52779c73-7563-47b2-8231-a24d6bf531a7')
|
|
||||||
def test_action_ex_concurrency(self):
|
|
||||||
resp, wf = self.client.create_workflow("wf_action_ex_concurrency.yaml")
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
wf_name = wf['workflows'][0]['name']
|
|
||||||
resp, execution = self.client.create_execution(wf_name)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual('RUNNING', execution['state'])
|
|
||||||
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('eb061c4d-2892-47f0-81e6-37ba15c376bb')
|
|
||||||
def test_task_ex_concurrency(self):
|
|
||||||
resp, wf = self.client.create_workflow("wf_task_ex_concurrency.yaml")
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
wf_name = wf['workflows'][0]['name']
|
|
||||||
resp, execution = self.client.create_execution(wf_name)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual('RUNNING', execution['state'])
|
|
||||||
|
|
||||||
self.client.wait_execution(execution, target_state='ERROR')
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('acc8e401-2b26-4c41-9e79-8da791da85c0')
|
|
||||||
def test_delete_execution_by_admin(self):
|
|
||||||
_, body = self.client.create_execution(self.direct_wf_id)
|
|
||||||
exec_id = body['id']
|
|
||||||
resp, _ = self.admin_client.delete_obj('executions', exec_id)
|
|
||||||
|
|
||||||
self.assertEqual(204, resp.status)
|
|
||||||
|
|
||||||
self.client.executions.remove(exec_id)
|
|
||||||
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.NotFound,
|
|
||||||
self.client.get_object,
|
|
||||||
'executions',
|
|
||||||
exec_id
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.idempotent_id('a882876b-7565-4f7f-9714-d99032ffaabb')
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
def test_workflow_execution_of_nested_workflows_within_namespace(self):
|
|
||||||
low_wf = 'for_wf_namespace/lowest_level_wf.yaml'
|
|
||||||
middle_wf = 'for_wf_namespace/middle_wf.yaml'
|
|
||||||
top_wf = 'for_wf_namespace/top_level_wf.yaml'
|
|
||||||
|
|
||||||
resp, wf = self.client.create_workflow(low_wf)
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
namespace = 'abc'
|
|
||||||
resp, wf = self.client.create_workflow(low_wf, namespace=namespace)
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, wf = self.client.create_workflow(middle_wf)
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, wf = self.client.create_workflow(top_wf)
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, wf = self.client.create_workflow(top_wf, namespace=namespace)
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
wf_name = wf['workflows'][0]['name']
|
|
||||||
resp, top_execution = self.client.create_execution(wf_name, namespace)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual('RUNNING', top_execution['state'])
|
|
||||||
self.assertEqual(wf_name, top_execution['workflow_name'])
|
|
||||||
self.assertEqual(wf_name, top_execution['workflow_name'])
|
|
||||||
self.assertEqual(namespace, top_execution['workflow_namespace'])
|
|
||||||
|
|
||||||
self.client.wait_execution(top_execution, target_state='SUCCESS')
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
namespace,
|
|
||||||
json.loads(top_execution['params'])['namespace']
|
|
||||||
)
|
|
||||||
|
|
||||||
resp, tasks = self.client.get_tasks(top_execution['id'])
|
|
||||||
top_task = tasks['tasks'][0]
|
|
||||||
|
|
||||||
self.assertEqual(wf_name, top_task['workflow_name'])
|
|
||||||
self.assertEqual(namespace, top_task['workflow_namespace'])
|
|
||||||
|
|
||||||
resp, executions = self.client.get_executions(top_task['id'])
|
|
||||||
middle_execution = executions['executions'][0]
|
|
||||||
|
|
||||||
self.assertEqual('middle_wf', middle_execution['workflow_name'])
|
|
||||||
self.assertEqual('', middle_execution['workflow_namespace'])
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
namespace,
|
|
||||||
json.loads(middle_execution['params'])['namespace']
|
|
||||||
)
|
|
||||||
|
|
||||||
resp, tasks = self.client.get_tasks(middle_execution['id'])
|
|
||||||
middle_task = tasks['tasks'][0]
|
|
||||||
|
|
||||||
self.assertEqual('middle_wf', middle_task['workflow_name'])
|
|
||||||
self.assertEqual('', middle_task['workflow_namespace'])
|
|
||||||
|
|
||||||
resp, executions = self.client.get_executions(middle_task['id'])
|
|
||||||
lowest_execution = executions['executions'][0]
|
|
||||||
|
|
||||||
self.assertEqual('lowest_level_wf', lowest_execution['workflow_name'])
|
|
||||||
self.assertEqual(namespace, lowest_execution['workflow_namespace'])
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
namespace,
|
|
||||||
json.loads(lowest_execution['params'])['namespace']
|
|
||||||
)
|
|
||||||
|
|
||||||
resp, tasks = self.client.get_tasks(lowest_execution['id'])
|
|
||||||
lowest_task = tasks['tasks'][0]
|
|
||||||
|
|
||||||
self.assertEqual('lowest_level_wf', lowest_task['workflow_name'])
|
|
||||||
self.assertEqual(namespace, lowest_task['workflow_namespace'])
|
|
||||||
|
|
||||||
resp, action_executions = self.client.get_action_executions(
|
|
||||||
lowest_task['id']
|
|
||||||
)
|
|
||||||
|
|
||||||
action_execution = action_executions['action_executions'][0]
|
|
||||||
|
|
||||||
self.assertEqual('lowest_level_wf', action_execution['workflow_name'])
|
|
||||||
self.assertEqual(namespace, action_execution['workflow_namespace'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('2baa25c5-0b65-4fbe-8d90-2c6599831b6b')
|
|
||||||
def test_root_execution_id(self):
|
|
||||||
resp, execution = self.client.create_execution(self.sub_wf_name)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual('RUNNING', execution['state'])
|
|
||||||
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'executions?root_execution_id={}'.format(execution['id']))
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertNotEmpty(body['executions'])
|
|
||||||
|
|
||||||
self.assertEqual(2, len(body['executions']))
|
|
||||||
|
|
||||||
for exc in body['executions']:
|
|
||||||
self.assertEqual(exc['root_execution_id'], execution['id'])
|
|
@ -1,108 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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_concurrency.fixture import lockutils
|
|
||||||
from tempest.lib import decorators
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TasksTestsV2(base.TestCase):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TasksTestsV2, self).setUp()
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
self.direct_wf_name = body['workflows'][0]['name']
|
|
||||||
_, execution = self.client.create_execution(self.direct_wf_name)
|
|
||||||
self.execution_id = execution['id']
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
for wf in self.client.workflows:
|
|
||||||
self.client.delete_obj('workflows', wf)
|
|
||||||
self.client.workflows = []
|
|
||||||
|
|
||||||
for wf in self.client.executions:
|
|
||||||
self.client.delete_obj('executions', wf)
|
|
||||||
self.client.executions = []
|
|
||||||
|
|
||||||
super(TasksTestsV2, self).tearDown()
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('81159dce-3802-44ee-a8d4-5ddca106fd91')
|
|
||||||
def test_get_tasks_list(self):
|
|
||||||
resp, body = self.client.get_list_obj('tasks')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertNotEmpty(body['tasks'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('f62664de-bd2b-4153-8d0f-5a76d78abbad')
|
|
||||||
def test_get_task(self):
|
|
||||||
resp, body = self.client.get_list_obj('tasks')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(
|
|
||||||
self.direct_wf_name, body['tasks'][-1]['workflow_name']
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('3230d694-40fd-4094-ad12-024f40a21b94')
|
|
||||||
def test_get_tasks_of_execution(self):
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'tasks?workflow_execution_id=%s' % self.execution_id)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(
|
|
||||||
self.direct_wf_name, body['tasks'][-1]['workflow_name']
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TaskTypesTestsV2(base.TestCase):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TaskTypesTestsV2, self).setUp()
|
|
||||||
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
|
|
||||||
_, wb_body = self.client.create_workbook('wb_with_nested_wf.yaml')
|
|
||||||
self.nested_wf_name = 'wb_with_nested_wf.wrapping_wf'
|
|
||||||
_, execution = self.client.create_execution(self.nested_wf_name)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('1ac726eb-b945-4b82-8755-a2fb2dc009bc')
|
|
||||||
def test_task_type(self):
|
|
||||||
resp, body = self.client.get_list_obj('tasks')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
bt = body['tasks']
|
|
||||||
ll = [[v for k, v in d.items() if 'type' in k] for d in bt]
|
|
||||||
types_list = [item for sublist in ll for item in sublist]
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
'WORKFLOW', types_list
|
|
||||||
)
|
|
||||||
self.assertIn(
|
|
||||||
'ACTION', types_list
|
|
||||||
)
|
|
||||||
|
|
||||||
# there are 2 tasks in the workflow one of each type
|
|
||||||
self.assertEqual(
|
|
||||||
2, len(types_list)
|
|
||||||
)
|
|
@ -1,138 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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_concurrency.fixture import lockutils
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class WorkbookTestsV2(base.TestCase):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
for wf in self.client.workflows:
|
|
||||||
self.client.delete_obj('workflows', wf)
|
|
||||||
self.client.workflows = []
|
|
||||||
|
|
||||||
super(WorkbookTestsV2, self).tearDown()
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('4d8752b9-8d69-4d81-8710-5dd8ef699b95')
|
|
||||||
def test_get_list_workbooks(self):
|
|
||||||
resp, body = self.client.get_list_obj('workbooks')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEmpty(body['workbooks'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('1a078ca2-bcf9-4eb9-8ed5-e3545038aa76')
|
|
||||||
def test_create_and_delete_workbook(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
resp, body = self.client.create_workbook('wb_v2.yaml')
|
|
||||||
name = body['name']
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('workbooks')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(name, body['workbooks'][0]['name'])
|
|
||||||
|
|
||||||
self.client.delete_obj('workbooks', name)
|
|
||||||
self.client.workbooks.remove(name)
|
|
||||||
|
|
||||||
_, body = self.client.get_list_obj('workbooks')
|
|
||||||
self.assertEmpty(body['workbooks'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('80f7d7a6-2821-4ab0-b090-ca45c98258ba')
|
|
||||||
def test_get_workbook(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workbook('wb_v2.yaml')
|
|
||||||
name = body['name']
|
|
||||||
|
|
||||||
resp, body = self.client.get_object('workbooks', name)
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(name, body['name'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('4d3b1e43-a493-41be-9c8a-389511675403')
|
|
||||||
def test_update_workbook(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workbook('wb_v2.yaml')
|
|
||||||
name = body['name']
|
|
||||||
resp, body = self.client.update_request('workbooks', 'wb_v2.yaml')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(name, body['name'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('506cdcc2-082f-4e1f-9ab2-717acd7f0eb5')
|
|
||||||
def test_get_workbook_definition(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workbook('wb_v2.yaml')
|
|
||||||
name = body['name']
|
|
||||||
resp, body = self.client.get_definition('workbooks', name)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertIsNotNone(body)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('d99f11c1-05a3-4d90-89c6-8d85558d3708')
|
|
||||||
def test_get_nonexistent_workbook_definition(self):
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.get_definition,
|
|
||||||
'workbooks', 'nonexist')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('61ed021e-ec56-42cb-ad05-eb6979aa00fd')
|
|
||||||
def test_get_nonexistent_workbook(self):
|
|
||||||
self.assertRaises(exceptions.NotFound, self.client.get_object,
|
|
||||||
'workbooks', 'nonexist')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('e3d76f8b-220d-4250-8238-0ba27fda6de9')
|
|
||||||
def test_double_create_workbook(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workbook('wb_v2.yaml')
|
|
||||||
name = body['name']
|
|
||||||
self.assertRaises(exceptions.Conflict,
|
|
||||||
self.client.create_workbook,
|
|
||||||
'wb_v2.yaml')
|
|
||||||
|
|
||||||
self.client.delete_obj('workbooks', name)
|
|
||||||
self.client.workbooks.remove(name)
|
|
||||||
_, body = self.client.get_list_obj('workbooks')
|
|
||||||
|
|
||||||
self.assertEmpty(body['workbooks'])
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('1cd6f6f7-b166-454e-96d2-bf1f95c23015')
|
|
||||||
def test_create_wb_with_invalid_def(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.create_workbook,
|
|
||||||
'wb_v1.yaml'
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('ac9a05d3-e285-4d88-91eb-fb9ad694a89a')
|
|
||||||
def test_update_wb_with_invalid_def(self):
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.update_request,
|
|
||||||
'workbooks',
|
|
||||||
'wb_v1.yaml'
|
|
||||||
)
|
|
@ -1,442 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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
|
|
||||||
|
|
||||||
from oslo_concurrency.fixture import lockutils
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
from mistral_tempest_tests.tests import utils
|
|
||||||
|
|
||||||
|
|
||||||
class WorkflowTestsV2(base.TestCase):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
for wf in self.client.workflows:
|
|
||||||
self.client.delete_obj('workflows', wf)
|
|
||||||
self.client.workflows = []
|
|
||||||
|
|
||||||
super(WorkflowTestsV2, self).tearDown()
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('e9cd6817-e8d1-4604-ba76-b0e17219f4c5')
|
|
||||||
def test_get_list_workflows(self):
|
|
||||||
resp, body = self.client.get_list_obj('workflows')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
names = [wf['name'] for wf in body['workflows']]
|
|
||||||
|
|
||||||
self.assertIn('std.create_instance', names)
|
|
||||||
|
|
||||||
self.assertNotIn('next', body)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('be8a4a44-eeb3-48e3-b11d-b83ba14dbf2c')
|
|
||||||
def test_get_list_workflows_by_admin(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
|
|
||||||
resp, raw_body = self.admin_client.get('workflows?all_projects=true')
|
|
||||||
body = json.loads(raw_body)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
names = [wf['name'] for wf in body['workflows']]
|
|
||||||
|
|
||||||
self.assertIn(name, names)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('c9e2ebbc-02aa-4c33-b244-e471c8266aa7')
|
|
||||||
def test_get_list_workflows_with_project_by_admin(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
|
|
||||||
resp, raw_body = self.admin_client.get(
|
|
||||||
'workflows?project_id=%s' %
|
|
||||||
self.client.auth_provider.credentials.tenant_id
|
|
||||||
)
|
|
||||||
body = json.loads(raw_body)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
names = [wf['name'] for wf in body['workflows']]
|
|
||||||
|
|
||||||
self.assertIn(name, names)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('b8dc1b02-8509-45e2-9df7-4630cdcfa1ab')
|
|
||||||
def test_get_list_other_project_private_workflows(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
|
|
||||||
resp, raw_body = self.alt_client.get(
|
|
||||||
'workflows?project_id=%s' %
|
|
||||||
self.client.auth_provider.credentials.tenant_id
|
|
||||||
)
|
|
||||||
body = json.loads(raw_body)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
names = [wf['name'] for wf in body['workflows']]
|
|
||||||
|
|
||||||
self.assertNotIn(name, names)
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('2063143b-ced8-4037-9383-e2504be581e6')
|
|
||||||
def test_get_list_workflows_with_fields(self):
|
|
||||||
resp, body = self.client.get_list_obj('workflows?fields=name')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
for wf in body['workflows']:
|
|
||||||
self.assertListEqual(sorted(['id', 'name']), sorted(list(wf)))
|
|
||||||
|
|
||||||
@decorators.attr(type='smoke')
|
|
||||||
@decorators.idempotent_id('81f28735-e74e-4dc1-8b94-b548f8a80556')
|
|
||||||
def test_get_list_workflows_with_pagination(self):
|
|
||||||
resp, body = self.client.get_list_obj(
|
|
||||||
'workflows?limit=1&sort_keys=name&sort_dirs=desc'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(1, len(body['workflows']))
|
|
||||||
self.assertIn('next', body)
|
|
||||||
|
|
||||||
name_1 = body['workflows'][0].get('name')
|
|
||||||
next = body.get('next')
|
|
||||||
|
|
||||||
param_dict = utils.get_dict_from_string(
|
|
||||||
next.split('?')[1],
|
|
||||||
delimiter='&'
|
|
||||||
)
|
|
||||||
|
|
||||||
# NOTE: 'id' gets included into sort keys automatically with 'desc'
|
|
||||||
# sorting to avoid pagination looping.
|
|
||||||
expected_sub_dict = {
|
|
||||||
'limit': 1,
|
|
||||||
'sort_keys': 'name,id',
|
|
||||||
'sort_dirs': 'desc,asc'
|
|
||||||
}
|
|
||||||
|
|
||||||
self.assertDictContainsSubset(expected_sub_dict, param_dict)
|
|
||||||
|
|
||||||
# Query again using 'next' hint
|
|
||||||
url_param = next.split('/')[-1]
|
|
||||||
resp, body = self.client.get_list_obj(url_param)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(1, len(body['workflows']))
|
|
||||||
|
|
||||||
name_2 = body['workflows'][0].get('name')
|
|
||||||
|
|
||||||
self.assertGreater(name_1, name_2)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('cdb5586f-a72f-4371-88d1-1472675915c3')
|
|
||||||
def test_get_list_workflows_nonexist_sort_dirs(self):
|
|
||||||
context = self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'workflows?limit=1&sort_keys=id&sort_dirs=nonexist'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
'Unknown sort direction',
|
|
||||||
context.resp_body.get('faultstring')
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('ac41a0ea-2be6-4307-9003-6b8dd52b0bf9')
|
|
||||||
def test_get_list_workflows_invalid_limit(self):
|
|
||||||
context = self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'workflows?limit=-1&sort_keys=id&sort_dirs=asc'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
'Limit must be positive',
|
|
||||||
context.resp_body.get('faultstring')
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('55759713-a8d7-44c2-aff1-2383f51136bd')
|
|
||||||
def test_get_list_workflows_duplicate_sort_keys(self):
|
|
||||||
context = self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.get_list_obj,
|
|
||||||
'workflows?limit=1&sort_keys=id,id&sort_dirs=asc,asc'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
'Length of sort_keys must be equal or greater than sort_dirs',
|
|
||||||
context.resp_body.get('faultstring')
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('e26b30b9-6699-4020-93a0-e25c2daca59a')
|
|
||||||
def test_create_and_delete_workflow(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
resp, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
resp, body = self.client.get_list_obj('workflows')
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
names = [wf['name'] for wf in body['workflows']]
|
|
||||||
self.assertIn(name, names)
|
|
||||||
|
|
||||||
self.client.delete_obj('workflows', name)
|
|
||||||
self.client.workflows.remove(name)
|
|
||||||
|
|
||||||
_, body = self.client.get_list_obj('workflows')
|
|
||||||
|
|
||||||
names = [wf['name'] for wf in body['workflows']]
|
|
||||||
self.assertNotIn(name, names)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('f5a4a771-79b2-4f28-bfac-940aa83990a4')
|
|
||||||
def test_get_workflow(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
|
|
||||||
resp, body = self.client.get_object('workflows', name)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(name, body['name'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('f516aad0-9a50-4ace-a217-fa1931fd9335')
|
|
||||||
def test_update_workflow(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workflow('single_wf.yaml')
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
|
|
||||||
resp, body = self.client.update_request('workflows', 'single_wf.yaml')
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(name, body['workflows'][0]['name'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('42f5d135-a2b8-4a31-8135-c5ce8c5f1ed5')
|
|
||||||
def test_workflow_within_namespace(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
|
|
||||||
namespace = 'abc'
|
|
||||||
resp, body = self.client.create_workflow(
|
|
||||||
'single_wf.yaml',
|
|
||||||
namespace=namespace
|
|
||||||
)
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
id = body['workflows'][0]['id']
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(name, body['workflows'][0]['name'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_workflow(
|
|
||||||
id
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(namespace, body['namespace'])
|
|
||||||
|
|
||||||
resp, body = self.client.update_workflow('single_wf.yaml', namespace)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertEqual(name, body['workflows'][0]['name'])
|
|
||||||
self.assertEqual(namespace, body['workflows'][0]['namespace'])
|
|
||||||
|
|
||||||
namespace = 'abc2'
|
|
||||||
resp, body = self.client.create_workflow(
|
|
||||||
'single_wf.yaml',
|
|
||||||
namespace=namespace
|
|
||||||
)
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
id = body['workflows'][0]['id']
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
self.assertEqual(name, body['workflows'][0]['name'])
|
|
||||||
|
|
||||||
resp, body = self.client.get_workflow(id)
|
|
||||||
|
|
||||||
self.assertEqual(namespace, body['namespace'])
|
|
||||||
|
|
||||||
self.assertRaises(exceptions.NotFound, self.client.get_workflow, name)
|
|
||||||
|
|
||||||
self.client.create_workflow(
|
|
||||||
'single_wf.yaml'
|
|
||||||
)
|
|
||||||
|
|
||||||
resp, body = self.client.get_workflow(id)
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('02bc1fc3-c31a-4e37-bb3d-eda46818505c')
|
|
||||||
def test_get_workflow_definition(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
|
|
||||||
resp, body = self.client.get_definition('workflows', name)
|
|
||||||
|
|
||||||
self.assertEqual(200, resp.status)
|
|
||||||
self.assertIsNotNone(body)
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('04fbd003-0e52-4034-858e-6634d4f84b29')
|
|
||||||
def test_get_workflow_uploaded_in_wb(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workbook('wb_v2.yaml')
|
|
||||||
wb_name = body['name']
|
|
||||||
|
|
||||||
_, body = self.client.get_list_obj('workflows')
|
|
||||||
wf_names = [wf['name'] for wf in body['workflows']
|
|
||||||
if wf['name'].startswith(wb_name)]
|
|
||||||
|
|
||||||
self.assertNotEmpty(wf_names)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('5e5f0403-fb2c-41ae-bf6f-25c181515358')
|
|
||||||
def test_get_nonexistent_workflow_definition(self):
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.get_definition,
|
|
||||||
'workflows', 'nonexist')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('23c72d01-c3bb-43d6-ba15-9b49c15f800c')
|
|
||||||
def test_get_nonexistent_workflow(self):
|
|
||||||
self.assertRaises(exceptions.NotFound, self.client.get_object,
|
|
||||||
'workflows', 'nonexist')
|
|
||||||
|
|
||||||
exception = self.assertRaises(
|
|
||||||
exceptions.NotFound,
|
|
||||||
self.client.get_workflow,
|
|
||||||
'nonexist_wf',
|
|
||||||
'nonexist_namespace'
|
|
||||||
)
|
|
||||||
self.assertIn('nonexist_wf', str(exception))
|
|
||||||
self.assertIn('nonexist_namespace', str(exception))
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('6b917213-7f11-423a-8fe0-55795dcf0fb2')
|
|
||||||
def test_double_create_workflows(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
self.assertRaises(exceptions.Conflict,
|
|
||||||
self.client.create_workflow,
|
|
||||||
'wf_v2.yaml')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('ffcd63d2-1104-4320-a67b-fadc4e2a0631')
|
|
||||||
def test_create_wf_with_invalid_def(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.create_workflow,
|
|
||||||
'wb_v1.yaml')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('eed46931-5485-436c-810f-1f63362223b9')
|
|
||||||
def test_update_wf_with_invalid_def(self):
|
|
||||||
self.assertRaises(exceptions.BadRequest,
|
|
||||||
self.client.update_request,
|
|
||||||
'workflows', 'wb_v1.yaml')
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('9b7f5b5a-cacd-4f98-a35a-decf065b8234')
|
|
||||||
def test_delete_wf_with_trigger_associate(self):
|
|
||||||
tr_name = 'trigger'
|
|
||||||
resp, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
resp, body = self.client.create_cron_trigger(
|
|
||||||
tr_name, name, None, '5 * * * *')
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'workflows',
|
|
||||||
name
|
|
||||||
)
|
|
||||||
finally:
|
|
||||||
self.client.delete_obj('cron_triggers', tr_name)
|
|
||||||
self.client.triggers.remove(tr_name)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('46325022-cbd2-48f3-95f3-e587aab3b655')
|
|
||||||
def test_delete_wf_with_event_trigger_associate(self):
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml')
|
|
||||||
wf_id = body['workflows'][0]['id']
|
|
||||||
resp, body = self.client.create_event_trigger(
|
|
||||||
wf_id, 'openstack', 'notification', 'fake.event')
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'workflows',
|
|
||||||
wf_id
|
|
||||||
)
|
|
||||||
finally:
|
|
||||||
self.client.delete_obj('event_triggers', body['id'])
|
|
||||||
self.client.event_triggers.remove(body['id'])
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('1cb929e6-d375-4dcb-ab7c-73aa205af896')
|
|
||||||
def test_delete_wf_with_trigger_associate_in_other_tenant(self):
|
|
||||||
self.useFixture(lockutils.LockFixture('mistral-workflow'))
|
|
||||||
tr_name = 'trigger'
|
|
||||||
_, body = self.client.create_workflow('wf_v2.yaml', scope='public')
|
|
||||||
name = body['workflows'][0]['name']
|
|
||||||
resp, body = self.alt_client.create_cron_trigger(
|
|
||||||
tr_name,
|
|
||||||
name,
|
|
||||||
None,
|
|
||||||
'5 * * * *'
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
exception = self.assertRaises(
|
|
||||||
exceptions.BadRequest,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'workflows',
|
|
||||||
name
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
"Can't delete workflow that has cron triggers associated",
|
|
||||||
exception.resp_body['faultstring']
|
|
||||||
)
|
|
||||||
finally:
|
|
||||||
self.alt_client.delete_obj('cron_triggers', tr_name)
|
|
||||||
self.alt_client.triggers.remove(tr_name)
|
|
||||||
|
|
||||||
@decorators.attr(type='negative')
|
|
||||||
@decorators.idempotent_id('f575713b-27fd-4ec8-b84f-468a7adf5ed2')
|
|
||||||
def test_delete_nonexistent_wf(self):
|
|
||||||
self.assertRaises(exceptions.NotFound,
|
|
||||||
self.client.delete_obj,
|
|
||||||
'workflows', 'nonexist')
|
|
@ -1,91 +0,0 @@
|
|||||||
# Copyright 2016 NEC Corporation. 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 os
|
|
||||||
|
|
||||||
import mock
|
|
||||||
from tempest import config
|
|
||||||
from tempest import test as test
|
|
||||||
|
|
||||||
from mistral_tempest_tests.services import base as service_base
|
|
||||||
from mistral_tempest_tests.services.v2 import mistral_client
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class TestCase(test.BaseTestCase):
|
|
||||||
credentials = ['admin', 'primary', 'alt']
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def skip_checks(cls):
|
|
||||||
super(TestCase, cls).skip_checks()
|
|
||||||
|
|
||||||
if not CONF.service_available.mistral:
|
|
||||||
raise cls.skipException("Mistral support is required.")
|
|
||||||
|
|
||||||
@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 = service_base.AuthProv()
|
|
||||||
cls.admin_mgr = cls.alt_mgr = cls.mgr
|
|
||||||
else:
|
|
||||||
cls.admin_mgr = cls.os_admin
|
|
||||||
cls.mgr = cls.os_primary
|
|
||||||
cls.alt_mgr = cls.os_alt
|
|
||||||
|
|
||||||
if cls._service == 'workflowv2':
|
|
||||||
cls.admin_client = mistral_client.MistralClientV2(
|
|
||||||
cls.admin_mgr.auth_provider, cls._service)
|
|
||||||
cls.client = mistral_client.MistralClientV2(
|
|
||||||
cls.mgr.auth_provider, cls._service)
|
|
||||||
cls.alt_client = mistral_client.MistralClientV2(
|
|
||||||
cls.alt_mgr.auth_provider, cls._service)
|
|
||||||
|
|
||||||
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.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()
|
|
@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
version: "2.0"
|
|
||||||
|
|
||||||
greeting:
|
|
||||||
description: "This action says 'Hello'"
|
|
||||||
tags: [hello]
|
|
||||||
base: std.echo
|
|
||||||
base-input:
|
|
||||||
output: 'Hello, <% $.name %>'
|
|
||||||
input:
|
|
||||||
- name
|
|
||||||
output:
|
|
||||||
string: <% $ %>
|
|
||||||
|
|
||||||
farewell:
|
|
||||||
base: std.echo
|
|
||||||
base-input:
|
|
||||||
output: 'Bye!'
|
|
||||||
output:
|
|
||||||
info: <% $ %>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
lowest_level_wf:
|
|
||||||
tasks:
|
|
||||||
noop_task:
|
|
||||||
action: std.noop
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
middle_wf:
|
|
||||||
tasks:
|
|
||||||
run_workflow_with_name_lowest_level_wf:
|
|
||||||
workflow: lowest_level_wf
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
top_level_wf:
|
|
||||||
tasks:
|
|
||||||
run_workflow_with_name_middle_wf:
|
|
||||||
workflow: middle_wf
|
|
@ -1,75 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
name: action_collection
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
keystone:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
projects_list:
|
|
||||||
action: keystone.projects_list
|
|
||||||
publish:
|
|
||||||
result: <% task().result %>
|
|
||||||
|
|
||||||
nova:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
flavors_list:
|
|
||||||
action: nova.flavors_list
|
|
||||||
publish:
|
|
||||||
result: <% task().result %>
|
|
||||||
|
|
||||||
glance:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
images_list:
|
|
||||||
action: glance.images_list
|
|
||||||
publish:
|
|
||||||
result: <% task().result %>
|
|
||||||
|
|
||||||
heat:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
stacks_list:
|
|
||||||
action: heat.stacks_list
|
|
||||||
publish:
|
|
||||||
result: <% task().result %>
|
|
||||||
|
|
||||||
neutron:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
list_subnets:
|
|
||||||
action: neutron.list_subnets
|
|
||||||
publish:
|
|
||||||
result: <% task().result %>
|
|
||||||
|
|
||||||
cinder:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
volumes_list:
|
|
||||||
action: cinder.volumes_list
|
|
||||||
publish:
|
|
||||||
result: <% task().result %>
|
|
||||||
|
|
||||||
swift:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
get_account:
|
|
||||||
action: swift.get_account
|
|
||||||
publish:
|
|
||||||
result: <% task().result %>
|
|
||||||
|
|
||||||
zaqar:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
send_message:
|
|
||||||
action: zaqar.queue_post
|
|
||||||
input:
|
|
||||||
queue_name: test
|
|
||||||
messages:
|
|
||||||
body:
|
|
||||||
type: action_collection.zaqar
|
|
||||||
payload:
|
|
||||||
status: <% $.get(status, 'SUCCESS') %>
|
|
||||||
publish:
|
|
||||||
result: <% task().result %>
|
|
@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
|
|
||||||
single_wf:
|
|
||||||
type: direct
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
hello:
|
|
||||||
action: std.echo output="Hello"
|
|
||||||
publish:
|
|
||||||
result: <% task(hello).result %>
|
|
@ -1,12 +0,0 @@
|
|||||||
Namespaces:
|
|
||||||
Greetings:
|
|
||||||
actions:
|
|
||||||
hello:
|
|
||||||
class: std.echo
|
|
||||||
base-parameters:
|
|
||||||
output: Hello!
|
|
||||||
|
|
||||||
Workflow:
|
|
||||||
tasks:
|
|
||||||
hello:
|
|
||||||
action: Greetings.hello
|
|
@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
name: test
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
test:
|
|
||||||
type: direct
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
hello:
|
|
||||||
action: std.echo output="Hello"
|
|
||||||
publish:
|
|
||||||
result: <% task(hello).result %>
|
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
version: "2.0"
|
|
||||||
|
|
||||||
name: wb_with_nested_wf
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
|
|
||||||
wrapping_wf:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
call_inner_wf:
|
|
||||||
workflow: inner_wf
|
|
||||||
|
|
||||||
inner_wf:
|
|
||||||
type: direct
|
|
||||||
tasks:
|
|
||||||
hello:
|
|
||||||
action: std.echo output="Hello from inner workflow"
|
|
@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
|
|
||||||
test_action_ex_concurrency:
|
|
||||||
tasks:
|
|
||||||
test_with_items:
|
|
||||||
with-items: index in <% range(2) %>
|
|
||||||
action: std.echo output='<% $.index %>'
|
|
@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
|
|
||||||
test_task_ex_concurrency:
|
|
||||||
tasks:
|
|
||||||
task1:
|
|
||||||
action: std.async_noop
|
|
||||||
timeout: 2
|
|
||||||
task2:
|
|
||||||
action: std.async_noop
|
|
||||||
timeout: 2
|
|
@ -1,55 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2.0'
|
|
||||||
|
|
||||||
wf:
|
|
||||||
type: direct
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
hello:
|
|
||||||
action: std.echo output="Hello"
|
|
||||||
wait-before: 1
|
|
||||||
publish:
|
|
||||||
result: <% task(hello).result %>
|
|
||||||
|
|
||||||
wf1:
|
|
||||||
type: reverse
|
|
||||||
input:
|
|
||||||
- farewell
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
addressee:
|
|
||||||
action: std.echo output="John"
|
|
||||||
publish:
|
|
||||||
name: <% task(addressee).result %>
|
|
||||||
|
|
||||||
goodbye:
|
|
||||||
action: std.echo output="<% $.farewell %>, <% $.name %>"
|
|
||||||
requires: [addressee]
|
|
||||||
|
|
||||||
wf2:
|
|
||||||
type: direct
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
hello:
|
|
||||||
action: std.echo output="Doe"
|
|
||||||
|
|
||||||
subwf1:
|
|
||||||
type: direct
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
task1:
|
|
||||||
workflow: subwf2
|
|
||||||
|
|
||||||
subwf2:
|
|
||||||
type: direct
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
task1:
|
|
||||||
workflow: subwf3
|
|
||||||
|
|
||||||
subwf3:
|
|
||||||
type: direct
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
task1:
|
|
||||||
action: std.noop
|
|
@ -1,145 +0,0 @@
|
|||||||
# Copyright 2016 - Nokia, Inc.
|
|
||||||
#
|
|
||||||
# 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 base64
|
|
||||||
|
|
||||||
from keystoneclient import service_catalog as ks_service_catalog
|
|
||||||
from oslo_serialization import jsonutils
|
|
||||||
from oslo_utils import uuidutils
|
|
||||||
from six.moves.urllib.parse import urlparse
|
|
||||||
from tempest.lib import decorators
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class MultiVimActionsTests(base.TestCase):
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def resource_setup(cls):
|
|
||||||
super(MultiVimActionsTests, cls).resource_setup()
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('dadc9960-9c03-41a9-9a9d-7e97d527e6dd')
|
|
||||||
def test_multi_vim_support_target_headers(self):
|
|
||||||
client_1 = self.alt_client
|
|
||||||
client_2 = self.client
|
|
||||||
|
|
||||||
# Create stack with client2.
|
|
||||||
result = _execute_action(client_2, _get_create_stack_request())
|
|
||||||
stack_id = str(
|
|
||||||
jsonutils.loads(result['output'])['result']['stack']['id']
|
|
||||||
)
|
|
||||||
|
|
||||||
# List stacks with client1, and assert that there is no stack.
|
|
||||||
result = _execute_action(client_1, _get_list_stack_request())
|
|
||||||
self.assertEmpty(jsonutils.loads(result['output'])['result'])
|
|
||||||
|
|
||||||
# List stacks with client1, but with the target headers of client2,
|
|
||||||
# and assert the created stack is there.
|
|
||||||
result = _execute_action(
|
|
||||||
client_1,
|
|
||||||
_get_list_stack_request(),
|
|
||||||
extra_headers=_extract_target_headers_from_client(client_2)
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
stack_id,
|
|
||||||
str(jsonutils.loads(result['output'])['result'][0]['id'])
|
|
||||||
)
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('bc0e9b99-62b0-4d96-95c9-016a3f69b02a')
|
|
||||||
@decorators.skip_because(bug="1736685")
|
|
||||||
def test_multi_vim_support_target_headers_and_service_catalog(self):
|
|
||||||
client_1 = self.alt_client
|
|
||||||
client_2 = self.client
|
|
||||||
|
|
||||||
# List stacks with client1, but with the target headers of client2,
|
|
||||||
# and additionally with an invalid X-Target-Service-Catalog.
|
|
||||||
extra_headers = _extract_target_headers_from_client(client_2)
|
|
||||||
|
|
||||||
# Use ServiceCatalog to eliminate differences between keystone v2 and
|
|
||||||
# v3.
|
|
||||||
token_data = client_2.auth_provider.auth_data[1]
|
|
||||||
service_catalog = ks_service_catalog.ServiceCatalog.factory(
|
|
||||||
token_data
|
|
||||||
).get_data()
|
|
||||||
|
|
||||||
for service in service_catalog:
|
|
||||||
if service['name'] == 'heat':
|
|
||||||
for ep in service['endpoints']:
|
|
||||||
if 'publicURL' in ep:
|
|
||||||
ep['publicURL'] = "invalid"
|
|
||||||
elif ep['interface'] == 'public':
|
|
||||||
ep['url'] = "invalid"
|
|
||||||
break
|
|
||||||
break
|
|
||||||
|
|
||||||
if 'catalog' in token_data:
|
|
||||||
token_data['catalog'] = service_catalog
|
|
||||||
else:
|
|
||||||
token_data['serviceCatalog'] = service_catalog
|
|
||||||
|
|
||||||
invalid_service_catalog = {
|
|
||||||
"X-Target-Service-Catalog": base64.b64encode(
|
|
||||||
jsonutils.dumps(token_data)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
extra_headers.update(invalid_service_catalog)
|
|
||||||
result = _execute_action(
|
|
||||||
client_1,
|
|
||||||
_get_list_stack_request(),
|
|
||||||
extra_headers=extra_headers
|
|
||||||
)
|
|
||||||
|
|
||||||
# Assert that the invalid catalog was used.
|
|
||||||
self.assertIn("Invalid URL", result['output'])
|
|
||||||
|
|
||||||
|
|
||||||
def _extract_target_headers_from_client(client):
|
|
||||||
u = urlparse(client.auth_provider.auth_url)
|
|
||||||
v3_auth_url = '{}://{}/identity/v3/'.format(u.scheme, u.netloc)
|
|
||||||
return {
|
|
||||||
'X-Target-Auth-Token': client.token,
|
|
||||||
'X-Target-Auth-Uri': v3_auth_url,
|
|
||||||
'X-Target-Project-Id': client.tenant_id,
|
|
||||||
'X-Target-User-Id': client.user_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _execute_action(client, request, extra_headers={}):
|
|
||||||
_, result = client.create_action_execution(
|
|
||||||
request,
|
|
||||||
extra_headers=extra_headers
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _get_create_stack_request():
|
|
||||||
stack_name = 'multi_vim_test_stack_{}'.format(
|
|
||||||
uuidutils.generate_uuid()[:8])
|
|
||||||
|
|
||||||
return {
|
|
||||||
'name': 'heat.stacks_create',
|
|
||||||
'input': {
|
|
||||||
'stack_name': stack_name,
|
|
||||||
"template": {"heat_template_version": "2013-05-23"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _get_list_stack_request():
|
|
||||||
return {
|
|
||||||
'name': 'heat.stacks_list',
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
# Copyright 2015 - Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 tempest.lib import decorators
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class OpenStackActionsTestsV2(base.TestCase):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
# TODO(akuznetsova): add checks for task result after task_output
|
|
||||||
# TODO(akuznetsova): refactoring will be finished
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def resource_setup(cls):
|
|
||||||
super(OpenStackActionsTestsV2, cls).resource_setup()
|
|
||||||
|
|
||||||
_, cls.wb = cls.client.create_workbook(
|
|
||||||
'openstack/action_collection_wb.yaml')
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('9a999fc2-a089-4375-bc69-e1ed85b17a82')
|
|
||||||
def test_nova_actions(self):
|
|
||||||
wf_name = self.wb['name'] + '.nova'
|
|
||||||
_, execution = self.client.create_execution(wf_name)
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
executed_task = self.client.get_wf_tasks(wf_name)[-1]
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', executed_task['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('81bdc1c9-cd9a-4c97-b8ce-e44f5211eace')
|
|
||||||
def test_keystone_actions(self):
|
|
||||||
wf_name = self.wb['name'] + '.keystone'
|
|
||||||
_, execution = self.admin_client.create_execution(wf_name)
|
|
||||||
self.admin_client.wait_execution_success(execution)
|
|
||||||
executed_task = self.admin_client.get_wf_tasks(wf_name)[-1]
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', executed_task['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('fde681b8-3e1b-4172-a4b8-2fcac1f070d9')
|
|
||||||
def test_heat_actions(self):
|
|
||||||
wf_name = self.wb['name'] + '.heat'
|
|
||||||
_, execution = self.client.create_execution(wf_name)
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
executed_task = self.client.get_wf_tasks(wf_name)[-1]
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', executed_task['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('5981360d-f336-45ca-9d38-799c7a8ade26')
|
|
||||||
def test_glance_actions(self):
|
|
||||||
wf_name = self.wb['name'] + '.glance'
|
|
||||||
_, execution = self.client.create_execution(wf_name)
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
executed_task = self.client.get_wf_tasks(wf_name)[-1]
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', executed_task['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('a1f71a72-3681-4d32-aad9-117068717b33')
|
|
||||||
def test_cinder_actions(self):
|
|
||||||
wf_name = self.wb['name'] + '.cinder'
|
|
||||||
_, execution = self.client.create_execution(wf_name)
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
executed_task = self.client.get_wf_tasks(wf_name)[-1]
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', executed_task['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('586dd973-fc65-40e2-9a85-31418b22473a')
|
|
||||||
def test_neutron_actions(self):
|
|
||||||
wf_name = self.wb['name'] + '.neutron'
|
|
||||||
_, execution = self.client.create_execution(wf_name)
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
executed_task = self.client.get_wf_tasks(wf_name)[-1]
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', executed_task['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('985c1051-cf2e-4fd0-8ceb-a9b8110597a1')
|
|
||||||
def test_swift_actions(self):
|
|
||||||
wf_name = self.wb['name'] + '.swift'
|
|
||||||
_, execution = self.client.create_execution(wf_name)
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
executed_task = self.client.get_wf_tasks(wf_name)[-1]
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', executed_task['state'])
|
|
||||||
|
|
||||||
@decorators.attr(type='openstack')
|
|
||||||
@decorators.idempotent_id('6f000ae8-3f41-49bd-b7a7-ad2256d80076')
|
|
||||||
def test_zaqar_actions(self):
|
|
||||||
wf_name = self.wb['name'] + '.zaqar'
|
|
||||||
_, execution = self.client.create_execution(wf_name)
|
|
||||||
self.client.wait_execution_success(execution)
|
|
||||||
executed_task = self.client.get_wf_tasks(wf_name)[-1]
|
|
||||||
|
|
||||||
self.assertEqual('SUCCESS', executed_task['state'])
|
|
@ -1,295 +0,0 @@
|
|||||||
# Copyright 2015 - Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
from os import path
|
|
||||||
import time
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from paramiko import ssh_exception
|
|
||||||
from tempest import config
|
|
||||||
from tempest.lib import decorators
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
from mistral_tempest_tests.tests import base
|
|
||||||
from mistral_tempest_tests.tests import ssh_utils
|
|
||||||
from mistral_tempest_tests.tests import utils
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
CONF = config.CONF
|
|
||||||
SSH_KEYS_DIRECTORY = path.expanduser("~/.ssh/")
|
|
||||||
|
|
||||||
|
|
||||||
class SSHActionsTestsV2(base.TestCaseAdvanced):
|
|
||||||
|
|
||||||
_service = 'workflowv2'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _create_security_group_rule_ssh(cls):
|
|
||||||
sec_groups = (
|
|
||||||
cls.mgr.compute_security_groups_client.
|
|
||||||
list_security_groups()
|
|
||||||
)
|
|
||||||
sec_groups = sec_groups['security_groups']
|
|
||||||
|
|
||||||
default_group = next(
|
|
||||||
g for g in sec_groups if g['name'] == 'default'
|
|
||||||
)
|
|
||||||
|
|
||||||
rule = (
|
|
||||||
cls.mgr.compute_security_group_rules_client
|
|
||||||
.create_security_group_rule(
|
|
||||||
parent_group_id=default_group['id'],
|
|
||||||
ip_protocol="tcp",
|
|
||||||
from_port=22,
|
|
||||||
to_port=22,
|
|
||||||
cidr="0.0.0.0/0"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
cls.ssh_rule_id = rule['security_group_rule']['id']
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _create_server(cls, server_name, **kwargs):
|
|
||||||
return cls.mgr.servers_client.create_server(
|
|
||||||
name=server_name,
|
|
||||||
imageRef=CONF.compute.image_ref,
|
|
||||||
flavorRef=CONF.compute.flavor_ref,
|
|
||||||
**kwargs
|
|
||||||
).get('server')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _associate_floating_ip_to_server(cls, server_id):
|
|
||||||
fl_ip_client = cls.mgr.compute_floating_ips_client
|
|
||||||
|
|
||||||
all_ips = fl_ip_client.list_floating_ips().get(
|
|
||||||
'floating_ips'
|
|
||||||
)
|
|
||||||
free_ips = list(
|
|
||||||
[fl_ip for fl_ip in all_ips if fl_ip['instance_id'] is None]
|
|
||||||
)
|
|
||||||
|
|
||||||
if free_ips:
|
|
||||||
ip = free_ips[0]['ip']
|
|
||||||
else:
|
|
||||||
# Allocate new floating ip.
|
|
||||||
ip = fl_ip_client.create_floating_ip()['floating_ip']['ip']
|
|
||||||
|
|
||||||
# Associate IP.
|
|
||||||
fl_ip_client.associate_floating_ip_to_server(
|
|
||||||
floating_ip=ip,
|
|
||||||
server_id=server_id
|
|
||||||
)
|
|
||||||
|
|
||||||
return ip
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _wait_until_server_up(cls, server_ip, timeout=120, delay=2):
|
|
||||||
seconds_remain = timeout
|
|
||||||
|
|
||||||
LOG.info("Waiting server SSH [IP=%s]...", server_ip)
|
|
||||||
|
|
||||||
while seconds_remain > 0:
|
|
||||||
try:
|
|
||||||
ssh_utils.execute_command('cd', server_ip, None)
|
|
||||||
except ssh_exception.SSHException:
|
|
||||||
LOG.info("Server %s: SSH service is ready.")
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
LOG.info(str(e))
|
|
||||||
seconds_remain -= delay
|
|
||||||
time.sleep(delay)
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
raise Exception(
|
|
||||||
"Failed waiting until server's '%s' SSH is up." % server_ip
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _wait_until_server_active(cls, server_id, timeout=60, delay=2):
|
|
||||||
seconds_remain = timeout
|
|
||||||
|
|
||||||
LOG.info("Waiting server [id=%s]...", server_id)
|
|
||||||
|
|
||||||
while seconds_remain > 0:
|
|
||||||
server_info = cls.mgr.servers_client.show_server(server_id)
|
|
||||||
if server_info['server']['status'] == 'ACTIVE':
|
|
||||||
return
|
|
||||||
|
|
||||||
seconds_remain -= delay
|
|
||||||
time.sleep(delay)
|
|
||||||
|
|
||||||
raise Exception(
|
|
||||||
"Failed waiting until server %s is active." % server_id
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _wait_until_server_delete(cls, server_id, timeout=60, delay=2):
|
|
||||||
seconds_remain = timeout
|
|
||||||
|
|
||||||
LOG.info("Deleting server [id=%s]...", server_id)
|
|
||||||
|
|
||||||
while seconds_remain > 0:
|
|
||||||
try:
|
|
||||||
cls.mgr.servers_client.show_server(server_id)
|
|
||||||
seconds_remain -= delay
|
|
||||||
time.sleep(delay)
|
|
||||||
except exceptions.NotFound:
|
|
||||||
return
|
|
||||||
|
|
||||||
raise RuntimeError("Server delete timeout!")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def resource_setup(cls):
|
|
||||||
super(SSHActionsTestsV2, cls).resource_setup()
|
|
||||||
|
|
||||||
# Modify security group for accessing VM via SSH.
|
|
||||||
cls._create_security_group_rule_ssh()
|
|
||||||
|
|
||||||
# Create keypair (public and private keys).
|
|
||||||
cls.private_key, cls.public_key = utils.generate_key_pair()
|
|
||||||
cls.key_name = 'mistral-functional-tests-key'
|
|
||||||
|
|
||||||
# If ZUUL_PROJECT is specified, it means
|
|
||||||
# tests are running on Jenkins gate.
|
|
||||||
|
|
||||||
if os.environ.get('ZUUL_PROJECT'):
|
|
||||||
cls.key_dir = "/opt/stack/new/.ssh/"
|
|
||||||
|
|
||||||
if not path.exists(cls.key_dir):
|
|
||||||
os.mkdir(cls.key_dir)
|
|
||||||
else:
|
|
||||||
cls.key_dir = SSH_KEYS_DIRECTORY
|
|
||||||
|
|
||||||
utils.save_text_to(
|
|
||||||
cls.private_key,
|
|
||||||
cls.key_dir + cls.key_name,
|
|
||||||
overwrite=True
|
|
||||||
)
|
|
||||||
|
|
||||||
LOG.info("Private key saved to %s", cls.key_dir + cls.key_name)
|
|
||||||
|
|
||||||
# Create keypair in nova.
|
|
||||||
cls.mgr.keypairs_client.create_keypair(
|
|
||||||
name=cls.key_name,
|
|
||||||
public_key=cls.public_key
|
|
||||||
)
|
|
||||||
|
|
||||||
# Start servers and provide key_name.
|
|
||||||
# Note: start public vm only after starting the guest one,
|
|
||||||
# so we can track public vm launching using ssh, but can't
|
|
||||||
# do the same with guest VM.
|
|
||||||
cls.guest_vm = cls._create_server(
|
|
||||||
'mistral-guest-vm',
|
|
||||||
key_name=cls.key_name
|
|
||||||
)
|
|
||||||
cls.public_vm = cls._create_server(
|
|
||||||
'mistral-public-vm',
|
|
||||||
key_name=cls.key_name
|
|
||||||
)
|
|
||||||
|
|
||||||
cls._wait_until_server_active(cls.public_vm['id'])
|
|
||||||
|
|
||||||
cls.public_vm_ip = cls._associate_floating_ip_to_server(
|
|
||||||
cls.public_vm['id']
|
|
||||||
)
|
|
||||||
|
|
||||||
# Wait until server is up.
|
|
||||||
cls._wait_until_server_up(cls.public_vm_ip)
|
|
||||||
|
|
||||||
# Update servers info.
|
|
||||||
cls.public_vm = cls.mgr.servers_client.show_server(
|
|
||||||
cls.public_vm['id']
|
|
||||||
).get('server')
|
|
||||||
|
|
||||||
cls.guest_vm = cls.mgr.servers_client.show_server(
|
|
||||||
cls.guest_vm['id']
|
|
||||||
).get('server')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def resource_cleanup(cls):
|
|
||||||
fl_ip_client = cls.mgr.compute_floating_ips_client
|
|
||||||
fl_ip_client.disassociate_floating_ip_from_server(
|
|
||||||
cls.public_vm_ip,
|
|
||||||
cls.public_vm['id']
|
|
||||||
)
|
|
||||||
|
|
||||||
cls.mgr.servers_client.delete_server(cls.public_vm['id'])
|
|
||||||
cls.mgr.servers_client.delete_server(cls.guest_vm['id'])
|
|
||||||
|
|
||||||
cls._wait_until_server_delete(cls.public_vm['id'])
|
|
||||||
cls._wait_until_server_delete(cls.guest_vm['id'])
|
|
||||||
|
|
||||||
cls.mgr.keypairs_client.delete_keypair(cls.key_name)
|
|
||||||
|
|
||||||
cls.mgr.compute_security_group_rules_client.delete_security_group_rule(
|
|
||||||
cls.ssh_rule_id
|
|
||||||
)
|
|
||||||
os.remove(cls.key_dir + cls.key_name)
|
|
||||||
|
|
||||||
super(SSHActionsTestsV2, cls).resource_cleanup()
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('3e12a2ad-5b10-46b0-ae1f-ed34d3cc6ae2')
|
|
||||||
def test_run_ssh_action(self):
|
|
||||||
input_data = {
|
|
||||||
'cmd': 'hostname',
|
|
||||||
'host': self.public_vm_ip,
|
|
||||||
'username': CONF.validation.image_ssh_user,
|
|
||||||
'private_key_filename': self.key_name
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, body = self.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.ssh',
|
|
||||||
'input': json.dumps(input_data)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
output = json.loads(body['output'])
|
|
||||||
|
|
||||||
self.assertIn(self.public_vm['name'], output['result'])
|
|
||||||
|
|
||||||
@decorators.attr(type='sanity')
|
|
||||||
@decorators.idempotent_id('6c09fb04-70b4-43a6-b5f8-a53ca92e66e0')
|
|
||||||
@decorators.skip_because(bug="1736685")
|
|
||||||
def test_run_ssh_proxied_action(self):
|
|
||||||
guest_vm_ip = self.guest_vm['addresses'].popitem()[1][0]['addr']
|
|
||||||
|
|
||||||
input_data = {
|
|
||||||
'cmd': 'hostname',
|
|
||||||
'host': guest_vm_ip,
|
|
||||||
'username': CONF.validation.image_ssh_user,
|
|
||||||
'private_key_filename': self.key_name,
|
|
||||||
'gateway_host': self.public_vm_ip,
|
|
||||||
'gateway_username': CONF.validation.image_ssh_user
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, body = self.client.create_action_execution(
|
|
||||||
{
|
|
||||||
'name': 'std.ssh_proxied',
|
|
||||||
'input': json.dumps(input_data)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(201, resp.status)
|
|
||||||
|
|
||||||
output = json.loads(body['output'])
|
|
||||||
|
|
||||||
self.assertIn(self.guest_vm['name'], output['result'])
|
|
@ -1,103 +0,0 @@
|
|||||||
# Copyright 2014 - Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os import path
|
|
||||||
from oslo_log import log as logging
|
|
||||||
import paramiko
|
|
||||||
import six
|
|
||||||
|
|
||||||
KEY_PATH = path.expanduser("~/.ssh/")
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def _read_paramimko_stream(recv_func):
|
|
||||||
result = ''
|
|
||||||
buf = recv_func(1024)
|
|
||||||
while buf != '':
|
|
||||||
result += buf
|
|
||||||
buf = recv_func(1024)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _to_paramiko_private_key(private_key_filename, password=None):
|
|
||||||
if '../' in private_key_filename or '..\\' in private_key_filename:
|
|
||||||
raise OSError(
|
|
||||||
"Private key filename must not contain '..'. "
|
|
||||||
"Actual: %s" % private_key_filename
|
|
||||||
)
|
|
||||||
|
|
||||||
private_key_path = KEY_PATH + private_key_filename
|
|
||||||
|
|
||||||
return paramiko.RSAKey(
|
|
||||||
filename=private_key_path,
|
|
||||||
password=password
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _connect(host, username, password=None, pkey=None, proxy=None):
|
|
||||||
if isinstance(pkey, six.string_types):
|
|
||||||
pkey = _to_paramiko_private_key(pkey, password)
|
|
||||||
|
|
||||||
LOG.debug('Creating SSH connection to %s', host)
|
|
||||||
|
|
||||||
ssh_client = paramiko.SSHClient()
|
|
||||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
||||||
|
|
||||||
ssh_client.connect(
|
|
||||||
host,
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
pkey=pkey,
|
|
||||||
sock=proxy
|
|
||||||
)
|
|
||||||
|
|
||||||
return ssh_client
|
|
||||||
|
|
||||||
|
|
||||||
def _cleanup(ssh_client):
|
|
||||||
ssh_client.close()
|
|
||||||
|
|
||||||
|
|
||||||
def _execute_command(ssh_client, cmd, get_stderr=False,
|
|
||||||
raise_when_error=True):
|
|
||||||
try:
|
|
||||||
chan = ssh_client.get_transport().open_session()
|
|
||||||
chan.exec_command(cmd)
|
|
||||||
|
|
||||||
# TODO(nmakhotkin): that could hang if stderr buffer overflows
|
|
||||||
stdout = _read_paramimko_stream(chan.recv)
|
|
||||||
stderr = _read_paramimko_stream(chan.recv_stderr)
|
|
||||||
|
|
||||||
ret_code = chan.recv_exit_status()
|
|
||||||
|
|
||||||
if ret_code and raise_when_error:
|
|
||||||
raise RuntimeError("Cmd: %s\nReturn code: %s\nstdout: %s"
|
|
||||||
% (cmd, ret_code, stdout))
|
|
||||||
if get_stderr:
|
|
||||||
return ret_code, stdout, stderr
|
|
||||||
else:
|
|
||||||
return ret_code, stdout
|
|
||||||
finally:
|
|
||||||
_cleanup(ssh_client)
|
|
||||||
|
|
||||||
|
|
||||||
def execute_command(cmd, host, username, password=None,
|
|
||||||
private_key_filename=None, get_stderr=False,
|
|
||||||
raise_when_error=True):
|
|
||||||
ssh_client = _connect(host, username, password, private_key_filename)
|
|
||||||
|
|
||||||
LOG.debug("Executing command %s", cmd)
|
|
||||||
|
|
||||||
return _execute_command(ssh_client, cmd, get_stderr, raise_when_error)
|
|
@ -1,152 +0,0 @@
|
|||||||
# Copyright 2013 - Mirantis, Inc.
|
|
||||||
# Copyright 2015 - Huawei Technologies Co. Ltd
|
|
||||||
# Copyright 2016 - Brocade Communications Systems, Inc.
|
|
||||||
#
|
|
||||||
# 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 contextlib
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from oslo_concurrency import processutils
|
|
||||||
|
|
||||||
|
|
||||||
class NotDefined(object):
|
|
||||||
"""Marker of an empty value.
|
|
||||||
|
|
||||||
In a number of cases None can't be used to express the semantics of
|
|
||||||
a not defined value because None is just a normal value rather than
|
|
||||||
a value set to denote that it's not defined. This class can be used
|
|
||||||
in such cases instead of None.
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_dict_from_string(string, delimiter=','):
|
|
||||||
if not string:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
kv_dicts = []
|
|
||||||
|
|
||||||
for kv_pair_str in string.split(delimiter):
|
|
||||||
kv_str = kv_pair_str.strip()
|
|
||||||
kv_list = kv_str.split('=')
|
|
||||||
|
|
||||||
if len(kv_list) > 1:
|
|
||||||
try:
|
|
||||||
value = json.loads(kv_list[1])
|
|
||||||
except ValueError:
|
|
||||||
value = kv_list[1]
|
|
||||||
|
|
||||||
kv_dicts += [{kv_list[0]: value}]
|
|
||||||
else:
|
|
||||||
kv_dicts += [kv_list[0]]
|
|
||||||
|
|
||||||
return get_dict_from_entries(kv_dicts)
|
|
||||||
|
|
||||||
|
|
||||||
def get_dict_from_entries(entries):
|
|
||||||
"""Transforms a list of entries into dictionary.
|
|
||||||
|
|
||||||
:param entries: A list of entries.
|
|
||||||
If an entry is a dictionary the method simply updates the result
|
|
||||||
dictionary with its content.
|
|
||||||
If an entry is not a dict adds {entry, NotDefined} into the result.
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
for e in entries:
|
|
||||||
if isinstance(e, dict):
|
|
||||||
result.update(e)
|
|
||||||
else:
|
|
||||||
# NOTE(kong): we put NotDefined here as the value of
|
|
||||||
# param without value specified, to distinguish from
|
|
||||||
# the valid values such as None, ''(empty string), etc.
|
|
||||||
result[e] = NotDefined
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def tempdir(**kwargs):
|
|
||||||
argdict = kwargs.copy()
|
|
||||||
|
|
||||||
if 'dir' not in argdict:
|
|
||||||
argdict['dir'] = '/tmp/'
|
|
||||||
|
|
||||||
tmpdir = tempfile.mkdtemp(**argdict)
|
|
||||||
|
|
||||||
try:
|
|
||||||
yield tmpdir
|
|
||||||
finally:
|
|
||||||
try:
|
|
||||||
shutil.rmtree(tmpdir)
|
|
||||||
except OSError as e:
|
|
||||||
raise OSError(
|
|
||||||
"Failed to delete temp dir %(dir)s (reason: %(reason)s)" %
|
|
||||||
{'dir': tmpdir, 'reason': e}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def save_text_to(text, file_path, overwrite=False):
|
|
||||||
if os.path.exists(file_path) and not overwrite:
|
|
||||||
raise OSError(
|
|
||||||
"Cannot save data to file. File %s already exists."
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
f.write(text)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_key_pair(key_length=2048):
|
|
||||||
"""Create RSA key pair with specified number of bits in key.
|
|
||||||
|
|
||||||
Returns tuple of private and public keys.
|
|
||||||
"""
|
|
||||||
with tempdir() as tmpdir:
|
|
||||||
keyfile = os.path.join(tmpdir, 'tempkey')
|
|
||||||
args = [
|
|
||||||
'ssh-keygen',
|
|
||||||
'-q', # quiet
|
|
||||||
'-N', '', # w/o passphrase
|
|
||||||
'-t', 'rsa', # create key of rsa type
|
|
||||||
'-f', keyfile, # filename of the key file
|
|
||||||
'-C', 'Generated-by-Mistral' # key comment
|
|
||||||
]
|
|
||||||
|
|
||||||
if key_length is not None:
|
|
||||||
args.extend(['-b', key_length])
|
|
||||||
|
|
||||||
processutils.execute(*args)
|
|
||||||
|
|
||||||
if not os.path.exists(keyfile):
|
|
||||||
# raise exc.DataAccessException(
|
|
||||||
# "Private key file hasn't been created"
|
|
||||||
# )
|
|
||||||
raise OSError("Private key file hasn't been created")
|
|
||||||
|
|
||||||
private_key = open(keyfile).read()
|
|
||||||
public_key_path = keyfile + '.pub'
|
|
||||||
|
|
||||||
if not os.path.exists(public_key_path):
|
|
||||||
# raise exc.DataAccessException(
|
|
||||||
# "Public key file hasn't been created"
|
|
||||||
# )
|
|
||||||
raise OSError("Private key file hasn't been created")
|
|
||||||
public_key = open(public_key_path).read()
|
|
||||||
|
|
||||||
return private_key, public_key
|
|
@ -23,6 +23,18 @@
|
|||||||
chdir: '{{ ansible_user_dir }}/workspace'
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
environment: '{{ zuul | zuul_legacy_vars }}'
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
cat << 'EOF' >>"/tmp/dg-local.conf"
|
||||||
|
[[local|localrc]]
|
||||||
|
TEMPEST_PLUGINS='/opt/stack/new/mistral-tempest-plugin'
|
||||||
|
EOF
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
- shell:
|
- shell:
|
||||||
cmd: |
|
cmd: |
|
||||||
set -e
|
set -e
|
||||||
@ -38,11 +50,13 @@
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
export ENABLED_SERVICES=heat,h-api,h-api-cfn,h-api-cw,h-eng,tempest
|
export ENABLED_SERVICES=heat,h-api,h-api-cfn,h-api-cw,h-eng,tempest
|
||||||
|
export DEVSTACK_GATE_TEMPEST=1
|
||||||
export PROJECTS="openstack/zaqar $PROJECTS"
|
export PROJECTS="openstack/zaqar $PROJECTS"
|
||||||
export PROJECTS="openstack/python-zaqarclient $PROJECTS"
|
export PROJECTS="openstack/python-zaqarclient $PROJECTS"
|
||||||
export PROJECTS="openstack/heat $PROJECTS"
|
export PROJECTS="openstack/heat $PROJECTS"
|
||||||
export PROJECTS="openstack/mistral $PROJECTS"
|
export PROJECTS="openstack/mistral $PROJECTS"
|
||||||
export PROJECTS="openstack/mistral-dashboard $PROJECTS"
|
export PROJECTS="openstack/mistral-dashboard $PROJECTS"
|
||||||
|
export PROJECTS="openstack/mistral-tempest-plugin $PROJECTS"
|
||||||
export DEVSTACK_LOCAL_CONFIG="enable_plugin mistral https://git.openstack.org/openstack/mistral"
|
export DEVSTACK_LOCAL_CONFIG="enable_plugin mistral https://git.openstack.org/openstack/mistral"
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin heat git://git.openstack.org/openstack/heat"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin heat git://git.openstack.org/openstack/heat"
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin zaqar git://git.openstack.org/openstack/zaqar"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin zaqar git://git.openstack.org/openstack/zaqar"
|
||||||
|
@ -23,6 +23,18 @@
|
|||||||
chdir: '{{ ansible_user_dir }}/workspace'
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
environment: '{{ zuul | zuul_legacy_vars }}'
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
cat << 'EOF' >>"/tmp/dg-local.conf"
|
||||||
|
[[local|localrc]]
|
||||||
|
TEMPEST_PLUGINS='/opt/stack/new/mistral-tempest-plugin'
|
||||||
|
EOF
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
- shell:
|
- shell:
|
||||||
cmd: |
|
cmd: |
|
||||||
set -e
|
set -e
|
||||||
@ -38,11 +50,13 @@
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
export ENABLED_SERVICES=heat,h-api,h-api-cfn,h-api-cw,h-eng,tempest
|
export ENABLED_SERVICES=heat,h-api,h-api-cfn,h-api-cw,h-eng,tempest
|
||||||
|
export DEVSTACK_GATE_TEMPEST=1
|
||||||
export PROJECTS="openstack/zaqar $PROJECTS"
|
export PROJECTS="openstack/zaqar $PROJECTS"
|
||||||
export PROJECTS="openstack/python-zaqarclient $PROJECTS"
|
export PROJECTS="openstack/python-zaqarclient $PROJECTS"
|
||||||
export PROJECTS="openstack/heat $PROJECTS"
|
export PROJECTS="openstack/heat $PROJECTS"
|
||||||
export PROJECTS="openstack/mistral $PROJECTS"
|
export PROJECTS="openstack/mistral $PROJECTS"
|
||||||
export PROJECTS="openstack/mistral-dashboard $PROJECTS"
|
export PROJECTS="openstack/mistral-dashboard $PROJECTS"
|
||||||
|
export PROJECTS="openstack/mistral-tempest-plugin $PROJECTS"
|
||||||
export DEVSTACK_LOCAL_CONFIG="enable_plugin mistral https://git.openstack.org/openstack/mistral"
|
export DEVSTACK_LOCAL_CONFIG="enable_plugin mistral https://git.openstack.org/openstack/mistral"
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin heat git://git.openstack.org/openstack/heat"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin heat git://git.openstack.org/openstack/heat"
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin zaqar git://git.openstack.org/openstack/zaqar"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin zaqar git://git.openstack.org/openstack/zaqar"
|
||||||
|
@ -23,6 +23,18 @@
|
|||||||
chdir: '{{ ansible_user_dir }}/workspace'
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
environment: '{{ zuul | zuul_legacy_vars }}'
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
cat << 'EOF' >>"/tmp/dg-local.conf"
|
||||||
|
[[local|localrc]]
|
||||||
|
TEMPEST_PLUGINS='/opt/stack/new/mistral-tempest-plugin'
|
||||||
|
EOF
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
- shell:
|
- shell:
|
||||||
cmd: |
|
cmd: |
|
||||||
set -e
|
set -e
|
||||||
@ -38,11 +50,13 @@
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
export ENABLED_SERVICES=heat,h-api,h-api-cfn,h-api-cw,h-eng,tempest
|
export ENABLED_SERVICES=heat,h-api,h-api-cfn,h-api-cw,h-eng,tempest
|
||||||
|
export DEVSTACK_GATE_TEMPEST=1
|
||||||
export PROJECTS="openstack/zaqar $PROJECTS"
|
export PROJECTS="openstack/zaqar $PROJECTS"
|
||||||
export PROJECTS="openstack/python-zaqarclient $PROJECTS"
|
export PROJECTS="openstack/python-zaqarclient $PROJECTS"
|
||||||
export PROJECTS="openstack/heat $PROJECTS"
|
export PROJECTS="openstack/heat $PROJECTS"
|
||||||
export PROJECTS="openstack/mistral $PROJECTS"
|
export PROJECTS="openstack/mistral $PROJECTS"
|
||||||
export PROJECTS="openstack/mistral-dashboard $PROJECTS"
|
export PROJECTS="openstack/mistral-dashboard $PROJECTS"
|
||||||
|
export PROJECTS="openstack/mistral-tempest-plugin $PROJECTS"
|
||||||
export DEVSTACK_LOCAL_CONFIG="enable_plugin mistral https://git.openstack.org/openstack/mistral"
|
export DEVSTACK_LOCAL_CONFIG="enable_plugin mistral https://git.openstack.org/openstack/mistral"
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin heat git://git.openstack.org/openstack/heat"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin heat git://git.openstack.org/openstack/heat"
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin zaqar git://git.openstack.org/openstack/zaqar"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin zaqar git://git.openstack.org/openstack/zaqar"
|
||||||
|
@ -56,9 +56,6 @@ oslo.config.opts.defaults =
|
|||||||
oslo.policy.policies =
|
oslo.policy.policies =
|
||||||
mistral = mistral.policies:list_rules
|
mistral = mistral.policies:list_rules
|
||||||
|
|
||||||
tempest.test_plugins =
|
|
||||||
mistral_test = mistral_tempest_tests.plugin:MistralTempestPlugin
|
|
||||||
|
|
||||||
mistral.actions =
|
mistral.actions =
|
||||||
std.async_noop = mistral.actions.std_actions:AsyncNoOpAction
|
std.async_noop = mistral.actions.std_actions:AsyncNoOpAction
|
||||||
std.noop = mistral.actions.std_actions:NoOpAction
|
std.noop = mistral.actions.std_actions:NoOpAction
|
||||||
|
1
tox.ini
1
tox.ini
@ -37,7 +37,6 @@ basepython = python2.7
|
|||||||
commands =
|
commands =
|
||||||
doc8 doc/source
|
doc8 doc/source
|
||||||
flake8 {posargs} . {toxinidir}/tools/get_action_list.py {toxinidir}/tools/sync_db.py
|
flake8 {posargs} . {toxinidir}/tools/get_action_list.py {toxinidir}/tools/sync_db.py
|
||||||
check-uuid --package mistral_tempest_tests
|
|
||||||
|
|
||||||
[testenv:cover]
|
[testenv:cover]
|
||||||
# Also do not run test_coverage_ext tests while gathering coverage as those
|
# Also do not run test_coverage_ext tests while gathering coverage as those
|
||||||
|
Loading…
Reference in New Issue
Block a user