Add --force-stack-create

--force-stack-create will force the stack action to be CREATE when
UPDATE would otherwise be used. This is useful in scenarios when you had
a successful deployment, but did something wrong, so you want to froce a
recreate, for example when using a completely wrong roles file.

--force-stack-create and --force-stack-update are added to an argparse
mutually exclusive group so that both can't be specified at the same
time.

Also adds unit tests around the stack action handling and adds missing
unit tests for --force-stack-update.

Change-Id: Ia3edebfe2271710abc4b84ce090b9e7ddf3c9d88
This commit is contained in:
James Slagle 2018-07-20 08:43:27 -04:00
parent 9f41f9556e
commit 26d0ed218e
2 changed files with 97 additions and 18 deletions

View File

@ -715,3 +715,63 @@ class TestDeployUndercloud(TestPluginV1):
'--standalone'], [])
self.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args)
@mock.patch('os.path.isfile', return_value=False)
def test_set_stack_action_default_create(self, mock_isfile):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone'], [])
self.cmd._set_stack_action(parsed_args)
self.assertEqual('CREATE', self.cmd.stack_action)
@mock.patch('os.path.isfile', return_value=True)
def test_set_stack_action_default_update(self, mock_isfile):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone'], [])
self.cmd._set_stack_action(parsed_args)
self.assertEqual('UPDATE', self.cmd.stack_action)
@mock.patch('os.path.isfile', return_value=False)
def test_set_stack_action_force_update(self, mock_isfile):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone',
'--force-stack-update'], [])
self.cmd._set_stack_action(parsed_args)
self.assertEqual('UPDATE', self.cmd.stack_action)
@mock.patch('os.path.isfile', return_value=True)
def test_set_stack_action_force_create(self, mock_isfile):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone',
'--force-stack-create'], [])
self.cmd._set_stack_action(parsed_args)
self.assertEqual('CREATE', self.cmd.stack_action)
@mock.patch('os.path.isfile', return_value=True)
def test_set_stack_action_mutually_exclusive(self, mock_isfile):
self.assertRaises(
SystemExit,
self.check_parser,
self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone',
'--force-stack-create',
'--force-stack-update'], [])

View File

@ -137,6 +137,18 @@ class Deploy(command.Command):
else:
self.roles_file = os.path.join(templates_dir, file_name)
def _set_stack_action(self, parsed_args):
"""Set the stack action for deployment"""
# Prepare the heat stack action we want to start deployment with
if ((os.path.isfile(self.stack_update_mark) or
parsed_args.force_stack_update) and
not parsed_args.force_stack_create):
self.stack_action = 'UPDATE'
self.log.warning(
_('The heat stack {0} action is {1}').format(
parsed_args.stack, self.stack_action))
def _get_roles_data(self):
"""Load the roles data for deployment"""
# only load once
@ -775,16 +787,6 @@ class Deploy(command.Command):
help=_("Name for the ephemeral (one-time create "
"and forget) heat stack."),
default='standalone')
parser.add_argument('--force-stack-update',
dest='force_stack_update',
action='store_true',
default=False,
help=_("Do a virtual update of the ephemeral "
"heat stack (it cannot take real updates). "
"New or failed deployments "
"always have the stack_action=CREATE. This "
"option enforces stack_action=UPDATE."),
)
parser.add_argument('--output-dir',
dest='output_dir',
help=_("Directory to output state, processed heat "
@ -918,6 +920,30 @@ class Deploy(command.Command):
'openstack stack list\n '
'where 8006 is the port specified by --heat-api-port.')
)
stack_action_group = parser.add_mutually_exclusive_group()
stack_action_group.add_argument(
'--force-stack-update',
dest='force_stack_update',
action='store_true',
default=False,
help=_("Do a virtual update of the ephemeral "
"heat stack (it cannot take real updates). "
"New or failed deployments "
"always have the stack_action=CREATE. This "
"option enforces stack_action=UPDATE."),
)
stack_action_group.add_argument(
'--force-stack-create',
dest='force_stack_create',
action='store_true',
default=False,
help=_("Do a virtual create of the ephemeral "
"heat stack. New or failed deployments "
"always have the stack_action=CREATE. This "
"option enforces stack_action=CREATE."),
)
return parser
def _process_hieradata_overrides(self, override_file=None,
@ -1037,14 +1063,7 @@ class Deploy(command.Command):
constants.STANDALONE_EPHEMERAL_STACK_VSTATE,
mark_uuid)
# Prepare the heat stack action we want to start deployment with
if (os.path.isfile(self.stack_update_mark) or
parsed_args.force_stack_update):
self.stack_action = 'UPDATE'
self.log.warning(
_('The heat stack {0} action is {1}').format(
parsed_args.stack, self.stack_action))
self._set_stack_action(parsed_args)
# Launch heat.
orchestration_client = self._launch_heat(parsed_args)