tripleo-common/tripleo_common/tests/actions/test_deployment.py

615 lines
26 KiB
Python

# Copyright 2016 Red Hat, 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 json
import mock
import yaml
from heatclient import exc as heat_exc
from mistral_lib import actions
from swiftclient import exceptions as swiftexceptions
from tripleo_common.actions import deployment
from tripleo_common import constants
from tripleo_common.tests import base
class OrchestrationDeployActionTest(base.TestCase):
def setUp(self,):
super(OrchestrationDeployActionTest, self).setUp()
self.server_id = 'server_id'
self.config = 'config'
self.name = 'name'
self.input_values = []
self.action = 'CREATE'
self.signal_transport = 'TEMP_URL_SIGNAL'
self.timeout = 300
self.group = 'script'
def test_extract_container_object_from_swift_url(self):
swift_url = 'https://example.com' + \
'/v1/a422b2-91f3-2f46-74b7-d7c9e8958f5d30/container/object' + \
'?temp_url_sig=da39a3ee5e6b4&temp_url_expires=1323479485'
action = deployment.OrchestrationDeployAction(self.server_id,
self.config, self.name,
self.timeout)
self.assertEqual(('container', 'object'),
action._extract_container_object_from_swift_url(
swift_url))
@mock.patch(
'heatclient.common.deployment_utils.build_derived_config_params')
def test_build_sc_params(self, build_derived_config_params_mock):
build_derived_config_params_mock.return_value = 'built_params'
action = deployment.OrchestrationDeployAction(self.server_id,
self.config, self.name)
self.assertEqual('built_params', action._build_sc_params('swift_url'))
build_derived_config_params_mock.assert_called_once()
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_wait_for_data(self, get_obj_client_mock):
mock_ctx = mock.MagicMock()
swift = mock.MagicMock()
swift.get_object.return_value = ({}, 'body')
get_obj_client_mock.return_value = swift
action = deployment.OrchestrationDeployAction(self.server_id,
self.config, self.name)
self.assertEqual('body', action._wait_for_data('container',
'object',
context=mock_ctx))
get_obj_client_mock.assert_called_once()
swift.get_object.assert_called_once_with('container', 'object')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('time.sleep')
def test_wait_for_data_timeout(self, sleep, get_obj_client_mock):
mock_ctx = mock.MagicMock()
swift = mock.MagicMock()
swift.get_object.return_value = ({}, None)
get_obj_client_mock.return_value = swift
action = deployment.OrchestrationDeployAction(self.server_id,
self.config, self.name,
timeout=10)
self.assertIsNone(action._wait_for_data('container',
'object',
context=mock_ctx))
get_obj_client_mock.assert_called_once()
swift.get_object.assert_called_with('container', 'object')
# Trying every 3 seconds, so 4 times for a timeout of 10 seconds
self.assertEqual(swift.get_object.call_count, 4)
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('heatclient.common.deployment_utils.create_temp_url')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_extract_container_object_from_swift_url')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_build_sc_params')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_wait_for_data')
def test_run(self, wait_for_data_mock, build_sc_params_mock,
extract_from_swift_url_mock, create_temp_url_mock,
get_heat_mock, get_obj_client_mock):
extract_from_swift_url_mock.return_value = ('container', 'object')
mock_ctx = mock.MagicMock()
build_sc_params_mock.return_value = {'foo': 'bar'}
config = mock.MagicMock()
sd = mock.MagicMock()
get_heat_mock().software_configs.create.return_value = config
get_heat_mock().software_deployments.create.return_value = sd
wait_for_data_mock.return_value = '{"deploy_status_code": 0}'
action = deployment.OrchestrationDeployAction(self.server_id,
self.config, self.name)
expected = actions.Result(
data={"deploy_status_code": 0},
error=None)
self.assertEqual(expected, action.run(context=mock_ctx))
create_temp_url_mock.assert_called_once()
extract_from_swift_url_mock.assert_called_once()
build_sc_params_mock.assert_called_once()
get_obj_client_mock.assert_called_once()
wait_for_data_mock.assert_called_once()
sd.delete.assert_called_once()
config.delete.assert_called_once()
get_obj_client_mock.delete_object.called_once_with('container',
'object')
get_obj_client_mock.delete_container.called_once_with('container')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('heatclient.common.deployment_utils.create_temp_url')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_extract_container_object_from_swift_url')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_build_sc_params')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_wait_for_data')
def test_run_timeout(self, wait_for_data_mock, build_sc_params_mock,
extract_from_swift_url_mock, create_temp_url_mock,
get_heat_mock, get_obj_client_mock):
extract_from_swift_url_mock.return_value = ('container', 'object')
mock_ctx = mock.MagicMock()
config = mock.MagicMock()
sd = mock.MagicMock()
get_heat_mock().software_configs.create.return_value = config
get_heat_mock().software_deployments.create.return_value = sd
wait_for_data_mock.return_value = None
action = deployment.OrchestrationDeployAction(self.server_id,
self.config, self.name)
expected = actions.Result(
data={},
error="Timeout for heat deployment 'name'")
self.assertEqual(expected, action.run(mock_ctx))
sd.delete.assert_called_once()
config.delete.assert_called_once()
get_obj_client_mock.delete_object.called_once_with('container',
'object')
get_obj_client_mock.delete_container.called_once_with('container')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('heatclient.common.deployment_utils.create_temp_url')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_extract_container_object_from_swift_url')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_build_sc_params')
@mock.patch('tripleo_common.actions.deployment.OrchestrationDeployAction.'
'_wait_for_data')
def test_run_failed(self, wait_for_data_mock, build_sc_params_mock,
extract_from_swift_url_mock, create_temp_url_mock,
get_heat_mock, get_obj_client_mock):
extract_from_swift_url_mock.return_value = ('container', 'object')
mock_ctx = mock.MagicMock()
config = mock.MagicMock()
sd = mock.MagicMock()
get_heat_mock().software_configs.create.return_value = config
get_heat_mock().software_deployments.create.return_value = sd
wait_for_data_mock.return_value = '{"deploy_status_code": 1}'
action = deployment.OrchestrationDeployAction(self.server_id,
self.config, self.name)
expected = actions.Result(
data={"deploy_status_code": 1},
error="Heat deployment failed for 'name'")
self.assertEqual(expected, action.run(mock_ctx))
sd.delete.assert_called_once()
config.delete.assert_called_once()
get_obj_client_mock.delete_object.called_once_with('container',
'object')
get_obj_client_mock.delete_container.called_once_with('container')
class DeployStackActionTest(base.TestCase):
@mock.patch('tripleo_common.actions.deployment.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_orchestration_client')
def test_run(self, get_orchestration_client_mock,
mock_get_object_client, mock_get_template_contents,
mock_process_multiple_environments_and_files,
mock_time):
mock_ctx = mock.MagicMock()
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_object_client.return_value = swift
heat = mock.MagicMock()
heat.stacks.get.return_value = None
get_orchestration_client_mock.return_value = heat
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
action = deployment.DeployStackAction(1, 'overcloud')
action.run(mock_ctx)
# verify parameters are as expected
expected_defaults = {'DeployIdentifier': 1473366264,
'StackAction': 'CREATE',
'UpdateIdentifier': '',
'random_existing_data': 'a_value'}
mock_env_updated = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'parameter_defaults': expected_defaults,
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}]
}, default_flow_style=False)
swift.put_object.assert_called_once_with(
'overcloud',
constants.PLAN_ENVIRONMENT,
mock_env_updated
)
heat.stacks.create.assert_called_once_with(
environment={},
files={},
stack_name='overcloud',
template={'heat_template_version': '2016-04-30'},
timeout_mins=1,
)
swift.delete_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz")
swift.copy_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz",
"overcloud-swift-rings/swift-rings.tar.gz-%d" % 1473366264)
@mock.patch('tripleo_common.actions.deployment.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_orchestration_client')
def test_run_skip_deploy_identifier(
self, get_orchestration_client_mock,
mock_get_object_client, mock_get_template_contents,
mock_process_multiple_environments_and_files,
mock_time):
mock_ctx = mock.MagicMock()
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_get_object_client.return_value = swift
heat = mock.MagicMock()
heat.stacks.get.return_value = None
get_orchestration_client_mock.return_value = heat
mock_env = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
action = deployment.DeployStackAction(1, 'overcloud',
skip_deploy_identifier=True)
action.run(mock_ctx)
# verify parameters are as expected
mock_env_updated = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'parameter_defaults': {'StackAction': 'CREATE',
'UpdateIdentifier': '',
'random_existing_data': 'a_value'},
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}]
}, default_flow_style=False)
swift.put_object.assert_called_once_with(
constants.DEFAULT_CONTAINER_NAME,
constants.PLAN_ENVIRONMENT,
mock_env_updated
)
heat.stacks.create.assert_called_once_with(
environment={},
files={},
stack_name='overcloud',
template={'heat_template_version': '2016-04-30'},
timeout_mins=1,
)
swift.delete_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz")
swift.copy_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz",
"overcloud-swift-rings/swift-rings.tar.gz-%d" % 1473366264)
@mock.patch('tripleo_common.actions.deployment.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_orchestration_client')
def test_run_create_failed(
self, get_orchestration_client_mock, mock_get_object_client,
mock_get_template_contents,
mock_process_multiple_environments_and_files, mock_time):
mock_ctx = mock.MagicMock()
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_object_client.return_value = swift
heat = mock.MagicMock()
heat.stacks.get.return_value = None
heat.stacks.create.side_effect = heat_exc.HTTPException("Oops")
get_orchestration_client_mock.return_value = heat
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
action = deployment.DeployStackAction(1, 'overcloud')
expected = actions.Result(
error="Error during stack creation: ERROR: Oops\n")
self.assertEqual(expected, action.run(mock_ctx))
@mock.patch('tripleo_common.actions.deployment.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_orchestration_client')
def test_run_update_failed(
self, get_orchestration_client_mock, mock_get_object_client,
mock_get_template_contents,
mock_process_multiple_environments_and_files, mock_time):
mock_ctx = mock.MagicMock()
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_object_client.return_value = swift
heat = mock.MagicMock()
heat.stacks.get.return_value = mock.Mock()
heat.stacks.update.side_effect = heat_exc.HTTPException("Oops")
get_orchestration_client_mock.return_value = heat
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
action = deployment.DeployStackAction(1, 'overcloud')
expected = actions.Result(
error="Error during stack update: ERROR: Oops\n")
self.assertEqual(expected, action.run(mock_ctx))
class OvercloudRcActionTestCase(base.TestCase):
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_no_stack(self, mock_get_orchestration, mock_get_object):
mock_ctx = mock.MagicMock()
not_found = heat_exc.HTTPNotFound()
mock_get_orchestration.return_value.stacks.get.side_effect = not_found
action = deployment.OvercloudRcAction("overcast")
result = action.run(mock_ctx)
self.assertEqual(result.error, (
"The Heat stack overcast could not be found. Make sure you have "
"deployed before calling this action."
))
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_no_env(self, mock_get_orchestration, mock_get_object):
mock_ctx = mock.MagicMock()
mock_get_object.return_value.get_object.side_effect = (
swiftexceptions.ClientException("overcast"))
action = deployment.OvercloudRcAction("overcast")
result = action.run(mock_ctx)
self.assertEqual(result.error, "Error retrieving environment for plan "
"overcast: overcast")
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_no_password(self, mock_get_orchestration, mock_get_object):
mock_ctx = mock.MagicMock()
mock_get_object.return_value.get_object.return_value = (
{}, "version: 1.0")
action = deployment.OvercloudRcAction("overcast")
result = action.run(mock_ctx)
self.assertEqual(
result.error,
"Unable to find the AdminPassword in the plan environment.")
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')
@mock.patch('tripleo_common.utils.overcloudrc.create_overcloudrc')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_success(self, mock_get_orchestration, mock_create_overcloudrc,
mock_get_object):
mock_ctx = mock.MagicMock()
mock_env = """
version: 1.0
template: overcloud.yaml
environments:
- path: overcloud-resource-registry-puppet.yaml
- path: environments/services/sahara.yaml
parameter_defaults:
BlockStorageCount: 42
OvercloudControlFlavor: yummy
passwords:
AdminPassword: SUPERSECUREPASSWORD
"""
mock_get_object.return_value.get_object.return_value = ({}, mock_env)
mock_create_overcloudrc.return_value = {
"overcloudrc": "fake overcloudrc"
}
action = deployment.OvercloudRcAction("overcast")
result = action.run(mock_ctx)
self.assertEqual(result, {"overcloudrc": "fake overcloudrc"})
class DeploymentFailuresActionTest(base.TestCase):
def setUp(self):
super(DeploymentFailuresActionTest, self).setUp()
self.plan = 'overcloud'
self.ctx = mock.MagicMock()
@mock.patch('tripleo_common.actions.deployment.yaml.safe_load')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.actions.deployment.open')
def test_get_deployment_failures(
self, mock_open, mock_obj_client, mock_yaml_load):
test_result = dict(host0=["a", "b", "c"])
mock_read = mock.MagicMock()
mock_read.read.return_value = json.dumps(test_result)
mock_open.return_value = mock_read
action = deployment.DeploymentFailuresAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result['failures'], test_result)
@mock.patch('tripleo_common.actions.deployment.yaml.safe_load')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.actions.deployment.open')
def test_get_deployment_failures_no_container(
self, mock_open, mock_obj_client, mock_yaml_load):
test_result = dict(
failures={},
message='Swift container overcloud-messages not found')
swift = mock.MagicMock()
swift.get_object.side_effect = swiftexceptions.ClientException("404")
mock_obj_client.return_value = swift
action = deployment.DeploymentFailuresAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result, test_result)
@mock.patch('tripleo_common.actions.deployment.yaml.safe_load')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.actions.deployment.open')
def test_get_deployment_failures_no_execution(
self, mock_open, mock_obj_client, mock_yaml_load):
test_result = dict(
failures={},
message='Execution not found in deployment_status.yaml')
mock_yaml_load.side_effect = KeyError()
action = deployment.DeploymentFailuresAction(self.plan)
result = action.run(self.ctx)
self.assertEqual(result, test_result)
@mock.patch('tripleo_common.actions.deployment.yaml.safe_load')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch('tripleo_common.actions.deployment.open')
def test_get_deployment_failures_no_file(
self, mock_open, mock_obj_client, mock_yaml_load):
mock_open.side_effect = IOError()
action = deployment.DeploymentFailuresAction(self.plan)
result = action.run(self.ctx)
self.assertTrue(result['message'].startswith(
"Ansible errors file not found at"))
self.assertEqual({}, result['failures'])