Add support for an answers file

Adding an --answers-file parameter to overcloud deploy and
overcloud update that takes a YAML file:

   templates: ~/templates
   environments:
     - ~/test-env1.yaml
     - ~/test-env2.yaml
     - ~/test-env3.yaml

Change-Id: I623143924951d02fcc9179c1da7705b4b7b551b7
This commit is contained in:
Lennart Regebro 2015-11-24 14:48:43 +01:00
parent c838918d81
commit 0821856178
5 changed files with 153 additions and 2 deletions

View File

@ -43,6 +43,7 @@ Deploy an overcloud stack
[--reg-org REG_ORG] [--reg-force]
[--reg-sat-url REG_SAT_URL]
[--reg-activation-key REG_ACTIVATION_KEY]
[--answers-file <ANSWERS FILE>]
.. option:: --stack <stack_name>
@ -193,3 +194,14 @@ Deploy an overcloud stack
.. option:: --reg-activation-key <key>
Activation key to use for registration.
.. option:: --answers-file <file>
Point to a file that specifies a templates directory and a list
of environment files in YAML format::
templates: ~/templates
environments:
- ~/test-env1.yaml
- ~/test-env2.yaml
- ~/test-env3.yaml

View File

@ -18,6 +18,7 @@ import json
import os
import six
import tempfile
import yaml
import mock
@ -814,3 +815,67 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
self.assertFalse(mock_oc_endpoint.called)
self.assertFalse(mock_create_ocrc.called)
self.assertFalse(mock_create_tempest_deployer_input.called)
@mock.patch('tripleoclient.utils.check_nodes_count',
autospec=True)
@mock.patch('tripleoclient.utils.create_tempest_deployer_input',
autospec=True)
@mock.patch('tripleoclient.utils.create_overcloudrc', autospec=True)
@mock.patch('tripleoclient.utils.get_overcloud_endpoint', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_heat_deploy', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'set_overcloud_passwords', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_pre_heat_deploy', autospec=True)
def test_answers_file(self,
mock_pre_deploy,
mock_set_overcloud_passwords,
mock_heat_deploy,
mock_oc_endpoint,
mock_create_ocrc,
mock_create_tempest_deployer_input,
mock_check_nodes_count):
clients = self.app.client_manager
network_client = clients.network
network_client.stacks.get.return_value = None
net = network_client.api.find_attr('networks', 'ctlplane')
net.configure_mock(__getitem__=lambda x, y: 'testnet')
with tempfile.NamedTemporaryFile(mode="w+t") as answerfile:
yaml.dump(
{'templates': '/dev/null',
'environments': ['/tmp/foo.yaml']
},
answerfile
)
answerfile.flush()
arglist = ['--answers-file', answerfile.name,
'--environment-file', '/tmp/environment.yaml',
'--block-storage-scale', '3']
verifylist = [
('answers_file', answerfile.name),
('environment_files', ['/tmp/environment.yaml']),
('block_storage_scale', 3)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertTrue(result)
self.assertTrue(mock_heat_deploy.called)
self.assertTrue(mock_oc_endpoint.called)
self.assertTrue(mock_create_ocrc.called)
self.assertTrue(mock_create_tempest_deployer_input.called)
# Check that Heat was called with correct parameters:
call_args = mock_heat_deploy.call_args[0]
self.assertEqual(call_args[3],
'/dev/null/overcloud-without-mergepy.yaml')
self.assertIn('/tmp/foo.yaml', call_args[5])
self.assertIn('/tmp/environment.yaml', call_args[5])
foo_index = call_args[5].index('/tmp/foo.yaml')
env_index = call_args[5].index('/tmp/environment.yaml')
self.assertGreater(env_index, foo_index)
mock_create_tempest_deployer_input.assert_called_with()

View File

@ -14,6 +14,7 @@
#
import mock
import tempfile
from tripleoclient.tests.v1.overcloud_update import fakes
from tripleoclient.v1 import overcloud_update
@ -35,9 +36,41 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
verifylist = [
('stack', 'overcloud'),
('interactive', True),
('templates', '/usr/share/openstack-tripleo-heat-templates/')
]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.cmd.take_action(parsed_args)
update_manager.get_status.called_once()
update_manager.update.called_once()
update_manager.do_interactive_update.called_once()
@mock.patch('tripleo_common.update.PackageUpdateManager')
def test_update_answerfile(self, update_manager):
answers = ("templates: {templates}\n"
"environments:\n"
" - {environment}\n")
with tempfile.NamedTemporaryFile(mode="w+t") as answerfile:
answerfile.write(answers.format(
templates='/dev/null',
environment='/dev/null'))
answerfile.flush()
update_manager.return_value.get_status.return_value = (
'UPDATE_COMPLETE', {})
argslist = ['overcloud', '-i', '--answers-file', answerfile.name]
verifylist = [
('stack', 'overcloud'),
('interactive', True),
('answers_file', answerfile.name)
]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.cmd.take_action(parsed_args)
update_manager.get_status.called_once()
update_manager.update.called_once()
update_manager.do_interactive_update.called_once()
called_args = update_manager.call_args[1]
self.assertEqual(called_args['tht_dir'], '/dev/null')
self.assertEqual(called_args['environment_files'], ['/dev/null'])

View File

@ -26,6 +26,7 @@ import sys
import tempfile
import time
import uuid
import yaml
from cliff import command
from heatclient.common import event_utils
@ -97,6 +98,18 @@ class DeployOvercloud(command.Command):
timestamp = int(time.time())
parameters['DeployIdentifier'] = timestamp
# Update parameters from answers file:
if args.answers_file is not None:
with open(args.answers_file, 'r') as answers_file:
answers = yaml.load(answers_file)
if args.templates is None:
args.templates = answers['templates']
if 'environments' in answers:
if args.environment_files is not None:
answers['environments'].extend(args.environment_files)
args.environment_files = answers['environments']
param_args = (
('NeutronPublicInterface', 'neutron_public_interface'),
('NeutronBridgeMappings', 'neutron_bridge_mappings'),
@ -492,6 +505,10 @@ class DeployOvercloud(command.Command):
compute_client.flavors.create('m1.demo', 512, 1, 10, 'auto')
def _validate_args(self, parsed_args):
if parsed_args.templates is None and parsed_args.answers_file is None:
raise oscexc.CommandError(
"You must specify either --templates or --answers-file")
network_type = parsed_args.neutron_network_type
tunnel_types = parsed_args.neutron_tunnel_types
tunnel_disabled = parsed_args.neutron_disable_tunneling
@ -770,7 +787,6 @@ class DeployOvercloud(command.Command):
parser.add_argument(
'--templates', nargs='?', const=constants.TRIPLEO_HEAT_TEMPLATES,
help=_("The directory containing the Heat templates to deploy"),
required=True
)
parser.add_argument('--stack',
help=_("Stack name to create or update"),
@ -938,6 +954,10 @@ class DeployOvercloud(command.Command):
default='',
help=_('Activation key to use for registration.')
)
parser.add_argument(
'--answers-file',
help=_('Path to a YAML file with arguments and parameters.')
)
return parser

View File

@ -14,8 +14,10 @@
#
import logging
import yaml
from cliff import command
from openstackclient.common import exceptions as oscexc
from openstackclient.common import utils
from openstackclient.i18n import _
from tripleo_common import update
@ -38,7 +40,6 @@ class UpdateOvercloud(command.Command):
parser.add_argument(
'--templates', nargs='?', const=constants.TRIPLEO_HEAT_TEMPLATES,
help=_("The directory containing the Heat templates to deploy"),
required=True
)
parser.add_argument('-i', '--interactive', dest='interactive',
action='store_true')
@ -51,9 +52,29 @@ class UpdateOvercloud(command.Command):
'or heat stack-update command. (Can be specified more than '
'once.)')
)
parser.add_argument(
'--answers-file',
help=_('Path to a YAML file with arguments and parameters.')
)
return parser
def take_action(self, parsed_args):
if parsed_args.templates is None and parsed_args.answers_file is None:
raise oscexc.CommandError(
"You must specify either --templates or --answers-file")
if parsed_args.answers_file is not None:
with open(parsed_args.answers_file, 'r') as answers_file:
answers = yaml.load(answers_file)
if parsed_args.templates is None:
parsed_args.templates = answers['templates']
if 'environments' in answers:
if parsed_args.environment_files is not None:
answers.environments.extend(
parsed_args.environment_files)
parsed_args.environment_files = answers['environments']
self.log.debug("take_action(%s)" % parsed_args)
osc_plugin = self.app.client_manager.tripleoclient