Merge "Add option to protect available nodes from accidental deletion"
This commit is contained in:
commit
5d25189b13
@ -223,7 +223,7 @@ UPDATE_ALLOWED_STATES = (DEPLOYFAIL, INSPECTING, INSPECTFAIL, INSPECTWAIT,
|
|||||||
UNRESCUEFAIL)
|
UNRESCUEFAIL)
|
||||||
"""Transitional states in which we allow updating a node."""
|
"""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."""
|
"""States in which node deletion is allowed."""
|
||||||
|
|
||||||
STABLE_STATES = (ENROLL, MANAGEABLE, AVAILABLE, ACTIVE, ERROR, RESCUE)
|
STABLE_STATES = (ENROLL, MANAGEABLE, AVAILABLE, ACTIVE, ERROR, RESCUE)
|
||||||
|
@ -2269,15 +2269,18 @@ class ConductorManager(base_manager.BaseConductorManager):
|
|||||||
# CLEANFAIL -> MANAGEABLE
|
# CLEANFAIL -> MANAGEABLE
|
||||||
# INSPECTIONFAIL -> MANAGEABLE
|
# INSPECTIONFAIL -> MANAGEABLE
|
||||||
# DEPLOYFAIL -> DELETING
|
# 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
|
if (not node.maintenance
|
||||||
and node.provision_state
|
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 '
|
msg = (_('Can not delete node "%(node)s" while it is in '
|
||||||
'provision state "%(state)s". Valid provision states '
|
'provision state "%(state)s". Valid provision states '
|
||||||
'to perform deletion are: "%(valid_states)s", '
|
'to perform deletion are: "%(valid_states)s", '
|
||||||
'or set the node into maintenance mode') %
|
'or set the node into maintenance mode') %
|
||||||
{'node': node.uuid, 'state': node.provision_state,
|
{'node': node.uuid, 'state': node.provision_state,
|
||||||
'valid_states': states.DELETE_ALLOWED_STATES})
|
'valid_states': delete_allowed_states})
|
||||||
raise exception.InvalidState(msg)
|
raise exception.InvalidState(msg)
|
||||||
if node.console_enabled:
|
if node.console_enabled:
|
||||||
notify_utils.emit_console_notification(
|
notify_utils.emit_console_notification(
|
||||||
|
@ -199,6 +199,10 @@ opts = [
|
|||||||
'255 characters and is case insensitive. This '
|
'255 characters and is case insensitive. This '
|
||||||
'conductor will only manage nodes with a matching '
|
'conductor will only manage nodes with a matching '
|
||||||
'"conductor_group" field set on the node.')),
|
'"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.')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -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.
|
@ -5090,6 +5090,22 @@ class DestroyNodeTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
|
|||||||
node.refresh()
|
node.refresh()
|
||||||
self.assertIsNone(node.reservation)
|
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):
|
def test_destroy_node_protected(self):
|
||||||
self._start_service()
|
self._start_service()
|
||||||
node = obj_utils.create_test_node(self.context,
|
node = obj_utils.create_test_node(self.context,
|
||||||
|
Loading…
Reference in New Issue
Block a user