Add option to protect available nodes from accidental deletion

Ironic allows to delete nodes which are in state 'available'.
As bringing nodes into 'available' comes at an operational cost
(i.e. enroll, inspect, clean, ...), this patch proposes a new
option 'allow_deleting_available_nodes' to support the protection
of available nodes against accidental removal.

Change-Id: I08d31b5ddbad626811c971389e634a450aeaf066
Story: #2005060
Task: #29604
This commit is contained in:
Arne Wiebalck 2019-02-21 17:25:54 +00:00
parent ccf2bb1ea1
commit 885ddb4362
5 changed files with 38 additions and 3 deletions

View File

@ -223,7 +223,7 @@ UPDATE_ALLOWED_STATES = (DEPLOYFAIL, INSPECTING, INSPECTFAIL, INSPECTWAIT,
UNRESCUEFAIL)
"""Transitional states in which we allow updating a node."""
DELETE_ALLOWED_STATES = (AVAILABLE, MANAGEABLE, ENROLL, ADOPTFAIL)
DELETE_ALLOWED_STATES = (MANAGEABLE, ENROLL, ADOPTFAIL)
"""States in which node deletion is allowed."""
STABLE_STATES = (ENROLL, MANAGEABLE, AVAILABLE, ACTIVE, ERROR, RESCUE)

View File

@ -2267,15 +2267,18 @@ class ConductorManager(base_manager.BaseConductorManager):
# CLEANFAIL -> MANAGEABLE
# INSPECTIONFAIL -> MANAGEABLE
# DEPLOYFAIL -> DELETING
delete_allowed_states = states.DELETE_ALLOWED_STATES
if CONF.conductor.allow_deleting_available_nodes:
delete_allowed_states += (states.AVAILABLE,)
if (not node.maintenance
and node.provision_state
not in states.DELETE_ALLOWED_STATES):
not in delete_allowed_states):
msg = (_('Can not delete node "%(node)s" while it is in '
'provision state "%(state)s". Valid provision states '
'to perform deletion are: "%(valid_states)s", '
'or set the node into maintenance mode') %
{'node': node.uuid, 'state': node.provision_state,
'valid_states': states.DELETE_ALLOWED_STATES})
'valid_states': delete_allowed_states})
raise exception.InvalidState(msg)
if node.console_enabled:
notify_utils.emit_console_notification(

View File

@ -194,6 +194,10 @@ opts = [
'255 characters and is case insensitive. This '
'conductor will only manage nodes with a matching '
'"conductor_group" field set on the node.')),
cfg.BoolOpt('allow_deleting_available_nodes',
default=True,
help=_('Allow deleting nodes which are in state '
'\'available\'. Defaults to True.')),
]

View File

@ -0,0 +1,12 @@
---
features:
- Adds option 'allow_deleting_available_nodes' to control whether nodes in
state 'available' should be deletable (which is and stays the default).
Setting this option to False will remove 'available' from the list of
states in which nodes can be deleted from ironic. It hence provides
protection against accidental removal of nodes which are ready for
allocation (and is meant as a safeguard for the operational effort to
bring nodes into this state). For backwards compatibility reasons, the
default value for this option is True. The other states in which nodes
can be deleted from ironic ('manageable', 'enroll', and 'adoptfail')
remain unchanged.

View File

@ -5068,6 +5068,22 @@ class DestroyNodeTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
node.refresh()
self.assertIsNone(node.reservation)
def test_destroy_node_protected_provision_state_available(self):
CONF.set_override('allow_deleting_available_nodes',
False, group='conductor')
self._start_service()
node = obj_utils.create_test_node(self.context,
provision_state=states.AVAILABLE)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.destroy_node,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InvalidState, exc.exc_info[0])
# Verify reservation was released.
node.refresh()
self.assertIsNone(node.reservation)
def test_destroy_node_protected(self):
self._start_service()
node = obj_utils.create_test_node(self.context,