Add a user confirmation to node delete

Node delete is a destructive action and we should prompt a user to
confirm they want to do this action. We also added a --yes parameter to
skip this confirmation prompt.

Change-Id: If4a82977e2351fd3a9e0241eee60cb3719ef43c0
This commit is contained in:
Alex Schultz 2019-05-20 10:33:39 -06:00
parent 869832a635
commit 2b3b7f1e1f
3 changed files with 40 additions and 4 deletions

View File

@ -0,0 +1,6 @@
---
features:
- |
Added a y/n prompt to the node delete command to prevent a user from
accidently executing a node delete action since it's a destructive
action.

View File

@ -20,6 +20,7 @@ import mock
import os import os
import tempfile import tempfile
from osc_lib import exceptions as oscexc
from osc_lib.tests import utils as test_utils from osc_lib.tests import utils as test_utils
import yaml import yaml
@ -56,7 +57,7 @@ class TestDeleteNode(fakes.TestDeleteNode):
# probably be fixed so that it can pass with that. # probably be fixed so that it can pass with that.
def test_node_delete(self): def test_node_delete(self):
argslist = ['instance1', 'instance2', '--templates', argslist = ['instance1', 'instance2', '--templates',
'--stack', 'overcast', '--timeout', '90'] '--stack', 'overcast', '--timeout', '90', '--yes']
verifylist = [ verifylist = [
('stack', 'overcast'), ('stack', 'overcast'),
('nodes', ['instance1', 'instance2']) ('nodes', ['instance1', 'instance2'])
@ -82,9 +83,24 @@ class TestDeleteNode(fakes.TestDeleteNode):
'timeout': 90 'timeout': 90
}) })
@mock.patch('tripleoclient.utils.prompt_user_for_confirmation',
return_value=False)
def test_node_delete_no_confirm(self, confirm_mock):
argslist = ['instance1', 'instance2', '--templates',
'--stack', 'overcast', '--timeout', '90']
verifylist = [
('stack', 'overcast'),
('nodes', ['instance1', 'instance2'])
]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.assertRaises(oscexc.CommandError,
self.cmd.take_action,
parsed_args)
def test_node_wrong_stack(self): def test_node_wrong_stack(self):
argslist = ['instance1', '--templates', argslist = ['instance1', '--templates',
'--stack', 'overcast'] '--stack', 'overcast', '--yes']
verifylist = [ verifylist = [
('stack', 'overcast'), ('stack', 'overcast'),
('nodes', ['instance1', ]) ('nodes', ['instance1', ])
@ -102,7 +118,7 @@ class TestDeleteNode(fakes.TestDeleteNode):
def test_node_delete_without_stack(self): def test_node_delete_without_stack(self):
arglist = ['instance1', ] arglist = ['instance1', '--yes']
verifylist = [ verifylist = [
('stack', 'overcloud'), ('stack', 'overcloud'),
@ -130,7 +146,7 @@ class TestDeleteNode(fakes.TestDeleteNode):
def test_node_delete_wrong_instance(self): def test_node_delete_wrong_instance(self):
argslist = ['wrong_instance', '--templates', argslist = ['wrong_instance', '--templates',
'--stack', 'overcloud'] '--stack', 'overcloud', '--yes']
verifylist = [ verifylist = [
('stack', 'overcloud'), ('stack', 'overcloud'),
('nodes', ['wrong_instance']), ('nodes', ['wrong_instance']),

View File

@ -17,6 +17,7 @@ import argparse
import logging import logging
import os import os
from osc_lib import exceptions as oscexc
from osc_lib.i18n import _ from osc_lib.i18n import _
from osc_lib import utils from osc_lib import utils
import yaml import yaml
@ -69,11 +70,24 @@ class DeleteNode(command.Command):
"Keep in mind that due to keystone session duration " "Keep in mind that due to keystone session duration "
"that timeout has an upper bound of 4 hours ") "that timeout has an upper bound of 4 hours ")
) )
parser.add_argument('-y', '--yes',
help=_('Skip yes/no prompt (assume yes).'),
default=False,
action="store_true")
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args) self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager clients = self.app.client_manager
if not parsed_args.yes:
confirm = oooutils.prompt_user_for_confirmation(
message=_("Are you sure you want to delete these overcloud "
"nodes [y/N]? "),
logger=self.log)
if not confirm:
raise oscexc.CommandError("Action not confirmed, exiting.")
orchestration_client = clients.orchestration orchestration_client = clients.orchestration
stack = oooutils.get_stack(orchestration_client, parsed_args.stack) stack = oooutils.get_stack(orchestration_client, parsed_args.stack)