Skip to content

Commit 25cd117

Browse files
SeanMooneystephenfin
authored andcommitted
Require confirmation to reset server state.
This change updates the server set state command to require confirmation before it is applied. The same pattern as project clean is used and a new --auto-approve flag is added to allow skipping the prompt. Operators often use reset state in cases that are incorrect this change updates the warning to be more explicit about when and when not to use it. Change-Id: Iab14739cf6043ad45ad49edff0580e81d75af2fd
1 parent 7ecdb69 commit 25cd117

3 files changed

Lines changed: 96 additions & 4 deletions

File tree

openstackclient/compute/v2/server.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4462,15 +4462,27 @@ def get_parser(self, prog_name):
44624462
'(repeat option to set multiple properties)'
44634463
),
44644464
)
4465+
parser.add_argument(
4466+
'--auto-approve',
4467+
action='store_true',
4468+
help=_(
4469+
"Allow server state override without asking for confirmation"
4470+
),
4471+
)
44654472
parser.add_argument(
44664473
'--state',
44674474
metavar='<state>',
44684475
choices=['active', 'error'],
44694476
help=_(
4470-
'New server state '
4471-
'**WARNING** This can result in instances that are no longer '
4472-
'usable and should be used with caution '
4473-
'(admin only)'
4477+
'New server state.'
4478+
'**WARNING** Resetting the state is intended to work around '
4479+
'servers stuck in an intermediate state, such as deleting. '
4480+
'If the server is in an error state then this is almost '
4481+
'never the correct command to run and you should prefer hard '
4482+
'reboot where possible. In particular, if the server is in '
4483+
'an error state due to a move operation, setting the state '
4484+
'can result in instances that are no longer usable. Proceed '
4485+
'with caution. (admin only)'
44744486
),
44754487
)
44764488
parser.add_argument(
@@ -4505,6 +4517,20 @@ def get_parser(self, prog_name):
45054517
)
45064518
return parser
45074519

4520+
@staticmethod
4521+
def ask_user_yesno(msg):
4522+
"""Ask user Y/N question
4523+
4524+
:param str msg: question text
4525+
:return bool: User choice
4526+
"""
4527+
while True:
4528+
answer = getpass.getpass('{} [{}]: '.format(msg, 'y/n'))
4529+
if answer in ('y', 'Y', 'yes'):
4530+
return True
4531+
elif answer in ('n', 'N', 'no'):
4532+
return False
4533+
45084534
def take_action(self, parsed_args):
45094535
compute_client = self.app.client_manager.compute
45104536
server = compute_client.find_server(
@@ -4555,6 +4581,17 @@ def take_action(self, parsed_args):
45554581
)
45564582

45574583
if parsed_args.state:
4584+
if not parsed_args.auto_approve:
4585+
if not self.ask_user_yesno(
4586+
_(
4587+
"Resetting the server state can make it much harder "
4588+
"to recover a server from an error state. If the "
4589+
"server is in error status due to a failed move "
4590+
"operation then this is likely not the correct "
4591+
"approach to fix the problem. Do you wish to continue?"
4592+
)
4593+
):
4594+
return
45584595
compute_client.reset_server_state(server, state=parsed_args.state)
45594596

45604597
if parsed_args.root_password:

openstackclient/tests/unit/compute/v2/test_server.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7935,6 +7935,7 @@ def test_server_set_with_state(self):
79357935
arglist = [
79367936
'--state',
79377937
'active',
7938+
'--auto-approve',
79387939
self.server.id,
79397940
]
79407941
verifylist = [
@@ -7955,6 +7956,54 @@ def test_server_set_with_state(self):
79557956
self.compute_client.add_tag_to_server.assert_not_called()
79567957
self.assertIsNone(result)
79577958

7959+
def test_server_set_with_state_prompt_y(self):
7960+
arglist = [
7961+
'--state',
7962+
'active',
7963+
self.server.id,
7964+
]
7965+
verifylist = [
7966+
('state', 'active'),
7967+
('server', self.server.id),
7968+
]
7969+
7970+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
7971+
with mock.patch('getpass.getpass', return_value='y'):
7972+
result = self.cmd.take_action(parsed_args)
7973+
7974+
self.compute_client.reset_server_state.assert_called_once_with(
7975+
self.server, state='active'
7976+
)
7977+
self.compute_client.update_server.assert_not_called()
7978+
self.compute_client.set_server_metadata.assert_not_called()
7979+
self.compute_client.change_server_password.assert_not_called()
7980+
self.compute_client.clear_server_password.assert_not_called()
7981+
self.compute_client.add_tag_to_server.assert_not_called()
7982+
self.assertIsNone(result)
7983+
7984+
def test_server_set_with_state_prompt_n(self):
7985+
arglist = [
7986+
'--state',
7987+
'active',
7988+
self.server.id,
7989+
]
7990+
verifylist = [
7991+
('state', 'active'),
7992+
('server', self.server.id),
7993+
]
7994+
7995+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
7996+
with mock.patch('getpass.getpass', return_value='n'):
7997+
result = self.cmd.take_action(parsed_args)
7998+
7999+
self.compute_client.reset_server_state.assert_not_called()
8000+
self.compute_client.update_server.assert_not_called()
8001+
self.compute_client.set_server_metadata.assert_not_called()
8002+
self.compute_client.change_server_password.assert_not_called()
8003+
self.compute_client.clear_server_password.assert_not_called()
8004+
self.compute_client.add_tag_to_server.assert_not_called()
8005+
self.assertIsNone(result)
8006+
79588007
def test_server_set_with_invalid_state(self):
79598008
arglist = [
79608009
'--state',
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
upgrade:
3+
- |
4+
The ``openstack server set`` command has been extended with a new
5+
parameter ``--auto-approve`` and the existing ``--state`` parameter
6+
has been modified to require confirmation before resetting the state.

0 commit comments

Comments
 (0)