Add create-update-delete stack scenario

The create-update-delete stack scenario executes the following
actions with using Heat:
 - creates the new stack (with some resources)
 - updates the stack and stack resoruces with the new template
 - deletes the stack.
Unit-tests for the changes above are also included into patch.

Patch also adds rally test cases for stack create-update-delete
stack scenario (because rally tests require test scenario to be
used by test case samples):
Case 1:
- Heat creates a new stack
- Heat updates the stack with adding additional resource to
the stack
- Heat deletes the stack
Case 2:
- Heat creates a stack
- Heat updates the stack with deleting some resource from the
stack
- Heat deletes the stack.

Change-Id: I4c0ff24d0651434450a281a62670f1bd6c2a0000
This commit is contained in:
kairat_kushaev 2015-02-04 15:05:19 +03:00
parent d23a7e449c
commit 2fe25740bb
15 changed files with 322 additions and 18 deletions

View File

@ -0,0 +1,13 @@
heat_template_version: 2014-10-16
description: Test template for rally create-update-delete scenario
resources:
test_string_one:
type: OS::Heat::RandomString
properties:
length: 20
test_string_two:
type: OS::Heat::RandomString
properties:
length: 20

View File

@ -0,0 +1,19 @@
heat_template_version: 2014-10-16
description: >
Test template for create-update-delete-stack scenario in rally.
The template updates the stack defined by random_strings.yaml.template with additional resource.
resources:
test_string_one:
type: OS::Heat::RandomString
properties:
length: 20
test_string_two:
type: OS::Heat::RandomString
properties:
length: 20
test_string_three:
type: OS::Heat::RandomString
properties:
length: 20

View File

@ -0,0 +1,11 @@
heat_template_version: 2014-10-16
description: >
Test template for create-update-delete-stack scenario in rally.
The template deletes one resource from the stack defined by random_strings.yaml.template.
resources:
test_string_one:
type: OS::Heat::RandomString
properties:
length: 20

View File

@ -475,7 +475,7 @@
-
args:
template_path: '/home/jenkins/.rally/extra/server_with_volume.yaml.template'
template_path: "/home/jenkins/.rally/extra/server_with_volume.yaml.template"
runner:
type: "constant"
times: 2
@ -488,6 +488,39 @@
failure_rate:
max: 0
HeatStacks.create_update_delete_stack:
-
args:
template_path: "/home/jenkins/.rally/extra/random_strings.yaml.template"
updated_template_path: "/home/jenkins/.rally/extra/updated_random_strings_add.yaml.template"
runner:
type: "constant"
times: 6
concurrency: 3
context:
users:
tenants: 2
users_per_tenant: 3
sla:
failure_rate:
max: 0
-
args:
template_path: "/home/jenkins/.rally/extra/random_strings.yaml.template"
updated_template_path: "/home/jenkins/.rally/extra/updated_random_strings_delete.yaml.template"
runner:
type: "constant"
times: 6
concurrency: 3
context:
users:
tenants: 2
users_per_tenant: 3
sla:
failure_rate:
max: 0
Authenticate.keystone:
-
runner:

View File

@ -25,7 +25,8 @@ class HeatStacks(utils.HeatScenario):
RESOURCE_NAME_PREFIX = "rally_stack_"
RESOURCE_NAME_LENGTH = 7
def _get_template_from_file(self, template_path):
@staticmethod
def _get_template_from_file(template_path):
template = None
if template_path:
try:
@ -42,17 +43,15 @@ class HeatStacks(utils.HeatScenario):
def create_and_list_stack(self, template_path=None):
"""Add a stack and then list all stacks.
Mesure the "heat stack-create" and "heat stack-list" commands
Measure the "heat stack-create" and "heat stack-list" commands
performance.
:param template_path: path to template file. If None or incorrect,
then default empty template will be used.
"""
stack_name = self._generate_random_name()
template = self._get_template_from_file(template_path)
self._create_stack(stack_name, template)
self._create_stack(template)
self._list_stacks()
@validation.required_services(consts.Service.HEAT)
@ -67,8 +66,31 @@ class HeatStacks(utils.HeatScenario):
:param template_path: path to template file. If None or incorrect,
then default empty template will be used.
"""
stack_name = self._generate_random_name()
template = self._get_template_from_file(template_path)
stack = self._create_stack(stack_name, template)
template = self._get_template_from_file(template_path)
stack = self._create_stack(template)
self._delete_stack(stack)
@validation.required_services(consts.Service.HEAT)
@validation.required_openstack(users=True)
@base.scenario(context={"cleanup": ["heat"]})
def create_update_delete_stack(self, template_path=None,
updated_template_path=None):
"""Add, update and then delete a stack.
Measure the "heat stack-create", "heat stack-update"
and "heat stack-delete" commands performance.
:param template_path: path to template file. If None or incorrect,
then default empty template will be used.
:param updated_template_path: path to template file that will be used
for stack update. If None or incorrect,
then default empty template will be
used instead.
"""
template = self._get_template_from_file(template_path)
stack = self._create_stack(template)
updated_template = self._get_template_from_file(updated_template_path)
self._update_stack(stack, updated_template)
self._delete_stack(stack)

View File

@ -39,7 +39,18 @@ heat_benchmark_opts = [
cfg.FloatOpt("heat_stack_delete_poll_interval",
default=1.0,
help="Interval between checks when waiting for stack "
"deletion.")
"deletion."),
cfg.FloatOpt("heat_stack_update_prepoll_delay",
default=2.0,
help="Time to sleep after updating a resource before "
"polling for it status"),
cfg.FloatOpt("heat_stack_update_timeout",
default=3600.0,
help="Time to wait for stack to be updated"),
cfg.FloatOpt("heat_stack_update_poll_interval",
default=1.0,
help="Interval between checks when waiting for stack "
"update."),
]
@ -60,16 +71,15 @@ class HeatScenario(base.Scenario):
return list(self.clients("heat").stacks.list())
@base.atomic_action_timer("heat.create_stack")
def _create_stack(self, stack_name, template=None):
def _create_stack(self, template=None):
"""Create a new stack.
:param stack_name: string. Name for created stack.
:param template: optional parameter. Template with stack description.
:returns: object of stack
"""
stack_name = self._generate_random_name()
template = template or self.default_template
kw = {
"stack_name": stack_name,
"disable_rollback": True,
@ -95,6 +105,36 @@ class HeatScenario(base.Scenario):
return stack
@base.atomic_action_timer("heat.update_stack")
def _update_stack(self, stack, template=None):
"""Update an existing stack
:param stack: stack that need to be updated
:param template: Updated template
:return: object of updated stack
"""
template = template or self.default_template
kw = {
"stack_name": stack.stack_name,
"disable_rollback": True,
"parameters": {},
"template": template,
"files": {},
"environment": {}
}
self.clients("heat").stacks.update(stack.id, **kw)
time.sleep(CONF.benchmark.heat_stack_update_prepoll_delay)
stack = bench_utils.wait_for(
stack,
is_ready=bench_utils.resource_is("UPDATE_COMPLETE"),
update_resource=bench_utils.get_from_manager(["UPDATE_FAILED"]),
timeout=CONF.benchmark.heat_stack_update_timeout,
check_interval=CONF.benchmark.heat_stack_update_poll_interval)
return stack
@base.atomic_action_timer("heat.delete_stack")
def _delete_stack(self, stack):
"""Delete given stack.
@ -108,4 +148,4 @@ class HeatScenario(base.Scenario):
stack,
update_resource=bench_utils.get_from_manager(),
timeout=CONF.benchmark.heat_stack_delete_timeout,
check_interval=CONF.benchmark.heat_stack_delete_poll_interval)
check_interval=CONF.benchmark.heat_stack_delete_poll_interval)

View File

@ -0,0 +1,21 @@
{
"HeatStacks.create_update_delete_stack": [
{
"args": {
"template_path": "templates/random_strings.yaml.template",
"updated_template_path": "templates/updated_random_strings_add.yaml.template"
},
"runner": {
"type": "constant",
"times": 10,
"concurrency": 2
},
"context": {
"users": {
"tenants": 2,
"users_per_tenant": 3
}
}
}
]
}

View File

@ -0,0 +1,14 @@
---
HeatStacks.create_update_delete_stack:
-
args:
template_path: "templates/random_strings.yaml.template"
updated_template_path: "templates/updated_random_strings_add.yaml.template"
runner:
type: "constant"
times: 10
concurrency: 2
context:
users:
tenants: 2
users_per_tenant: 3

View File

@ -0,0 +1,21 @@
{
"HeatStacks.create_update_delete_stack": [
{
"args": {
"template_path": "templates/random_strings.yaml.template",
"updated_template_path": "templates/updated_random_strings_delete.yaml.template"
},
"runner": {
"type": "constant",
"times": 10,
"concurrency": 2
},
"context": {
"users": {
"tenants": 2,
"users_per_tenant": 3
}
}
}
]
}

View File

@ -0,0 +1,14 @@
---
HeatStacks.create_update_delete_stack:
-
args:
template_path: "templates/random_strings.yaml.template"
updated_template_path: "templates/updated_random_strings_delete.yaml.template"
runner:
type: "constant"
times: 10
concurrency: 2
context:
users:
tenants: 2
users_per_tenant: 3

View File

@ -0,0 +1,13 @@
heat_template_version: 2014-10-16
description: Test template for rally create-update-delete scenario
resources:
test_string_one:
type: OS::Heat::RandomString
properties:
length: 20
test_string_two:
type: OS::Heat::RandomString
properties:
length: 20

View File

@ -0,0 +1,19 @@
heat_template_version: 2014-10-16
description: >
Test template for create-update-delete-stack scenario in rally.
The template updates the stack defined by random_strings.yaml.template with additional resource.
resources:
test_string_one:
type: OS::Heat::RandomString
properties:
length: 20
test_string_two:
type: OS::Heat::RandomString
properties:
length: 20
test_string_three:
type: OS::Heat::RandomString
properties:
length: 20

View File

@ -0,0 +1,11 @@
heat_template_version: 2014-10-16
description: >
Test template for create-update-delete-stack scenario in rally.
The template deletes one resource from the stack defined by random_strings.yaml.template.
resources:
test_string_one:
type: OS::Heat::RandomString
properties:
length: 20

View File

@ -59,5 +59,21 @@ class HeatStacksTestCase(test.TestCase):
mock_random_name.return_value = "test-rally-stack"
heat_scenario.create_and_delete_stack()
self.assertEqual(1, mock_create.called)
self.assertTrue(mock_create.called)
mock_delete.assert_called_once_with(fake_stack)
@mock.patch(HEAT_STACKS + "._generate_random_name")
@mock.patch(HEAT_STACKS + "._delete_stack")
@mock.patch(HEAT_STACKS + "._update_stack")
@mock.patch(HEAT_STACKS + "._create_stack")
def test_create_update_delete_stack(self, mock_create, mock_update,
mock_delete, mock_random_name):
heat_scenario = stacks.HeatStacks()
fake_stack = object()
mock_create.return_value = fake_stack
mock_random_name.return_value = "test-rally-stack"
heat_scenario.create_update_delete_stack()
self.assertTrue(mock_create.called)
mock_update.assert_called_once_with(fake_stack, None)
mock_delete.assert_called_once_with(fake_stack)

View File

@ -25,7 +25,6 @@ HEAT_UTILS = "rally.benchmark.scenarios.heat.utils"
class HeatScenarioTestCase(test.TestCase):
def setUp(self):
super(HeatScenarioTestCase, self).setUp()
self.stack = mock.Mock()
@ -59,7 +58,7 @@ class HeatScenarioTestCase(test.TestCase):
}
mock_clients("heat").stacks.get.return_value = self.stack
scenario = utils.HeatScenario()
return_stack = scenario._create_stack("stack_name")
return_stack = scenario._create_stack()
self.wait_for.mock.assert_called_once_with(self.stack,
update_resource=self.gfm(),
is_ready=self.res_is.mock(),
@ -70,6 +69,25 @@ class HeatScenarioTestCase(test.TestCase):
self._test_atomic_action_timer(scenario.atomic_actions(),
"heat.create_stack")
@mock.patch(HEAT_UTILS + ".HeatScenario.clients")
@mock.patch(HEAT_UTILS + ".CONF.benchmark")
def test_update_stack(self, mock_bench, mock_clients):
mock_clients("heat").stacks.update.return_value = None
mock_bench.heat_stack_update_timeout = 1
mock_bench.heat_stack_update_poll_interval = 1
scenario = utils.HeatScenario()
scenario._update_stack(self.stack, None)
self.wait_for.mock.assert_called_once_with(
self.stack,
update_resource=self.gfm(),
is_ready=self.res_is.mock(),
check_interval=1,
timeout=1)
self.res_is.mock.assert_has_calls([mock.call("UPDATE_COMPLETE")])
self._test_atomic_action_timer(scenario.atomic_actions(),
"heat.update_stack")
def test_delete_stack(self):
scenario = utils.HeatScenario()
scenario._delete_stack(self.stack)
@ -84,7 +102,6 @@ class HeatScenarioTestCase(test.TestCase):
class HeatScenarioNegativeTestCase(test.TestCase):
@mock.patch(HEAT_UTILS + ".HeatScenario.clients")
@mock.patch(HEAT_UTILS + ".CONF.benchmark")
def test_failed_create_stack(self, mock_bench, mock_clients):
@ -107,3 +124,23 @@ class HeatScenarioNegativeTestCase(test.TestCase):
self.assertIn("has CREATE_FAILED status", str(ex))
except exceptions.TimeoutException:
raise self.fail("Unrecognized error status")
@mock.patch(HEAT_UTILS + ".HeatScenario.clients")
@mock.patch(HEAT_UTILS + ".CONF.benchmark")
def test_failed_update_stack(self, mock_bench, mock_clients):
mock_bench.heat_stack_update_prepoll_delay = 2
mock_bench.heat_stack_update_timeout = 1
mock_bench.benchmark.heat_stack_update_poll_interval = 1
stack = mock.Mock()
resource = mock.Mock()
resource.stack_status = "UPDATE_FAILED"
stack.manager.get.return_value = resource
mock_clients("heat").stacks.get.return_value = stack
scenario = utils.HeatScenario()
try:
ex = self.assertRaises(exceptions.GetResourceErrorStatus,
scenario._update_stack, stack)
self.assertIn("has UPDATE_FAILED status", str(ex))
except exceptions.TimeoutException:
raise self.fail("Unrecognized error status")