Merge "Conductor changes target_power_state before starting work"
This commit is contained in:
commit
8a0923c437
@ -261,19 +261,40 @@ class ConductorManager(periodic_task.PeriodicTasks):
|
||||
# instance_uuid needs to be unset, and handle it.
|
||||
if 'instance_uuid' in delta:
|
||||
task.driver.power.validate(task)
|
||||
node_obj['power_state'] = \
|
||||
node_obj.power_state = \
|
||||
task.driver.power.get_power_state(task)
|
||||
|
||||
if node_obj['power_state'] != states.POWER_OFF:
|
||||
if node_obj.power_state != states.POWER_OFF:
|
||||
raise exception.NodeInWrongPowerState(
|
||||
node=node_id,
|
||||
pstate=node_obj['power_state'])
|
||||
pstate=node_obj.power_state)
|
||||
|
||||
# update any remaining parameters, then save
|
||||
node_obj.save()
|
||||
|
||||
return node_obj
|
||||
|
||||
def _power_state_error_handler(self, e, node, power_state):
|
||||
"""Set the node's power states if error occurs.
|
||||
|
||||
This hook gets called upon an execption being raised when spawning
|
||||
the worker thread to change the power state of a node.
|
||||
|
||||
:param e: the exception object that was raised.
|
||||
:param node: an Ironic node object.
|
||||
:param power_state: the power state to set on the node.
|
||||
|
||||
"""
|
||||
if isinstance(e, exception.NoFreeConductorWorker):
|
||||
node.power_state = power_state
|
||||
node.target_power_state = states.NOSTATE
|
||||
node.last_error = (_("No free conductor workers available"))
|
||||
node.save()
|
||||
LOG.warning(_LW("No free conductor workers available to perform "
|
||||
"an action on node %(node)s, setting node's "
|
||||
"power state back to %(power_state)s.",
|
||||
{'node': node.uuid, 'power_state': power_state}))
|
||||
|
||||
@messaging.expected_exceptions(exception.InvalidParameterValue,
|
||||
exception.MissingParameterValue,
|
||||
exception.NoFreeConductorWorker,
|
||||
@ -300,6 +321,17 @@ class ConductorManager(periodic_task.PeriodicTasks):
|
||||
|
||||
with task_manager.acquire(context, node_id, shared=False) as task:
|
||||
task.driver.power.validate(task)
|
||||
# Set the target_power_state and clear any last_error, since we're
|
||||
# starting a new operation. This will expose to other processes
|
||||
# and clients that work is in progress.
|
||||
if new_state == states.REBOOT:
|
||||
task.node.target_power_state = states.POWER_ON
|
||||
else:
|
||||
task.node.target_power_state = new_state
|
||||
task.node.last_error = None
|
||||
task.node.save()
|
||||
task.set_spawn_error_hook(self._power_state_error_handler,
|
||||
task.node, task.node.power_state)
|
||||
task.spawn_after(self._spawn_worker, utils.node_power_action,
|
||||
task, new_state)
|
||||
|
||||
|
@ -45,13 +45,13 @@ def node_set_boot_device(task, device, persistent=False):
|
||||
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
def node_power_action(task, state):
|
||||
def node_power_action(task, new_state):
|
||||
"""Change power state or reset for a node.
|
||||
|
||||
Perform the requested power action if the transition is required.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param state: Any power state from ironic.common.states. If the
|
||||
:param new_state: Any power state from ironic.common.states. If the
|
||||
state is 'REBOOT' then a reboot will be attempted, otherwise
|
||||
the node power state is directly set to 'state'.
|
||||
:raises: InvalidParameterValue when the wrong state is specified
|
||||
@ -61,9 +61,9 @@ def node_power_action(task, state):
|
||||
|
||||
"""
|
||||
node = task.node
|
||||
new_state = states.POWER_ON if state == states.REBOOT else state
|
||||
target_state = states.POWER_ON if new_state == states.REBOOT else new_state
|
||||
|
||||
if state != states.REBOOT:
|
||||
if new_state != states.REBOOT:
|
||||
try:
|
||||
curr_state = task.driver.power.get_power_state(task)
|
||||
except Exception as e:
|
||||
@ -96,16 +96,17 @@ def node_power_action(task, state):
|
||||
LOG.warn(_LW("Driver returns ERROR power state for node %s."),
|
||||
node.uuid)
|
||||
|
||||
# Set the target_power_state and clear any last_error, since we're
|
||||
# Set the target_power_state and clear any last_error, if we're
|
||||
# starting a new operation. This will expose to other processes
|
||||
# and clients that work is in progress.
|
||||
node['target_power_state'] = new_state
|
||||
node['last_error'] = None
|
||||
node.save()
|
||||
if node['target_power_state'] != target_state:
|
||||
node['target_power_state'] = target_state
|
||||
node['last_error'] = None
|
||||
node.save()
|
||||
|
||||
# take power action
|
||||
try:
|
||||
if state != states.REBOOT:
|
||||
if new_state != states.REBOOT:
|
||||
task.driver.power.set_power_state(task, new_state)
|
||||
else:
|
||||
task.driver.power.reboot(task)
|
||||
@ -114,13 +115,13 @@ def node_power_action(task, state):
|
||||
node['last_error'] = \
|
||||
_("Failed to change power state to '%(target)s'. "
|
||||
"Error: %(error)s") % {
|
||||
'target': new_state, 'error': e}
|
||||
'target': target_state, 'error': e}
|
||||
else:
|
||||
# success!
|
||||
node['power_state'] = new_state
|
||||
node['power_state'] = target_state
|
||||
LOG.info(_LI('Succesfully set node %(node)s power state to '
|
||||
'%(state)s.'),
|
||||
{'node': node.uuid, 'state': new_state})
|
||||
{'node': node.uuid, 'state': target_state})
|
||||
finally:
|
||||
node['target_power_state'] = states.NOSTATE
|
||||
node.save()
|
||||
|
@ -315,7 +315,7 @@ class ChangeNodePowerStateTestCase(_ServiceSetUpMixin,
|
||||
node.refresh()
|
||||
self.assertEqual(initial_state, node.power_state)
|
||||
self.assertIsNone(node.target_power_state)
|
||||
self.assertIsNone(node.last_error)
|
||||
self.assertIsNotNone(node.last_error)
|
||||
# Verify the picked reservation has been cleared due to full pool.
|
||||
self.assertIsNone(node.reservation)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user