d2f4e9717d
Move all modules under the next structure: - rally_openstack.common - rally_openstack.enviromnet - rally_openstack.task - rally_openstack.verification Change-Id: I41702d017cd49b117da3b8e12b19c7327229ae32
343 lines
16 KiB
Python
343 lines
16 KiB
Python
# Copyright 2014: 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 unittest import mock
|
|
|
|
from rally import exceptions
|
|
|
|
from rally_openstack.task.scenarios.heat import utils
|
|
from tests.unit import test
|
|
|
|
HEAT_UTILS = "rally_openstack.task.scenarios.heat.utils"
|
|
|
|
CONF = utils.CONF
|
|
|
|
|
|
class HeatScenarioTestCase(test.ScenarioTestCase):
|
|
def setUp(self):
|
|
super(HeatScenarioTestCase, self).setUp()
|
|
self.stack = mock.Mock()
|
|
self.scenario = utils.HeatScenario(self.context)
|
|
self.default_template = "heat_template_version: 2013-05-23"
|
|
self.dummy_parameters = {"dummy_param": "dummy_key"}
|
|
self.dummy_files = ["dummy_file.yaml"]
|
|
self.dummy_environment = {"dummy_env": "dummy_env_value"}
|
|
self.default_output_key = "dummy_output_key"
|
|
|
|
def test_list_stacks(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
return_stacks_list = scenario._list_stacks()
|
|
self.clients("heat").stacks.list.assert_called_once_with()
|
|
self.assertEqual(list(self.clients("heat").stacks.list.return_value),
|
|
return_stacks_list)
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.list_stacks")
|
|
|
|
def test_create_stack(self):
|
|
self.clients("heat").stacks.create.return_value = {
|
|
"stack": {"id": "test_id"}
|
|
}
|
|
self.clients("heat").stacks.get.return_value = self.stack
|
|
return_stack = self.scenario._create_stack(self.default_template,
|
|
self.dummy_parameters,
|
|
self.dummy_files,
|
|
self.dummy_environment)
|
|
args, kwargs = self.clients("heat").stacks.create.call_args
|
|
self.assertIn(self.dummy_parameters, kwargs.values())
|
|
self.assertIn(self.default_template, kwargs.values())
|
|
self.assertIn(self.dummy_files, kwargs.values())
|
|
self.assertIn(self.dummy_environment, kwargs.values())
|
|
self.mock_wait_for_status.mock.assert_called_once_with(
|
|
self.stack,
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
ready_statuses=["CREATE_COMPLETE"],
|
|
failure_statuses=["CREATE_FAILED", "ERROR"],
|
|
check_interval=CONF.openstack.heat_stack_create_poll_interval,
|
|
timeout=CONF.openstack.heat_stack_create_timeout)
|
|
self.mock_get_from_manager.mock.assert_called_once_with()
|
|
self.assertEqual(self.mock_wait_for_status.mock.return_value,
|
|
return_stack)
|
|
self._test_atomic_action_timer(self.scenario.atomic_actions(),
|
|
"heat.create_stack")
|
|
|
|
def test_update_stack(self):
|
|
self.clients("heat").stacks.update.return_value = None
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._update_stack(self.stack, self.default_template,
|
|
self.dummy_parameters, self.dummy_files,
|
|
self.dummy_environment)
|
|
args, kwargs = self.clients("heat").stacks.update.call_args
|
|
self.assertIn(self.dummy_parameters, kwargs.values())
|
|
self.assertIn(self.default_template, kwargs.values())
|
|
self.assertIn(self.dummy_files, kwargs.values())
|
|
self.assertIn(self.dummy_environment, kwargs.values())
|
|
self.assertIn(self.stack.id, args)
|
|
self.mock_wait_for_status.mock.assert_called_once_with(
|
|
self.stack,
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
ready_statuses=["UPDATE_COMPLETE"],
|
|
failure_statuses=["UPDATE_FAILED", "ERROR"],
|
|
check_interval=CONF.openstack.heat_stack_update_poll_interval,
|
|
timeout=CONF.openstack.heat_stack_update_timeout)
|
|
self.mock_get_from_manager.mock.assert_called_once_with()
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.update_stack")
|
|
|
|
def test_check_stack(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._check_stack(self.stack)
|
|
self.clients("heat").actions.check.assert_called_once_with(
|
|
self.stack.id)
|
|
self.mock_wait_for_status.mock.assert_called_once_with(
|
|
self.stack,
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
ready_statuses=["CHECK_COMPLETE"],
|
|
failure_statuses=["CHECK_FAILED", "ERROR"],
|
|
check_interval=CONF.openstack.heat_stack_check_poll_interval,
|
|
timeout=CONF.openstack.heat_stack_check_timeout)
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.check_stack")
|
|
|
|
def test_delete_stack(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._delete_stack(self.stack)
|
|
self.stack.delete.assert_called_once_with()
|
|
self.mock_wait_for_status.mock.assert_called_once_with(
|
|
self.stack,
|
|
ready_statuses=["DELETE_COMPLETE"],
|
|
failure_statuses=["DELETE_FAILED", "ERROR"],
|
|
check_deletion=True,
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
check_interval=CONF.openstack.heat_stack_delete_poll_interval,
|
|
timeout=CONF.openstack.heat_stack_delete_timeout)
|
|
self.mock_get_from_manager.mock.assert_called_once_with()
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.delete_stack")
|
|
|
|
def test_suspend_stack(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._suspend_stack(self.stack)
|
|
self.clients("heat").actions.suspend.assert_called_once_with(
|
|
self.stack.id)
|
|
self.mock_wait_for_status.mock.assert_called_once_with(
|
|
self.stack,
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
ready_statuses=["SUSPEND_COMPLETE"],
|
|
failure_statuses=["SUSPEND_FAILED", "ERROR"],
|
|
check_interval=CONF.openstack.heat_stack_suspend_poll_interval,
|
|
timeout=CONF.openstack.heat_stack_suspend_timeout)
|
|
self.mock_get_from_manager.mock.assert_called_once_with()
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.suspend_stack")
|
|
|
|
def test_resume_stack(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._resume_stack(self.stack)
|
|
self.clients("heat").actions.resume.assert_called_once_with(
|
|
self.stack.id)
|
|
self.mock_wait_for_status.mock.assert_called_once_with(
|
|
self.stack,
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
ready_statuses=["RESUME_COMPLETE"],
|
|
failure_statuses=["RESUME_FAILED", "ERROR"],
|
|
check_interval=CONF.openstack.heat_stack_resume_poll_interval,
|
|
timeout=CONF.openstack.heat_stack_resume_timeout)
|
|
self.mock_get_from_manager.mock.assert_called_once_with()
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.resume_stack")
|
|
|
|
def test_snapshot_stack(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._snapshot_stack(self.stack)
|
|
self.clients("heat").stacks.snapshot.assert_called_once_with(
|
|
self.stack.id)
|
|
self.mock_wait_for_status.mock.assert_called_once_with(
|
|
self.stack,
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
ready_statuses=["SNAPSHOT_COMPLETE"],
|
|
failure_statuses=["SNAPSHOT_FAILED", "ERROR"],
|
|
check_interval=CONF.openstack.heat_stack_snapshot_poll_interval,
|
|
timeout=CONF.openstack.heat_stack_snapshot_timeout)
|
|
self.mock_get_from_manager.mock.assert_called_once_with()
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.snapshot_stack")
|
|
|
|
def test_restore_stack(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._restore_stack(self.stack, "dummy_id")
|
|
self.clients("heat").stacks.restore.assert_called_once_with(
|
|
self.stack.id, "dummy_id")
|
|
self.mock_wait_for_status.mock.assert_called_once_with(
|
|
self.stack,
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
ready_statuses=["RESTORE_COMPLETE"],
|
|
failure_statuses=["RESTORE_FAILED", "ERROR"],
|
|
check_interval=CONF.openstack.heat_stack_restore_poll_interval,
|
|
timeout=CONF.openstack.heat_stack_restore_timeout)
|
|
self.mock_get_from_manager.mock.assert_called_once_with()
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.restore_stack")
|
|
|
|
def test__count_instances(self):
|
|
self.clients("heat").resources.list.return_value = [
|
|
mock.Mock(resource_type="OS::Nova::Server"),
|
|
mock.Mock(resource_type="OS::Nova::Server"),
|
|
mock.Mock(resource_type="OS::Heat::AutoScalingGroup")]
|
|
scenario = utils.HeatScenario(self.context)
|
|
self.assertEqual(scenario._count_instances(self.stack), 2)
|
|
self.clients("heat").resources.list.assert_called_once_with(
|
|
self.stack.id,
|
|
nested_depth=1)
|
|
|
|
def test__scale_stack(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._count_instances = mock.Mock(side_effect=[3, 3, 2])
|
|
scenario._stack_webhook = mock.Mock()
|
|
|
|
scenario._scale_stack(self.stack, "test_output_key", -1)
|
|
|
|
scenario._stack_webhook.assert_called_once_with(self.stack,
|
|
"test_output_key")
|
|
self.mock_wait_for.mock.assert_called_once_with(
|
|
self.stack,
|
|
is_ready=mock.ANY,
|
|
failure_statuses=["UPDATE_FAILED", "ERROR"],
|
|
update_resource=self.mock_get_from_manager.mock.return_value,
|
|
timeout=CONF.openstack.heat_stack_scale_timeout,
|
|
check_interval=CONF.openstack.heat_stack_scale_poll_interval)
|
|
self.mock_get_from_manager.mock.assert_called_once_with()
|
|
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.scale_with_test_output_key")
|
|
|
|
@mock.patch("requests.post")
|
|
def test_stack_webhook(self, mock_post):
|
|
env_context = {
|
|
"env": {
|
|
"spec": {
|
|
"existing@openstack": {
|
|
"https_cacert": "cacert.crt",
|
|
"https_insecure": False
|
|
}
|
|
}
|
|
}
|
|
}
|
|
env_context.update(self.context)
|
|
scenario = utils.HeatScenario(env_context)
|
|
stack = mock.Mock(outputs=[
|
|
{"output_key": "output1", "output_value": "url1"},
|
|
{"output_key": "output2", "output_value": "url2"}])
|
|
|
|
scenario._stack_webhook(stack, "output1")
|
|
mock_post.assert_called_with("url1", verify="cacert.crt")
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.output1_webhook")
|
|
|
|
@mock.patch("requests.post")
|
|
def test_stack_webhook_insecure(self, mock_post):
|
|
env_context = {
|
|
"env": {
|
|
"spec": {
|
|
"existing@openstack": {
|
|
"https_cacert": "cacert.crt",
|
|
"https_insecure": True
|
|
}
|
|
}
|
|
}
|
|
}
|
|
env_context.update(self.context)
|
|
scenario = utils.HeatScenario(env_context)
|
|
stack = mock.Mock(outputs=[
|
|
{"output_key": "output1", "output_value": "url1"},
|
|
{"output_key": "output2", "output_value": "url2"}])
|
|
|
|
scenario._stack_webhook(stack, "output1")
|
|
mock_post.assert_called_with("url1", verify=False)
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.output1_webhook")
|
|
|
|
@mock.patch("requests.post")
|
|
def test_stack_webhook_invalid_output_key(self, mock_post):
|
|
scenario = utils.HeatScenario(self.context)
|
|
stack = mock.Mock()
|
|
stack.outputs = [{"output_key": "output1", "output_value": "url1"},
|
|
{"output_key": "output2", "output_value": "url2"}]
|
|
|
|
self.assertRaises(exceptions.InvalidConfigException,
|
|
scenario._stack_webhook, stack, "bogus")
|
|
|
|
def test_stack_show_output(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._stack_show_output(self.stack, self.default_output_key)
|
|
self.clients("heat").stacks.output_show.assert_called_once_with(
|
|
self.stack.id, self.default_output_key)
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.show_output")
|
|
|
|
def test_stack_show_output_via_API(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._stack_show_output_via_API(
|
|
self.stack, self.default_output_key)
|
|
self.clients("heat").stacks.get.assert_called_once_with(
|
|
stack_id=self.stack.id)
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.show_output_via_API")
|
|
|
|
def test_stack_list_output(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._stack_list_output(self.stack)
|
|
self.clients("heat").stacks.output_list.assert_called_once_with(
|
|
self.stack.id)
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.list_output")
|
|
|
|
def test_stack_list_output_via_API(self):
|
|
scenario = utils.HeatScenario(self.context)
|
|
scenario._stack_list_output_via_API(self.stack)
|
|
self.clients("heat").stacks.get.assert_called_once_with(
|
|
stack_id=self.stack.id)
|
|
self._test_atomic_action_timer(scenario.atomic_actions(),
|
|
"heat.list_output_via_API")
|
|
|
|
|
|
class HeatScenarioNegativeTestCase(test.ScenarioTestCase):
|
|
patch_task_utils = False
|
|
|
|
def test_failed_create_stack(self):
|
|
self.clients("heat").stacks.create.return_value = {
|
|
"stack": {"id": "test_id"}
|
|
}
|
|
stack = mock.Mock()
|
|
resource = mock.Mock()
|
|
resource.stack_status = "CREATE_FAILED"
|
|
stack.manager.get.return_value = resource
|
|
self.clients("heat").stacks.get.return_value = stack
|
|
scenario = utils.HeatScenario(context=self.context)
|
|
ex = self.assertRaises(exceptions.GetResourceErrorStatus,
|
|
scenario._create_stack, "stack_name")
|
|
self.assertIn("has CREATE_FAILED status", str(ex))
|
|
|
|
def test_failed_update_stack(self):
|
|
stack = mock.Mock()
|
|
resource = mock.Mock()
|
|
resource.stack_status = "UPDATE_FAILED"
|
|
stack.manager.get.return_value = resource
|
|
self.clients("heat").stacks.get.return_value = stack
|
|
scenario = utils.HeatScenario(context=self.context)
|
|
ex = self.assertRaises(exceptions.GetResourceErrorStatus,
|
|
scenario._update_stack, stack,
|
|
"heat_template_version: 2013-05-23")
|
|
self.assertIn("has UPDATE_FAILED status", str(ex))
|