Add Ironic scenarios
Add Ironic scenarios: * create_and_list_node * create_and_delete_node Add Ironic related utils: * _create_node * _list_nodes * _delete_node Add unit tests for them. Add Ironic to _Service and BARE_METAL to _ServiceType. Also there is a new rally-ironic.yaml job file. Change-Id: I5bfa68d2f9dc30680996d932c5b03b01d6c0a0fa
This commit is contained in:
parent
e736a18ced
commit
8dd702c904
18
rally-jobs/rally-ironic.yaml
Normal file
18
rally-jobs/rally-ironic.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
{% for s in ("create_and_list_node", "create_and_delete_node") %}
|
||||||
|
IronicNodes.{{s}}:
|
||||||
|
-
|
||||||
|
args:
|
||||||
|
driver: "pxe_ssh"
|
||||||
|
runner:
|
||||||
|
type: "constant"
|
||||||
|
times: 10
|
||||||
|
concurrency: 2
|
||||||
|
context:
|
||||||
|
users:
|
||||||
|
tenants: 5
|
||||||
|
users_per_tenant: 1
|
||||||
|
sla:
|
||||||
|
failure_rate:
|
||||||
|
max: 0
|
||||||
|
{% endfor %}
|
@ -105,6 +105,7 @@ class _Service(utils.ImmutableMixin, utils.EnumMixin):
|
|||||||
SWIFT = "swift"
|
SWIFT = "swift"
|
||||||
MISTRAL = "mistral"
|
MISTRAL = "mistral"
|
||||||
MURANO = "murano"
|
MURANO = "murano"
|
||||||
|
IRONIC = "ironic"
|
||||||
|
|
||||||
|
|
||||||
class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
|
class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
|
||||||
@ -130,6 +131,7 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
|
|||||||
OBJECT_STORE = "object-store"
|
OBJECT_STORE = "object-store"
|
||||||
WORKFLOW_EXECUTION = "workflowv2"
|
WORKFLOW_EXECUTION = "workflowv2"
|
||||||
APPLICATION_CATALOG = "application_catalog"
|
APPLICATION_CATALOG = "application_catalog"
|
||||||
|
BARE_METAL = "baremetal"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__names = {
|
self.__names = {
|
||||||
@ -152,7 +154,8 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
|
|||||||
self.DATA_PROCESSING: _Service.SAHARA,
|
self.DATA_PROCESSING: _Service.SAHARA,
|
||||||
self.OBJECT_STORE: _Service.SWIFT,
|
self.OBJECT_STORE: _Service.SWIFT,
|
||||||
self.WORKFLOW_EXECUTION: _Service.MISTRAL,
|
self.WORKFLOW_EXECUTION: _Service.MISTRAL,
|
||||||
self.APPLICATION_CATALOG: _Service.MURANO
|
self.APPLICATION_CATALOG: _Service.MURANO,
|
||||||
|
self.BARE_METAL: _Service.IRONIC,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __getitem__(self, service_type):
|
def __getitem__(self, service_type):
|
||||||
|
78
rally/plugins/openstack/scenarios/ironic/nodes.py
Normal file
78
rally/plugins/openstack/scenarios/ironic/nodes.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Copyright 2015: Mirantis Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from rally import consts
|
||||||
|
from rally.plugins.openstack.scenarios.ironic import utils
|
||||||
|
from rally.task.scenarios import base
|
||||||
|
from rally.task import validation
|
||||||
|
|
||||||
|
|
||||||
|
class IronicNodes(utils.IronicScenario):
|
||||||
|
"""Base class for Ironic scenarios with basic atomic actions."""
|
||||||
|
|
||||||
|
@validation.required_parameters("driver")
|
||||||
|
@validation.required_services(consts.Service.IRONIC)
|
||||||
|
@validation.required_openstack(admin=True)
|
||||||
|
@base.scenario(context={"admin_cleanup": ["ironic"]})
|
||||||
|
def create_and_list_node(
|
||||||
|
self, associated=None, maintenance=None,
|
||||||
|
marker=None, limit=None, detail=False, sort_key=None,
|
||||||
|
sort_dir=None, **kwargs):
|
||||||
|
"""Create and list nodes.
|
||||||
|
|
||||||
|
:param associated: Optional. Either a Boolean or a string
|
||||||
|
representation of a Boolean that indicates whether
|
||||||
|
to return a list of associated (True or "True") or
|
||||||
|
unassociated (False or "False") nodes.
|
||||||
|
:param maintenance: Optional. Either a Boolean or a string
|
||||||
|
representation of a Boolean that indicates whether
|
||||||
|
to return nodes in maintenance mode (True or
|
||||||
|
"True"), or not in maintenance mode (False or
|
||||||
|
"False").
|
||||||
|
:param marker: Optional, the UUID of a node, eg the last
|
||||||
|
node from a previous result set. Return
|
||||||
|
the next result set.
|
||||||
|
:param limit: The maximum number of results to return per
|
||||||
|
request, if:
|
||||||
|
1) limit > 0, the maximum number of nodes to return.
|
||||||
|
2) limit == 0, return the entire list of nodes.
|
||||||
|
3) limit param is NOT specified (None), the number of items
|
||||||
|
returned respect the maximum imposed by the Ironic API
|
||||||
|
(see Ironic's api.max_limit option).
|
||||||
|
:param detail: Optional, boolean whether to return detailed
|
||||||
|
information about nodes.
|
||||||
|
:param sort_key: Optional, field used for sorting.
|
||||||
|
:param sort_dir: Optional, direction of sorting, either 'asc' (the
|
||||||
|
default) or 'desc'.
|
||||||
|
:param kwargs: Optional additional arguments for node creation
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._create_node(**kwargs)
|
||||||
|
|
||||||
|
self._list_nodes(
|
||||||
|
associated=associated, maintenance=maintenance, marker=marker,
|
||||||
|
limit=limit, detail=detail, sort_key=sort_key, sort_dir=sort_dir)
|
||||||
|
|
||||||
|
@validation.required_parameters("driver")
|
||||||
|
@validation.required_services(consts.Service.IRONIC)
|
||||||
|
@validation.required_openstack(admin=True)
|
||||||
|
@base.scenario(context={"admin_cleanup": ["ironic"]})
|
||||||
|
def create_and_delete_node(self, **kwargs):
|
||||||
|
"""Create and delete node.
|
||||||
|
|
||||||
|
:param kwargs: Optional additional arguments for node creation
|
||||||
|
"""
|
||||||
|
node = self._create_node(**kwargs)
|
||||||
|
self._delete_node(node.uuid)
|
102
rally/plugins/openstack/scenarios/ironic/utils.py
Normal file
102
rally/plugins/openstack/scenarios/ironic/utils.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# Copyright 2015: Mirantis Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import string
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
|
||||||
|
from rally.common import utils
|
||||||
|
from rally.plugins.openstack import scenario
|
||||||
|
from rally.task.scenarios import base
|
||||||
|
|
||||||
|
|
||||||
|
IRONIC_BENCHMARK_OPTS = [
|
||||||
|
cfg.FloatOpt("ironic_node_create_poll_interval",
|
||||||
|
default=1.0,
|
||||||
|
help="Interval(in sec) between checks when waiting for node "
|
||||||
|
"creation."),
|
||||||
|
]
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
benchmark_group = cfg.OptGroup(name="benchmark", title="benchmark options")
|
||||||
|
CONF.register_opts(IRONIC_BENCHMARK_OPTS, group=benchmark_group)
|
||||||
|
|
||||||
|
|
||||||
|
class IronicScenario(scenario.OpenStackScenario):
|
||||||
|
"""Base class for Ironic scenarios with basic atomic actions."""
|
||||||
|
|
||||||
|
@base.atomic_action_timer("ironic.create_node")
|
||||||
|
def _create_node(self, **kwargs):
|
||||||
|
"""Create node immediately.
|
||||||
|
|
||||||
|
:param kwargs: optional parameters to create image
|
||||||
|
:returns: node object
|
||||||
|
"""
|
||||||
|
if "name" not in kwargs:
|
||||||
|
# NOTE(rvasilets): can't use _generate_random_name() because
|
||||||
|
# ironic have specific format for node name.
|
||||||
|
# Check that the supplied hostname conforms to:
|
||||||
|
# * http://en.wikipedia.org/wiki/Hostname
|
||||||
|
# * http://tools.ietf.org/html/rfc952
|
||||||
|
# * http://tools.ietf.org/html/rfc1123
|
||||||
|
# or the name could be just uuid.
|
||||||
|
kwargs["name"] = utils.generate_random_name(
|
||||||
|
prefix="rally", choice=string.ascii_lowercase + string.digits)
|
||||||
|
|
||||||
|
return self.admin_clients("ironic").node.create(**kwargs)
|
||||||
|
|
||||||
|
@base.atomic_action_timer("ironic.list_nodes")
|
||||||
|
def _list_nodes(self, associated=None, maintenance=None, marker=None,
|
||||||
|
limit=None, detail=False, sort_key=None, sort_dir=None):
|
||||||
|
"""Return list of nodes.
|
||||||
|
|
||||||
|
:param associated: Optional. Either a Boolean or a string
|
||||||
|
representation of a Boolean that indicates whether
|
||||||
|
to return a list of associated (True or "True") or
|
||||||
|
unassociated (False or "False") nodes.
|
||||||
|
:param maintenance: Optional. Either a Boolean or a string
|
||||||
|
representation of a Boolean that indicates whether
|
||||||
|
to return nodes in maintenance mode (True or
|
||||||
|
"True"), or not in maintenance mode (False or
|
||||||
|
"False").
|
||||||
|
:param marker: Optional, the UUID of a node, eg the last
|
||||||
|
node from a previous result set. Return
|
||||||
|
the next result set.
|
||||||
|
:param limit: The maximum number of results to return per
|
||||||
|
request, if:
|
||||||
|
1) limit > 0, the maximum number of nodes to return.
|
||||||
|
2) limit == 0, return the entire list of nodes.
|
||||||
|
3) limit param is NOT specified (None), the number of items
|
||||||
|
returned respect the maximum imposed by the Ironic API
|
||||||
|
(see Ironic's api.max_limit option).
|
||||||
|
:param detail: Optional, boolean whether to return detailed information
|
||||||
|
about nodes.
|
||||||
|
:param sort_key: Optional, field used for sorting.
|
||||||
|
:param sort_dir: Optional, direction of sorting, either 'asc' (the
|
||||||
|
default) or 'desc'.
|
||||||
|
:returns: A list of nodes.
|
||||||
|
"""
|
||||||
|
return self.admin_clients("ironic").node.list(
|
||||||
|
associated=associated, maintenance=maintenance, marker=marker,
|
||||||
|
limit=limit, detail=detail, sort_key=sort_key, sort_dir=sort_dir)
|
||||||
|
|
||||||
|
@base.atomic_action_timer("ironic.delete_node")
|
||||||
|
def _delete_node(self, node_id):
|
||||||
|
"""Delete the node with specific id.
|
||||||
|
|
||||||
|
:param node_id: id of the node to be deleted
|
||||||
|
"""
|
||||||
|
self.admin_clients("ironic").node.delete(node_id)
|
20
samples/tasks/scenarios/ironic/create-and-delete-node.json
Normal file
20
samples/tasks/scenarios/ironic/create-and-delete-node.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"IronicNodes.create_and_delete_node": [
|
||||||
|
{
|
||||||
|
"args": {
|
||||||
|
"driver": "pxe_ssh"
|
||||||
|
},
|
||||||
|
"runner": {
|
||||||
|
"type": "constant",
|
||||||
|
"times": 10,
|
||||||
|
"concurrency": 2
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"users": {
|
||||||
|
"tenants": 5,
|
||||||
|
"users_per_tenant": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
13
samples/tasks/scenarios/ironic/create-and-delete-node.yaml
Normal file
13
samples/tasks/scenarios/ironic/create-and-delete-node.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
IronicNodes.create_and_delete_node:
|
||||||
|
-
|
||||||
|
args:
|
||||||
|
driver: "pxe_ssh"
|
||||||
|
runner:
|
||||||
|
type: "constant"
|
||||||
|
times: 10
|
||||||
|
concurrency: 2
|
||||||
|
context:
|
||||||
|
users:
|
||||||
|
tenants: 5
|
||||||
|
users_per_tenant: 1
|
20
samples/tasks/scenarios/ironic/create-and-list-node.json
Normal file
20
samples/tasks/scenarios/ironic/create-and-list-node.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"IronicNodes.create_and_list_node": [
|
||||||
|
{
|
||||||
|
"args": {
|
||||||
|
"driver": "pxe_ssh"
|
||||||
|
},
|
||||||
|
"runner": {
|
||||||
|
"type": "constant",
|
||||||
|
"times": 10,
|
||||||
|
"concurrency": 2
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"users": {
|
||||||
|
"tenants": 5,
|
||||||
|
"users_per_tenant": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
13
samples/tasks/scenarios/ironic/create-and-list-node.yaml
Normal file
13
samples/tasks/scenarios/ironic/create-and-list-node.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
IronicNodes.create_and_list_node:
|
||||||
|
-
|
||||||
|
args:
|
||||||
|
driver: "pxe_ssh"
|
||||||
|
runner:
|
||||||
|
type: "constant"
|
||||||
|
times: 10
|
||||||
|
concurrency: 2
|
||||||
|
context:
|
||||||
|
users:
|
||||||
|
tenants: 5
|
||||||
|
users_per_tenant: 1
|
56
tests/unit/plugins/openstack/scenarios/ironic/test_nodes.py
Normal file
56
tests/unit/plugins/openstack/scenarios/ironic/test_nodes.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Copyright 2015: Mirantis Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from rally.plugins.openstack.scenarios.ironic import nodes
|
||||||
|
from tests.unit import test
|
||||||
|
|
||||||
|
|
||||||
|
class IronicNodesTestCase(test.ScenarioTestCase):
|
||||||
|
|
||||||
|
def test_create_and_list_node(self):
|
||||||
|
scenario = nodes.IronicNodes()
|
||||||
|
scenario._create_node = mock.Mock()
|
||||||
|
scenario._list_nodes = mock.Mock()
|
||||||
|
fake_params = {
|
||||||
|
"sort_dir": "foo1",
|
||||||
|
"associated": "foo2",
|
||||||
|
"sort_key": "foo3",
|
||||||
|
"detail": True,
|
||||||
|
"limit": "foo4",
|
||||||
|
"maintenance": "foo5",
|
||||||
|
"marker": "foo6",
|
||||||
|
"fake_parameter1": "foo7"
|
||||||
|
}
|
||||||
|
scenario.create_and_list_node(**fake_params)
|
||||||
|
|
||||||
|
scenario._create_node.assert_called_once_with(fake_parameter1="foo7")
|
||||||
|
scenario._list_nodes.assert_called_once_with(
|
||||||
|
sort_dir="foo1", associated="foo2", sort_key="foo3", detail=True,
|
||||||
|
limit="foo4", maintenance="foo5", marker="foo6")
|
||||||
|
|
||||||
|
def test_create_and_delete_node(self):
|
||||||
|
fake_node = mock.Mock(uuid="fake_uuid")
|
||||||
|
scenario = nodes.IronicNodes()
|
||||||
|
scenario._create_node = mock.Mock(return_value=fake_node)
|
||||||
|
scenario._delete_node = mock.Mock()
|
||||||
|
|
||||||
|
scenario.create_and_delete_node(fake_parameter1="fake1",
|
||||||
|
fake_parameter2="fake2")
|
||||||
|
scenario._create_node.assert_called_once_with(fake_parameter1="fake1",
|
||||||
|
fake_parameter2="fake2")
|
||||||
|
|
||||||
|
scenario._delete_node.assert_called_once_with("fake_uuid")
|
68
tests/unit/plugins/openstack/scenarios/ironic/test_utils.py
Normal file
68
tests/unit/plugins/openstack/scenarios/ironic/test_utils.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Copyright 2015: Mirantis Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from rally.plugins.openstack.scenarios.ironic import utils
|
||||||
|
from tests.unit import test
|
||||||
|
|
||||||
|
IRONIC_UTILS = "rally.plugins.openstack.scenarios.ironic.utils"
|
||||||
|
|
||||||
|
|
||||||
|
class IronicScenarioTestCase(test.ScenarioTestCase):
|
||||||
|
|
||||||
|
@mock.patch("rally.common.utils.generate_random_name")
|
||||||
|
def test__create_node(self, mock_generate_random_name):
|
||||||
|
mock_generate_random_name.return_value = "rally_fake_random_string"
|
||||||
|
self.admin_clients("ironic").node.create.return_value = "fake_node"
|
||||||
|
scenario = utils.IronicScenario()
|
||||||
|
create_node = scenario._create_node(fake_param="foo")
|
||||||
|
|
||||||
|
self.assertEqual("fake_node", create_node)
|
||||||
|
self.admin_clients("ironic").node.create.assert_called_once_with(
|
||||||
|
fake_param="foo", name="rally_fake_random_string")
|
||||||
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
||||||
|
"ironic.create_node")
|
||||||
|
|
||||||
|
def test__delete_node(self):
|
||||||
|
mock_node_delete = mock.Mock()
|
||||||
|
self.admin_clients("ironic").node.delete = mock_node_delete
|
||||||
|
scenario = utils.IronicScenario()
|
||||||
|
scenario._delete_node("fake_id")
|
||||||
|
|
||||||
|
self.admin_clients("ironic").node.delete.assert_called_once_with(
|
||||||
|
"fake_id")
|
||||||
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
||||||
|
"ironic.delete_node")
|
||||||
|
|
||||||
|
def test__list_nodes(self):
|
||||||
|
self.admin_clients("ironic").node.list.return_value = ["fake"]
|
||||||
|
scenario = utils.IronicScenario()
|
||||||
|
fake_params = {
|
||||||
|
"sort_dir": "foo1",
|
||||||
|
"associated": "foo2",
|
||||||
|
"sort_key": "foo3",
|
||||||
|
"detail": True,
|
||||||
|
"limit": "foo4",
|
||||||
|
"maintenance": "foo5",
|
||||||
|
"marker": "foo6"
|
||||||
|
}
|
||||||
|
return_nodes_list = scenario._list_nodes(**fake_params)
|
||||||
|
self.assertEqual(["fake"], return_nodes_list)
|
||||||
|
self.admin_clients("ironic").node.list.assert_called_once_with(
|
||||||
|
sort_dir="foo1", associated="foo2", sort_key="foo3", detail=True,
|
||||||
|
limit="foo4", maintenance="foo5", marker="foo6")
|
||||||
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
||||||
|
"ironic.list_nodes")
|
Loading…
x
Reference in New Issue
Block a user