Merge "Add option to create mistral execution from workbook"

This commit is contained in:
Jenkins 2016-10-17 17:41:56 +00:00 committed by Gerrit Code Review
commit d599de9a26
24 changed files with 800 additions and 16 deletions

View File

@ -268,6 +268,9 @@
# (floating point value) # (floating point value)
#manila_share_delete_poll_interval = 2.0 #manila_share_delete_poll_interval = 2.0
# mistral execution timeout (integer value)
#mistral_execution_timeout = 200
# Delay between creating Monasca metrics and polling for its elements. # Delay between creating Monasca metrics and polling for its elements.
# (floating point value) # (floating point value)
#monasca_metric_create_prepoll_delay = 15.0 #monasca_metric_create_prepoll_delay = 15.0

View File

@ -44,3 +44,35 @@
sla: sla:
failure_rate: failure_rate:
max: 0 max: 0
MistralExecutions.list_executions:
-
runner:
type: "constant"
times: 50
concurrency: 10
context:
users:
tenants: 2
users_per_tenant: 2
sla:
failure_rate:
max: 0
MistralExecutions.create_execution_from_workbook:
-
args:
definition: "~/.rally/extra/mistral_wb.yaml"
workflow_name: "wf1"
do_delete: true
runner:
type: "constant"
times: 50
concurrency: 10
context:
users:
tenants: 2
users_per_tenant: 2
sla:
failure_rate:
max: 0

View File

@ -25,6 +25,7 @@ from rally.plugins.openstack.scenarios.heat import utils as heat_utils
from rally.plugins.openstack.scenarios.ironic import utils as ironic_utils from rally.plugins.openstack.scenarios.ironic import utils as ironic_utils
from rally.plugins.openstack.scenarios.magnum import utils as magnum_utils from rally.plugins.openstack.scenarios.magnum import utils as magnum_utils
from rally.plugins.openstack.scenarios.manila import utils as manila_utils from rally.plugins.openstack.scenarios.manila import utils as manila_utils
from rally.plugins.openstack.scenarios.mistral import utils as mistral_utils
from rally.plugins.openstack.scenarios.monasca import utils as monasca_utils from rally.plugins.openstack.scenarios.monasca import utils as monasca_utils
from rally.plugins.openstack.scenarios.murano import utils as murano_utils from rally.plugins.openstack.scenarios.murano import utils as murano_utils
from rally.plugins.openstack.scenarios.nova import utils as nova_utils from rally.plugins.openstack.scenarios.nova import utils as nova_utils
@ -48,6 +49,7 @@ def list_opts():
ironic_utils.IRONIC_BENCHMARK_OPTS, ironic_utils.IRONIC_BENCHMARK_OPTS,
magnum_utils.MAGNUM_BENCHMARK_OPTS, magnum_utils.MAGNUM_BENCHMARK_OPTS,
manila_utils.MANILA_BENCHMARK_OPTS, manila_utils.MANILA_BENCHMARK_OPTS,
mistral_utils.MISTRAL_BENCHMARK_OPTS,
monasca_utils.MONASCA_BENCHMARK_OPTS, monasca_utils.MONASCA_BENCHMARK_OPTS,
murano_utils.MURANO_BENCHMARK_OPTS, murano_utils.MURANO_BENCHMARK_OPTS,
nova_utils.NOVA_BENCHMARK_OPTS, nova_utils.NOVA_BENCHMARK_OPTS,

View File

@ -736,10 +736,32 @@ class SwiftContainer(SwiftMixin):
# MISTRAL # MISTRAL
@base.resource("mistral", "workbooks", order=1100, tenant_resource=True) _mistral_order = get_order(1100)
class MistralWorkbooks(SynchronizedDeletion, base.ResourceManager):
class MistralMixin(SynchronizedDeletion, base.ResourceManager):
def delete(self): def delete(self):
self._manager().delete(self.raw_resource.name) self._manager().delete(self.raw_resource["id"])
@base.resource("mistral", "workbooks", order=next(_mistral_order),
tenant_resource=True)
class MistralWorkbooks(MistralMixin):
def delete(self):
self._manager().delete(self.raw_resource["name"])
@base.resource("mistral", "workflows", order=next(_mistral_order),
tenant_resource=True)
class MistralWorkflows(MistralMixin):
pass
@base.resource("mistral", "executions", order=next(_mistral_order),
tenant_resource=True)
class MistralExecutions(MistralMixin):
pass
# MURANO # MURANO

View File

@ -0,0 +1,92 @@
# 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 six
import yaml
from rally import consts
from rally.plugins.openstack import scenario
from rally.plugins.openstack.scenarios.mistral import utils
from rally.task import types
from rally.task import validation
"""Scenarios for Mistral execution."""
@validation.required_clients("mistral")
@validation.required_openstack(users=True)
@validation.required_services(consts.Service.MISTRAL)
@scenario.configure(name="MistralExecutions.list_executions",
context={"cleanup": ["mistral"]})
class ListExecutions(utils.MistralScenario):
def run(self, marker="", limit=None, sort_keys="", sort_dirs=""):
"""Scenario test mistral execution-list command.
This simple scenario tests the Mistral execution-list
command by listing all the executions.
:param marker: The last execution uuid of the previous page, displays
list of executions after "marker".
:param limit: number Maximum number of executions to return in a single
result.
:param sort_keys: id,description
:param sort_dirs: [SORT_DIRS] Comma-separated list of sort directions.
Default: asc.
"""
self._list_executions(marker=marker, limit=limit,
sort_keys=sort_keys, sort_dirs=sort_dirs)
@validation.required_parameters("definition")
@validation.file_exists("definition")
@types.convert(definition={"type": "file"})
@validation.required_clients("mistral")
@validation.required_openstack(users=True)
@validation.required_services(consts.Service.MISTRAL)
@validation.workbook_contains_workflow("definition", "workflow_name")
@scenario.configure(
name="MistralExecutions.create_execution_from_workbook",
context={"cleanup": ["mistral"]})
class CreateExecutionFromWorkbook(utils.MistralScenario):
def run(self, definition, workflow_name=None, do_delete=False):
"""Scenario tests execution creation and deletion.
This scenario is a very useful tool to measure the
"mistral execution-create" and "mistral execution-delete"
commands performance.
:param definition: string (yaml string) representation of given file
content (Mistral workbook definition)
:param workflow_name: string the workflow name to execute. Should be
one of the to workflows in the definition. If no
workflow_name is passed, one of the workflows in
the definition will be taken.
:param do_delete: if False than it allows to check performance
in "create only" mode.
"""
wb = self._create_workbook(definition)
wb_def = yaml.safe_load(wb.definition)
if not workflow_name:
workflow_name = six.next(six.iterkeys(wb_def["workflows"]))
workflow_identifier = ".".join([wb.name, workflow_name])
ex = self._create_execution(workflow_identifier)
if do_delete:
self._delete_workbook(wb.name)
self._delete_execution(ex)

View File

@ -13,10 +13,23 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_config import cfg
import yaml import yaml
from rally.plugins.openstack import scenario from rally.plugins.openstack import scenario
from rally.task import atomic from rally.task import atomic
from rally.task import utils
MISTRAL_BENCHMARK_OPTS = [
cfg.IntOpt(
"mistral_execution_timeout",
default=200,
help="mistral execution timeout")
]
CONF = cfg.CONF
benchmark_group = cfg.OptGroup(name="benchmark", title="benchmark options")
CONF.register_opts(MISTRAL_BENCHMARK_OPTS, group=benchmark_group)
class MistralScenario(scenario.OpenStackScenario): class MistralScenario(scenario.OpenStackScenario):
@ -24,7 +37,10 @@ class MistralScenario(scenario.OpenStackScenario):
@atomic.action_timer("mistral.list_workbooks") @atomic.action_timer("mistral.list_workbooks")
def _list_workbooks(self): def _list_workbooks(self):
"""Gets list of existing workbooks.""" """Gets list of existing workbooks.
:returns: workbook list
"""
return self.clients("mistral").workbooks.list() return self.clients("mistral").workbooks.list()
@atomic.action_timer("mistral.create_workbook") @atomic.action_timer("mistral.create_workbook")
@ -48,3 +64,41 @@ class MistralScenario(scenario.OpenStackScenario):
:param wb_name: the name of workbook that would be deleted. :param wb_name: the name of workbook that would be deleted.
""" """
self.clients("mistral").workbooks.delete(wb_name) self.clients("mistral").workbooks.delete(wb_name)
@atomic.action_timer("mistral.list_executions")
def _list_executions(self, marker="", limit=None, sort_keys="",
sort_dirs=""):
"""Gets list of existing executions.
:returns: execution list
"""
return self.clients("mistral").executions.list(
marker=marker, limit=limit, sort_keys=sort_keys,
sort_dirs=sort_dirs)
@atomic.action_timer("mistral.create_execution")
def _create_execution(self, workflow_identifier):
"""Create a new execution.
:param workflow_identifier: name or id of the workflow to execute
:returns: executions object
"""
execution = self.clients("mistral").executions.create(
workflow_identifier)
execution = utils.wait_for_status(
execution, ready_statuses=["SUCCESS"], failure_statuses=["ERROR"],
update_resource=utils.get_from_manager(),
timeout=CONF.benchmark.mistral_execution_timeout)
return execution
@atomic.action_timer("mistral.delete_execution")
def _delete_execution(self, execution):
"""Delete the given execution.
:param ex: the execution that would be deleted.
"""
self.clients("mistral").executions.delete(execution.id)

View File

@ -38,9 +38,9 @@ class ListWorkbooks(utils.MistralScenario):
self._list_workbooks() self._list_workbooks()
@types.convert(definition={"type": "file"})
@validation.file_exists("definition")
@validation.required_parameters("definition") @validation.required_parameters("definition")
@validation.file_exists("definition")
@types.convert(definition={"type": "file"})
@validation.required_clients("mistral") @validation.required_clients("mistral")
@validation.required_openstack(users=True) @validation.required_openstack(users=True)
@validation.required_services(consts.Service.MISTRAL) @validation.required_services(consts.Service.MISTRAL)
@ -62,4 +62,4 @@ class CreateWorkbook(utils.MistralScenario):
wb = self._create_workbook(definition) wb = self._create_workbook(definition)
if do_delete: if do_delete:
self._delete_workbook(wb.name) self._delete_workbook(wb.name)

View File

@ -21,6 +21,7 @@ import re
from glanceclient import exc as glance_exc from glanceclient import exc as glance_exc
from novaclient import exceptions as nova_exc from novaclient import exceptions as nova_exc
import six import six
import yaml
from rally.common.i18n import _ from rally.common.i18n import _
from rally.common import objects from rally.common import objects
@ -727,3 +728,30 @@ def validate_heat_template(config, clients, deployment, *param_names):
msg = (_("Heat template validation failed on %(path)s. " msg = (_("Heat template validation failed on %(path)s. "
"Original error message: %(msg)s.") % dct) "Original error message: %(msg)s.") % dct)
return ValidationResult(False, msg) return ValidationResult(False, msg)
@validator
def workbook_contains_workflow(config, clients, deployment, workbook,
workflow_name):
"""Validate that workflow exist in workbook when workflow is passed
:param workbook: parameter containing the workbook definition
:param workflow_name: parameter containing the workflow name
"""
wf_name = config.get("args", {}).get(workflow_name)
if wf_name:
wb_path = config.get("args", {}).get(workbook)
wb_path = os.path.expanduser(wb_path)
file_result = _file_access_ok(config.get("args", {}).get(workbook),
os.R_OK, workbook)
if not file_result.is_valid:
return file_result
with open(wb_path, "r") as wb_def:
wb_def = yaml.safe_load(wb_def)
if wf_name not in wb_def["workflows"]:
return ValidationResult(
False,
"workflow '{}' not found in the definition '{}'".format(
wf_name, wb_def))

View File

@ -0,0 +1,27 @@
{
"MistralExecutions.create_execution_from_workbook": [
{
"args": {
"definition": "rally-jobs/extra/mistral_wb.yaml",
"workflow_name": "wf1",
"do_delete": true
},
"runner": {
"type": "constant",
"times": 20,
"concurrency": 5
},
"context": {
"users": {
"tenants": 2,
"users_per_tenant": 2
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@ -0,0 +1,20 @@
---
MistralExecutions.create_execution_from_workbook:
-
args:
definition: rally-jobs/extra/mistral_wb.yaml
workflow_name: wf1
do_delete: true
runner:
type: "constant"
times: 20
concurrency: 5
context:
users:
tenants: 2
users_per_tenant: 2
sla:
failure_rate:
max: 0

View File

@ -0,0 +1,26 @@
{
"MistralExecutions.create_execution_from_workbook": [
{
"args": {
"definition": "rally-jobs/extra/mistral_wb.yaml",
"do_delete": true
},
"runner": {
"type": "constant",
"times": 20,
"concurrency": 5
},
"context": {
"users": {
"tenants": 2,
"users_per_tenant": 2
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@ -0,0 +1,18 @@
---
MistralExecutions.create_execution_from_workbook:
-
args:
definition: rally-jobs/extra/mistral_wb.yaml
do_delete: true
runner:
type: "constant"
times: 20
concurrency: 5
context:
users:
tenants: 2
users_per_tenant: 2
sla:
failure_rate:
max: 0

View File

@ -0,0 +1,26 @@
{
"MistralExecutions.create_execution_from_workbook": [
{
"args": {
"definition": "rally-jobs/extra/mistral_wb.yaml",
"workflow_name": "wf1"
},
"runner": {
"type": "constant",
"times": 20,
"concurrency": 5
},
"context": {
"users": {
"tenants": 2,
"users_per_tenant": 2
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@ -0,0 +1,18 @@
---
MistralExecutions.create_execution_from_workbook:
-
args:
definition: rally-jobs/extra/mistral_wb.yaml
workflow_name: wf1
runner:
type: "constant"
times: 20
concurrency: 5
context:
users:
tenants: 2
users_per_tenant: 2
sla:
failure_rate:
max: 0

View File

@ -0,0 +1,25 @@
{
"MistralExecutions.create_execution_from_workbook": [
{
"args": {
"definition": "rally-jobs/extra/mistral_wb.yaml"
},
"runner": {
"type": "constant",
"times": 20,
"concurrency": 5
},
"context": {
"users": {
"tenants": 2,
"users_per_tenant": 2
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@ -0,0 +1,16 @@
---
MistralExecutions.create_execution_from_workbook:
-
args:
definition: rally-jobs/extra/mistral_wb.yaml
runner:
type: "constant"
times: 20
concurrency: 5
context:
users:
tenants: 2
users_per_tenant: 2
sla:
failure_rate:
max: 0

View File

@ -0,0 +1,22 @@
{
"MistralExecutions.list_executions": [
{
"runner": {
"type": "constant",
"times": 50,
"concurrency": 10
},
"context": {
"users": {
"tenants": 2,
"users_per_tenant": 2
}
},
"sla": {
"failure_rate": {
"max": 0
}
}
}
]
}

View File

@ -0,0 +1,14 @@
---
MistralExecutions.list_executions:
-
runner:
type: "constant"
times: 50
concurrency: 10
context:
users:
tenants: 2
users_per_tenant: 2
sla:
failure_rate:
max: 0

View File

@ -119,6 +119,20 @@ class Magnum(ResourceManager):
return result return result
class Mistral(ResourceManager):
REQUIRED_SERVICE = consts.Service.MISTRAL
def list_workbooks(self):
return self.client.workbooks.list()
def list_workflows(self):
return self.client.workflows.list()
def list_executions(self):
return self.client.executions.list()
class Nova(ResourceManager): class Nova(ResourceManager):
REQUIRED_SERVICE = consts.Service.NOVA REQUIRED_SERVICE = consts.Service.NOVA

View File

@ -318,6 +318,18 @@ class FakeWorkbook(FakeResource):
self.workbook = mock.MagicMock() self.workbook = mock.MagicMock()
class FakeWorkflow(FakeResource):
def __init__(self, manager=None):
super(FakeWorkflow, self).__init__(manager)
self.workflow = mock.MagicMock()
class FakeExecution(FakeResource):
def __init__(self, manager=None):
super(FakeExecution, self).__init__(manager)
self.execution = mock.MagicMock()
class FakeObject(FakeResource): class FakeObject(FakeResource):
pass pass
@ -939,6 +951,27 @@ class FakeWorkbookManager(FakeManager):
return [self.workbook] return [self.workbook]
class FakeWorkflowManager(FakeManager):
def __init__(self):
super(FakeWorkflowManager, self).__init__()
self.workflow = FakeWorkflow()
def list(self):
return [self.workflow]
class FakeExecutionManager(FakeManager):
def __init__(self):
super(FakeExecutionManager, self).__init__()
self.execution = FakeExecution()
def list(self):
return [self.execution]
def create(self):
return self.execution
class FakeObjectManager(FakeManager): class FakeObjectManager(FakeManager):
def get_account(self, **kwargs): def get_account(self, **kwargs):
@ -1505,6 +1538,8 @@ class FakeMistralClient(object):
def __init__(self): def __init__(self):
self.workbook = FakeWorkbookManager() self.workbook = FakeWorkbookManager()
self.workflow = FakeWorkflowManager()
self.execution = FakeExecutionManager()
class FakeSwiftClient(FakeObjectManager): class FakeSwiftClient(FakeObjectManager):

View File

@ -760,6 +760,36 @@ class ManilaSecurityServiceTestCase(test.TestCase):
"fake_id") "fake_id")
class MistralMixinTestCase(test.TestCase):
def test_delete(self):
mistral = resources.MistralMixin()
mistral._service = "mistral"
mistral.user = mock.MagicMock()
mistral._resource = "some_resources"
mistral.raw_resource = {"id": "TEST_ID"}
mistral.user.mistral().some_resources.delete.return_value = None
mistral.delete()
mistral.user.mistral().some_resources.delete.assert_called_once_with(
"TEST_ID")
class MistralWorkbookTestCase(test.TestCase):
def test_delete(self):
mistral = resources.MistralWorkbooks()
mistral._service = "mistral"
mistral.user = mock.MagicMock()
mistral._resource = "some_resources"
mistral.raw_resource = {"name": "TEST_NAME"}
mistral.user.mistral().some_resources.delete.return_value = None
mistral.delete()
mistral.user.mistral().some_resources.delete.assert_called_once_with(
"TEST_NAME")
class FuelEnvironmentTestCase(test.TestCase): class FuelEnvironmentTestCase(test.TestCase):
def test_id(self): def test_id(self):

View File

@ -0,0 +1,162 @@
# Copyright 2016: Nokia Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from rally.plugins.openstack.scenarios.mistral import executions
from tests.unit import test
BASE = "rally.plugins.openstack.scenarios.mistral.executions"
MISTRAL_WBS_BASE = "rally.plugins.openstack.scenarios.mistral.workbooks"
WB_DEFINITION = """---
version: 2.0
name: wb
workflows:
wf1:
type: direct
tasks:
noop_task:
action: std.noop
wf2:
type: direct
tasks:
noop_task:
action: std.noop
wf3:
type: direct
tasks:
noop_task:
action: std.noop
wf4:
type: direct
tasks:
noop_task:
action: std.noop
"""
WB_DEF_ONE_WF = """---
version: 2.0
name: wb
workflows:
wf1:
type: direct
tasks:
noop_task:
action: std.noop
"""
WB = type("obj", (object,), {"name": "wb", "definition": WB_DEFINITION})()
WB_ONE_WF = (
type("obj", (object,), {"name": "wb", "definition": WB_DEF_ONE_WF})()
)
class MistralExecutionsTestCase(test.ScenarioTestCase):
@mock.patch("%s.ListExecutions._list_executions" % BASE)
def test_list_executions(self, mock__list_executions):
executions.ListExecutions(self.context).run()
self.assertEqual(1, mock__list_executions.called)
@mock.patch("%s.CreateExecutionFromWorkbook._create_execution" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._create_workbook" % BASE,
return_value=WB)
def test_create_execution(self, mock__create_workbook,
mock__create_execution):
executions.CreateExecutionFromWorkbook(self.context).run(WB_DEFINITION)
self.assertEqual(1, mock__create_workbook.called)
self.assertEqual(1, mock__create_execution.called)
@mock.patch("%s.CreateExecutionFromWorkbook._create_execution" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._create_workbook" % BASE,
return_value=WB)
def test_create_execution_with_wf_name(self, mock__create_workbook,
mock__create_execution):
executions.CreateExecutionFromWorkbook(self.context).run(
WB_DEFINITION, "wf4")
self.assertEqual(1, mock__create_workbook.called)
self.assertEqual(1, mock__create_execution.called)
# we concatenate workbook name with the workflow name in the test
# the workbook name is not random because we mock the method that
# adds the random part
mock__create_execution.assert_called_once_with("wb.wf4")
@mock.patch("%s.CreateExecutionFromWorkbook._delete_execution" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._delete_workbook" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._create_execution" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._create_workbook" % BASE,
return_value=WB)
def test_create_delete_execution(
self, mock__create_workbook, mock__create_execution,
mock__delete_workbook, mock__delete_execution):
executions.CreateExecutionFromWorkbook(self.context).run(
WB_DEFINITION, do_delete=True)
self.assertEqual(1, mock__create_workbook.called)
self.assertEqual(1, mock__create_execution.called)
self.assertEqual(1, mock__delete_workbook.called)
self.assertEqual(1, mock__delete_execution.called)
@mock.patch("%s.CreateExecutionFromWorkbook._delete_execution" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._delete_workbook" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._create_execution" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._create_workbook" % BASE,
return_value=WB)
def test_create_delete_execution_with_wf_name(
self, mock__create_workbook, mock__create_execution,
mock__delete_workbook, mock__delete_execution):
executions.CreateExecutionFromWorkbook(self.context).run(
WB_DEFINITION, "wf4", do_delete=True)
self.assertEqual(1, mock__create_workbook.called)
self.assertEqual(1, mock__create_execution.called)
self.assertEqual(1, mock__delete_workbook.called)
self.assertEqual(1, mock__delete_execution.called)
# we concatenate workbook name with the workflow name in the test
# the workbook name is not random because we mock the method that
# adds the random part
mock__create_execution.assert_called_once_with("wb.wf4")
@mock.patch("%s.CreateExecutionFromWorkbook._delete_execution" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._delete_workbook" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._create_execution" % BASE)
@mock.patch("%s.CreateExecutionFromWorkbook._create_workbook" % BASE,
return_value=WB_ONE_WF)
def test_create_delete_execution_without_wf_name(
self, mock__create_workbook, mock__create_execution,
mock__delete_workbook, mock__delete_execution):
executions.CreateExecutionFromWorkbook(self.context).run(
WB_DEF_ONE_WF, do_delete=True)
self.assertEqual(1, mock__create_workbook.called)
self.assertEqual(1, mock__create_execution.called)
self.assertEqual(1, mock__delete_workbook.called)
self.assertEqual(1, mock__delete_execution.called)
# we concatenate workbook name with the workflow name in the test
# the workbook name is not random because we mock the method that
# adds the random part
mock__create_execution.assert_called_once_with("wb.wf1")

View File

@ -14,6 +14,7 @@
# under the License. # under the License.
from rally.plugins.openstack.scenarios.mistral import utils from rally.plugins.openstack.scenarios.mistral import utils
from tests.unit import fakes
from tests.unit import test from tests.unit import test
MISTRAL_UTILS = "rally.plugins.openstack.scenarios.mistral.utils" MISTRAL_UTILS = "rally.plugins.openstack.scenarios.mistral.utils"
@ -27,21 +28,77 @@ class MistralScenarioTestCase(test.ScenarioTestCase):
self.assertEqual( self.assertEqual(
self.clients("mistral").workbooks.list.return_value, self.clients("mistral").workbooks.list.return_value,
return_wbs_list) return_wbs_list)
self._test_atomic_action_timer(scenario.atomic_actions(), self._test_atomic_action_timer(
"mistral.list_workbooks") scenario.atomic_actions(),
"mistral.list_workbooks"
)
def test_create_workbook(self): def test_create_workbook(self):
definition = "version: \"2.0\"\nname: wb" definition = "version: \"2.0\"\nname: wb"
scenario = utils.MistralScenario(context=self.context) scenario = utils.MistralScenario(context=self.context)
self.assertEqual(scenario._create_workbook(definition), self.assertEqual(
self.clients("mistral").workbooks.create.return_value) self.clients("mistral").workbooks.create.return_value,
self._test_atomic_action_timer(scenario.atomic_actions(), scenario._create_workbook(definition)
"mistral.create_workbook") )
self._test_atomic_action_timer(
scenario.atomic_actions(),
"mistral.create_workbook"
)
def test_delete_workbook(self): def test_delete_workbook(self):
scenario = utils.MistralScenario(context=self.context) scenario = utils.MistralScenario(context=self.context)
scenario._delete_workbook("wb_name") scenario._delete_workbook("wb_name")
self.clients("mistral").workbooks.delete.assert_called_once_with( self.clients("mistral").workbooks.delete.assert_called_once_with(
"wb_name") "wb_name"
self._test_atomic_action_timer(scenario.atomic_actions(), )
"mistral.delete_workbook") self._test_atomic_action_timer(
scenario.atomic_actions(),
"mistral.delete_workbook"
)
def test_list_executions(self):
scenario = utils.MistralScenario(context=self.context)
return_executions_list = scenario._list_executions()
self.assertEqual(
return_executions_list,
self.clients("mistral").executions.list.return_value
)
self._test_atomic_action_timer(
scenario.atomic_actions(),
"mistral.list_executions"
)
def test_create_execution(self):
scenario = utils.MistralScenario(context=self.context)
mock_wait_for_status = self.mock_wait_for_status.mock
wf_name = "fake_wf_name"
mock_create_exec = self.clients("mistral").executions.create
self.assertEqual(
mock_wait_for_status.return_value,
scenario._create_execution("%s" % wf_name)
)
mock_create_exec.assert_called_once_with(wf_name)
args, kwargs = mock_wait_for_status.call_args
self.assertEqual(mock_create_exec.return_value, args[0])
self.assertEqual(["ERROR"], kwargs["failure_statuses"])
self.assertEqual(["SUCCESS"], kwargs["ready_statuses"])
self._test_atomic_action_timer(
scenario.atomic_actions(),
"mistral.create_execution"
)
def test_delete_execution(self):
scenario = utils.MistralScenario(context=self.context)
execution = fakes.FakeMistralClient().execution.create()
scenario._delete_execution(execution)
self.clients("mistral").executions.delete.assert_called_once_with(
execution.id
)
self._test_atomic_action_timer(
scenario.atomic_actions(),
"mistral.delete_execution"
)

View File

@ -974,3 +974,44 @@ class ValidatorsTestCase(test.TestCase):
config = {"context": {"api_versions": {"nova": {"version": 2}}}} config = {"context": {"api_versions": {"nova": {"version": 2}}}}
self.assertEqual(validator(config, clients, None).is_valid, self.assertEqual(validator(config, clients, None).is_valid,
valid) valid)
@mock.patch(
"yaml.safe_load",
return_value={
"version": "2.0",
"name": "wb",
"workflows": {
"wf1": {
"type": "direct",
"tasks": {
"t1": {
"action": "std.noop"
}
}
}
}
}
)
@mock.patch(MODULE + "os.access")
@mock.patch(MODULE + "open")
def test_workbook_contains_workflow(self, mock_open, mock_access,
mock_safe_load):
validator = self._unwrap_validator(
validation.workbook_contains_workflow, "definition",
"workflow_name")
clients = mock.MagicMock()
context = {
"args": {
"definition": "fake_path1",
"workflow_name": "wf1"
}
}
result = validator(context, clients, None)
self.assertTrue(result.is_valid)
self.assertEqual(1, mock_open.called)
self.assertEqual(1, mock_access.called)
self.assertEqual(1, mock_safe_load.called)