Merge "Add option to create mistral execution from workbook"
This commit is contained in:
commit
d599de9a26
@ -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
|
||||||
|
@ -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
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
92
rally/plugins/openstack/scenarios/mistral/executions.py
Normal file
92
rally/plugins/openstack/scenarios/mistral/executions.py
Normal 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)
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
||||||
|
|
||||||
|
|
26
samples/tasks/scenarios/mistral/create-delete-execution.json
Normal file
26
samples/tasks/scenarios/mistral/create-delete-execution.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
18
samples/tasks/scenarios/mistral/create-delete-execution.yaml
Normal file
18
samples/tasks/scenarios/mistral/create-delete-execution.yaml
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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
|
||||||
|
|
25
samples/tasks/scenarios/mistral/create-execution.json
Normal file
25
samples/tasks/scenarios/mistral/create-execution.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
16
samples/tasks/scenarios/mistral/create-execution.yaml
Normal file
16
samples/tasks/scenarios/mistral/create-execution.yaml
Normal 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
|
22
samples/tasks/scenarios/mistral/list-executions.json
Normal file
22
samples/tasks/scenarios/mistral/list-executions.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
14
samples/tasks/scenarios/mistral/list-executions.yaml
Normal file
14
samples/tasks/scenarios/mistral/list-executions.yaml
Normal 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
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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")
|
@ -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"
|
||||||
|
)
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user