Adds support for soft power operations to 'ilo' power interface
Adds support for soft power-off and soft reboot operations to 'ilo' power interface. Change-Id: I38571f49d21007c00ddda28651569333e0823cad Story: #2003727 Task: #26394
This commit is contained in:
parent
1dabeb28f8
commit
f4fc39de7c
@ -64,6 +64,10 @@ opts = [
|
|||||||
'password.')),
|
'password.')),
|
||||||
cfg.IntOpt('power_retry',
|
cfg.IntOpt('power_retry',
|
||||||
default=6,
|
default=6,
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason=_('This configuration option is replaced '
|
||||||
|
'by [conductor] soft_power_off_timeout, '
|
||||||
|
'please use that instead.'),
|
||||||
help=_('Number of times a power operation needs to be '
|
help=_('Number of times a power operation needs to be '
|
||||||
'retried')),
|
'retried')),
|
||||||
cfg.IntOpt('power_wait',
|
cfg.IntOpt('power_wait',
|
||||||
|
@ -96,6 +96,27 @@ BOOT_MODE_GENERIC_TO_ILO = {'bios': 'legacy', 'uefi': 'uefi'}
|
|||||||
BOOT_MODE_ILO_TO_GENERIC = dict(
|
BOOT_MODE_ILO_TO_GENERIC = dict(
|
||||||
(v, k) for (k, v) in BOOT_MODE_GENERIC_TO_ILO.items())
|
(v, k) for (k, v) in BOOT_MODE_GENERIC_TO_ILO.items())
|
||||||
|
|
||||||
|
POST_NULL_STATE = 'Null'
|
||||||
|
""" Node is in Null post state."""
|
||||||
|
|
||||||
|
POST_UNKNOWN_STATE = "Unknown"
|
||||||
|
""" Node is in Unknown post state."""
|
||||||
|
|
||||||
|
POST_RESET_STATE = "Reset"
|
||||||
|
""" Node is in Reset post state."""
|
||||||
|
|
||||||
|
POST_POWEROFF_STATE = "PowerOff"
|
||||||
|
""" Node is in PowerOff post state."""
|
||||||
|
|
||||||
|
POST_INPOST_STATE = "InPost"
|
||||||
|
""" Node is in InPost post state."""
|
||||||
|
|
||||||
|
POST_INPOSTDISCOVERY_STATE = "InPostDiscoveryComplete"
|
||||||
|
""" Node is in InPostDiscoveryComplete post state."""
|
||||||
|
|
||||||
|
POST_FINISHEDPOST_STATE = "FinishedPost"
|
||||||
|
""" Node is in FinishedPost post state."""
|
||||||
|
|
||||||
|
|
||||||
def copy_image_to_web_server(source_file_path, destination):
|
def copy_image_to_web_server(source_file_path, destination):
|
||||||
"""Copies the given image to the http web server.
|
"""Copies the given image to the http web server.
|
||||||
@ -785,3 +806,26 @@ def verify_image_checksum(image_location, expected_checksum):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.ImageRefValidationFailed(image_href=image_location,
|
raise exception.ImageRefValidationFailed(image_href=image_location,
|
||||||
reason=msg)
|
reason=msg)
|
||||||
|
|
||||||
|
|
||||||
|
def get_server_post_state(node):
|
||||||
|
"""Get the current state of system POST.
|
||||||
|
|
||||||
|
:param node: an ironic node object.
|
||||||
|
:returns: POST state of the server. The valida states are:-
|
||||||
|
null, Unknown, Reset, PowerOff, InPost, InPostDiscoveryComplete
|
||||||
|
and FinishedPost.
|
||||||
|
:raises: IloOperationError on an error from IloClient library.
|
||||||
|
:raises: IloOperationNotSupported if retrieving post state is not
|
||||||
|
supported on the server.
|
||||||
|
"""
|
||||||
|
ilo_object = get_ilo_object(node)
|
||||||
|
operation = _("Get server post state for node %s.") % node.uuid
|
||||||
|
try:
|
||||||
|
return ilo_object.get_host_post_state()
|
||||||
|
except ilo_error.IloCommandNotSupportedError as ilo_exception:
|
||||||
|
raise exception.IloOperationNotSupported(operation=operation,
|
||||||
|
error=ilo_exception)
|
||||||
|
except ilo_error.IloError as ilo_exception:
|
||||||
|
raise exception.IloOperationError(operation=operation,
|
||||||
|
error=ilo_exception)
|
||||||
|
@ -94,46 +94,113 @@ def _get_power_state(node):
|
|||||||
return states.ERROR
|
return states.ERROR
|
||||||
|
|
||||||
|
|
||||||
def _wait_for_state_change(node, target_state):
|
def _wait_for_state_change(node, target_state,
|
||||||
"""Wait for the power state change to get reflected."""
|
is_final_state=True, timeout=None):
|
||||||
|
"""Wait for the power state change to get reflected.
|
||||||
|
|
||||||
|
:param node: The node.
|
||||||
|
:param target_state: target power state of the node.
|
||||||
|
:param is_final_state: True, if the given target state is the final
|
||||||
|
expected power state of the node. Default is True.
|
||||||
|
:param timeout: timeout (in seconds) positive integer (> 0) for any
|
||||||
|
power state. ``None`` indicates default timeout.
|
||||||
|
:returns: time consumed to achieve the power state change.
|
||||||
|
:raises: IloOperationError on an error from IloClient library.
|
||||||
|
:raises: PowerStateFailure if power state failed to change within timeout.
|
||||||
|
"""
|
||||||
state = [None]
|
state = [None]
|
||||||
retries = [0]
|
retries = [0]
|
||||||
|
force_legacy_behavior = False
|
||||||
|
interval = CONF.ilo.power_wait
|
||||||
|
if timeout:
|
||||||
|
max_retry = int(timeout / interval)
|
||||||
|
elif CONF.ilo.power_retry:
|
||||||
|
# Do not use post state to track power state as its too small
|
||||||
|
# timeout value to track post state when server is powered on.
|
||||||
|
# The total timeout value would be 12 secs with default values
|
||||||
|
# of configs ilo.power_retry and ilo.power_wait.
|
||||||
|
# Use older ways of get_power_state() to track the power state
|
||||||
|
# changes, instead.
|
||||||
|
force_legacy_behavior = True
|
||||||
|
max_retry = CONF.ilo.power_retry
|
||||||
|
else:
|
||||||
|
# Since we are going to track server post state, we are not using
|
||||||
|
# CONF.conductor.power_state_change_timeout as its default value
|
||||||
|
# is too short for bare metal to reach 'finished post' state
|
||||||
|
# during 'power on' operation. It could lead to deploy failures
|
||||||
|
# with default ironic configuration.
|
||||||
|
# Use conductor.soft_power_off_timeout, instead.
|
||||||
|
max_retry = int(CONF.conductor.soft_power_off_timeout / interval)
|
||||||
|
|
||||||
|
state_to_check = target_state
|
||||||
|
use_post_state = False
|
||||||
|
if not force_legacy_behavior and _can_get_server_post_state(node):
|
||||||
|
use_post_state = True
|
||||||
|
if (target_state in [states.POWER_OFF, states.SOFT_POWER_OFF] or
|
||||||
|
target_state == states.SOFT_REBOOT and not is_final_state):
|
||||||
|
state_to_check = ilo_common.POST_POWEROFF_STATE
|
||||||
|
else:
|
||||||
|
state_to_check = ilo_common.POST_FINISHEDPOST_STATE
|
||||||
|
|
||||||
def _wait(state):
|
def _wait(state):
|
||||||
|
if use_post_state:
|
||||||
state[0] = _get_power_state(node)
|
state[0] = ilo_common.get_server_post_state(node)
|
||||||
|
else:
|
||||||
|
state[0] = _get_power_state(node)
|
||||||
|
|
||||||
# NOTE(rameshg87): For reboot operations, initially the state
|
# NOTE(rameshg87): For reboot operations, initially the state
|
||||||
# will be same as the final state. So defer the check for one retry.
|
# will be same as the final state. So defer the check for one retry.
|
||||||
if retries[0] != 0 and state[0] == target_state:
|
if retries[0] != 0 and state[0] == state_to_check:
|
||||||
raise loopingcall.LoopingCallDone()
|
raise loopingcall.LoopingCallDone()
|
||||||
|
|
||||||
if retries[0] > CONF.ilo.power_retry:
|
if retries[0] > max_retry:
|
||||||
state[0] = states.ERROR
|
state[0] = states.ERROR
|
||||||
raise loopingcall.LoopingCallDone()
|
raise loopingcall.LoopingCallDone()
|
||||||
|
|
||||||
|
LOG.debug("%(tim)s secs elapsed while waiting for power state "
|
||||||
|
"of '%(target_state)s', current state of server %(node)s "
|
||||||
|
"is '%(cur_state)s'.",
|
||||||
|
{'tim': int(retries[0] * interval),
|
||||||
|
'target_state': state_to_check,
|
||||||
|
'node': node.uuid,
|
||||||
|
'cur_state': state[0]})
|
||||||
retries[0] += 1
|
retries[0] += 1
|
||||||
|
|
||||||
# Start a timer and wait for the operation to complete.
|
# Start a timer and wait for the operation to complete.
|
||||||
timer = loopingcall.FixedIntervalLoopingCall(_wait, state)
|
timer = loopingcall.FixedIntervalLoopingCall(_wait, state)
|
||||||
timer.start(interval=CONF.ilo.power_wait).wait()
|
timer.start(interval=interval).wait()
|
||||||
|
if state[0] == state_to_check:
|
||||||
return state[0]
|
return int(retries[0] * interval)
|
||||||
|
else:
|
||||||
|
timeout = int(max_retry * interval)
|
||||||
|
LOG.error("iLO failed to change state to %(tstate)s "
|
||||||
|
"within %(timeout)s sec for node %(node)s",
|
||||||
|
{'tstate': target_state, 'node': node.uuid,
|
||||||
|
'timeout': int(max_retry * interval)})
|
||||||
|
raise exception.PowerStateFailure(pstate=target_state)
|
||||||
|
|
||||||
|
|
||||||
def _set_power_state(task, target_state):
|
def _set_power_state(task, target_state, timeout=None):
|
||||||
"""Turns the server power on/off or do a reboot.
|
"""Turns the server power on/off or do a reboot.
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
:param task: a TaskManager instance containing the node to act on.
|
||||||
:param target_state: target state of the node.
|
:param target_state: target state of the node.
|
||||||
|
:param timeout: timeout (in seconds) positive integer (> 0) for any
|
||||||
|
power state. ``None`` indicates default timeout.
|
||||||
:raises: InvalidParameterValue if an invalid power state was specified.
|
:raises: InvalidParameterValue if an invalid power state was specified.
|
||||||
:raises: IloOperationError on an error from IloClient library.
|
:raises: IloOperationError on an error from IloClient library.
|
||||||
:raises: PowerStateFailure if the power couldn't be set to target_state.
|
:raises: PowerStateFailure if the power couldn't be set to target_state.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
node = task.node
|
node = task.node
|
||||||
ilo_object = ilo_common.get_ilo_object(node)
|
ilo_object = ilo_common.get_ilo_object(node)
|
||||||
|
|
||||||
|
# Check if its soft power operation
|
||||||
|
soft_power_op = target_state in [states.SOFT_POWER_OFF, states.SOFT_REBOOT]
|
||||||
|
|
||||||
|
if target_state == states.SOFT_REBOOT:
|
||||||
|
if _get_power_state(node) == states.POWER_OFF:
|
||||||
|
target_state = states.POWER_ON
|
||||||
|
|
||||||
# Trigger the operation based on the target state.
|
# Trigger the operation based on the target state.
|
||||||
try:
|
try:
|
||||||
if target_state == states.POWER_OFF:
|
if target_state == states.POWER_OFF:
|
||||||
@ -145,6 +212,8 @@ def _set_power_state(task, target_state):
|
|||||||
_attach_boot_iso_if_needed(task)
|
_attach_boot_iso_if_needed(task)
|
||||||
ilo_object.reset_server()
|
ilo_object.reset_server()
|
||||||
target_state = states.POWER_ON
|
target_state = states.POWER_ON
|
||||||
|
elif target_state in (states.SOFT_POWER_OFF, states.SOFT_REBOOT):
|
||||||
|
ilo_object.press_pwr_btn()
|
||||||
else:
|
else:
|
||||||
msg = _("_set_power_state called with invalid power state "
|
msg = _("_set_power_state called with invalid power state "
|
||||||
"'%s'") % target_state
|
"'%s'") % target_state
|
||||||
@ -159,15 +228,58 @@ def _set_power_state(task, target_state):
|
|||||||
raise exception.IloOperationError(operation=operation,
|
raise exception.IloOperationError(operation=operation,
|
||||||
error=ilo_exception)
|
error=ilo_exception)
|
||||||
|
|
||||||
# Wait till the state change gets reflected.
|
# Wait till the soft power state change gets reflected.
|
||||||
state = _wait_for_state_change(node, target_state)
|
time_consumed = 0
|
||||||
|
if soft_power_op:
|
||||||
|
# For soft power-off, bare metal reaches final state with one
|
||||||
|
# power operation. In case of soft reboot it takes two; soft
|
||||||
|
# power-off followed by power-on. Also, for soft reboot we
|
||||||
|
# need to ensure timeout does not expire during power-off
|
||||||
|
# and power-on operation.
|
||||||
|
is_final_state = target_state in (states.SOFT_POWER_OFF,
|
||||||
|
states.POWER_ON)
|
||||||
|
time_consumed = _wait_for_state_change(
|
||||||
|
node, target_state, is_final_state=is_final_state, timeout=timeout)
|
||||||
|
if target_state == states.SOFT_REBOOT:
|
||||||
|
_attach_boot_iso_if_needed(task)
|
||||||
|
try:
|
||||||
|
ilo_object.set_host_power('ON')
|
||||||
|
except ilo_error.IloError as ilo_exception:
|
||||||
|
operation = (_('Powering on failed after soft power off for '
|
||||||
|
'node %s') % node.uuid)
|
||||||
|
raise exception.IloOperationError(operation=operation,
|
||||||
|
error=ilo_exception)
|
||||||
|
# Re-calculate timeout available for power-on operation
|
||||||
|
rem_timeout = timeout - time_consumed
|
||||||
|
time_consumed += _wait_for_state_change(
|
||||||
|
node, states.SOFT_REBOOT, is_final_state=True,
|
||||||
|
timeout=rem_timeout)
|
||||||
|
else:
|
||||||
|
time_consumed = _wait_for_state_change(
|
||||||
|
node, target_state, is_final_state=True, timeout=timeout)
|
||||||
|
LOG.info("The node %(node_id)s operation of '%(state)s' "
|
||||||
|
"is completed in %(time_consumed)s seconds.",
|
||||||
|
{'node_id': node.uuid, 'state': target_state,
|
||||||
|
'time_consumed': time_consumed})
|
||||||
|
|
||||||
if state != target_state:
|
|
||||||
timeout = (CONF.ilo.power_wait) * (CONF.ilo.power_retry)
|
def _can_get_server_post_state(node):
|
||||||
LOG.error("iLO failed to change state to %(tstate)s "
|
"""Checks if POST state can be retrieved.
|
||||||
"within %(timeout)s sec",
|
|
||||||
{'tstate': target_state, 'timeout': timeout})
|
Returns True if the POST state of the server can be retrieved.
|
||||||
raise exception.PowerStateFailure(pstate=target_state)
|
It cannot be retrieved for older ProLiant models.
|
||||||
|
:param node: The node.
|
||||||
|
:returns: True if POST state can be retrieved, else Flase.
|
||||||
|
:raises: IloOperationError on an error from IloClient library.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
ilo_common.get_server_post_state(node)
|
||||||
|
return True
|
||||||
|
except exception.IloOperationNotSupported as exc:
|
||||||
|
LOG.debug("Node %(node)s does not support retrieval of "
|
||||||
|
"boot post state. Reason: %(reason)s",
|
||||||
|
{'node': node.uuid, 'reason': exc})
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class IloPower(base.PowerInterface):
|
class IloPower(base.PowerInterface):
|
||||||
@ -211,14 +323,7 @@ class IloPower(base.PowerInterface):
|
|||||||
:raises: IloOperationError on an error from IloClient library.
|
:raises: IloOperationError on an error from IloClient library.
|
||||||
:raises: PowerStateFailure if the power couldn't be set to power_state.
|
:raises: PowerStateFailure if the power couldn't be set to power_state.
|
||||||
"""
|
"""
|
||||||
# TODO(rloo): Support timeouts!
|
_set_power_state(task, power_state, timeout=timeout)
|
||||||
if timeout is not None:
|
|
||||||
LOG.warning("The 'ilo' Power Interface's 'set_power_state' method "
|
|
||||||
"doesn't support the 'timeout' parameter. Ignoring "
|
|
||||||
"timeout=%(timeout)s",
|
|
||||||
{'timeout': timeout})
|
|
||||||
|
|
||||||
_set_power_state(task, power_state)
|
|
||||||
|
|
||||||
@METRICS.timer('IloPower.reboot')
|
@METRICS.timer('IloPower.reboot')
|
||||||
@task_manager.require_exclusive_lock
|
@task_manager.require_exclusive_lock
|
||||||
@ -231,16 +336,21 @@ class IloPower(base.PowerInterface):
|
|||||||
POWER_ON.
|
POWER_ON.
|
||||||
:raises: IloOperationError on an error from IloClient library.
|
:raises: IloOperationError on an error from IloClient library.
|
||||||
"""
|
"""
|
||||||
# TODO(rloo): Support timeouts!
|
|
||||||
if timeout is not None:
|
|
||||||
LOG.warning("The 'ilo' Power Interface's 'reboot' method "
|
|
||||||
"doesn't support the 'timeout' parameter. Ignoring "
|
|
||||||
"timeout=%(timeout)s",
|
|
||||||
{'timeout': timeout})
|
|
||||||
|
|
||||||
node = task.node
|
node = task.node
|
||||||
current_pstate = _get_power_state(node)
|
current_pstate = _get_power_state(node)
|
||||||
if current_pstate == states.POWER_ON:
|
if current_pstate == states.POWER_ON:
|
||||||
_set_power_state(task, states.REBOOT)
|
_set_power_state(task, states.REBOOT, timeout=timeout)
|
||||||
elif current_pstate == states.POWER_OFF:
|
elif current_pstate == states.POWER_OFF:
|
||||||
_set_power_state(task, states.POWER_ON)
|
_set_power_state(task, states.POWER_ON, timeout=timeout)
|
||||||
|
|
||||||
|
@METRICS.timer('IloPower.get_supported_power_states')
|
||||||
|
def get_supported_power_states(self, task):
|
||||||
|
"""Get a list of the supported power states.
|
||||||
|
|
||||||
|
:param task: A TaskManager instance containing the node to act on.
|
||||||
|
currently not used.
|
||||||
|
:returns: A list with the supported power states defined
|
||||||
|
in :mod:`ironic.common.states`.
|
||||||
|
"""
|
||||||
|
return [states.POWER_OFF, states.POWER_ON, states.REBOOT,
|
||||||
|
states.SOFT_POWER_OFF, states.SOFT_REBOOT]
|
||||||
|
@ -1071,3 +1071,45 @@ class IloCommonMethodsTestCase(BaseIloTest):
|
|||||||
ilo_common.verify_image_checksum,
|
ilo_common.verify_image_checksum,
|
||||||
file_like_object,
|
file_like_object,
|
||||||
invalid_hash)
|
invalid_hash)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_get_server_post_state(self,
|
||||||
|
get_ilo_object_mock):
|
||||||
|
ilo_object_mock = get_ilo_object_mock.return_value
|
||||||
|
post_state = 'FinishedPost'
|
||||||
|
ilo_object_mock.get_host_post_state.return_value = post_state
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
ret = ilo_common.get_server_post_state(task.node)
|
||||||
|
ilo_object_mock.get_host_post_state.assert_called_once_with()
|
||||||
|
self.assertEqual(post_state, ret)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_get_server_post_state_fail(self,
|
||||||
|
get_ilo_object_mock):
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
exc = ilo_error.IloError('error')
|
||||||
|
ilo_mock_object.get_host_post_state.side_effect = exc
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
self.assertRaises(exception.IloOperationError,
|
||||||
|
ilo_common.get_server_post_state, task.node)
|
||||||
|
ilo_mock_object.get_host_post_state.assert_called_once_with()
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_get_server_post_state_not_supported(self,
|
||||||
|
ilo_object_mock):
|
||||||
|
ilo_mock_object = ilo_object_mock.return_value
|
||||||
|
exc = ilo_error.IloCommandNotSupportedError('error')
|
||||||
|
ilo_mock_object.get_host_post_state.side_effect = exc
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
self.assertRaises(exception.IloOperationNotSupported,
|
||||||
|
ilo_common.get_server_post_state,
|
||||||
|
task.node)
|
||||||
|
ilo_mock_object.get_host_post_state.assert_called_once_with()
|
||||||
|
@ -128,6 +128,199 @@ class IloPowerInternalMethodsTestCase(test_common.BaseIloTest):
|
|||||||
ilo_mock_object.get_host_power_status.assert_called_with()
|
ilo_mock_object.get_host_power_status.assert_called_with()
|
||||||
ilo_mock_object.set_host_power.assert_called_once_with('ON')
|
ilo_mock_object.set_host_power.assert_called_once_with('ON')
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_power.LOG, 'info')
|
||||||
|
@mock.patch.object(ilo_power, '_attach_boot_iso_if_needed',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'get_server_post_state', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__set_power_state_soft_reboot_ok(
|
||||||
|
self, get_post_mock, attach_boot_iso_mock,
|
||||||
|
log_mock, get_ilo_object_mock):
|
||||||
|
CONF.set_override('power_wait', 1, 'ilo')
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
ilo_mock_object.get_host_power_status.return_value = 'ON'
|
||||||
|
get_post_mock.side_effect = (
|
||||||
|
['FinishedPost', 'FinishedPost', 'PowerOff', 'PowerOff', 'InPost',
|
||||||
|
'FinishedPost'])
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
ilo_power._set_power_state(task, states.SOFT_REBOOT, timeout=3)
|
||||||
|
get_post_mock.assert_called_with(task.node)
|
||||||
|
ilo_mock_object.press_pwr_btn.assert_called_once_with()
|
||||||
|
attach_boot_iso_mock.assert_called_once_with(task)
|
||||||
|
ilo_mock_object.set_host_power.assert_called_once_with('ON')
|
||||||
|
log_mock.assert_called_once_with(
|
||||||
|
"The node %(node_id)s operation of '%(state)s' "
|
||||||
|
"is completed in %(time_consumed)s seconds.",
|
||||||
|
{'state': 'soft rebooting', 'node_id': task.node.uuid,
|
||||||
|
'time_consumed': 2})
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_power.LOG, 'info')
|
||||||
|
@mock.patch.object(ilo_power, '_attach_boot_iso_if_needed',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'get_server_post_state', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__set_power_state_soft_reboot_ok_initial_power_off(
|
||||||
|
self, get_post_mock, attach_boot_iso_mock,
|
||||||
|
log_mock, get_ilo_object_mock):
|
||||||
|
CONF.set_override('power_wait', 1, 'ilo')
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
ilo_mock_object.get_host_power_status.return_value = 'OFF'
|
||||||
|
get_post_mock.side_effect = ['FinishedPost', 'PowerOff',
|
||||||
|
'FinishedPost']
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
ilo_power._set_power_state(task, states.SOFT_REBOOT, timeout=3)
|
||||||
|
get_post_mock.assert_called_with(task.node)
|
||||||
|
attach_boot_iso_mock.assert_called_once_with(task)
|
||||||
|
ilo_mock_object.set_host_power.assert_called_once_with('ON')
|
||||||
|
log_mock.assert_called_once_with(
|
||||||
|
"The node %(node_id)s operation of '%(state)s' "
|
||||||
|
"is completed in %(time_consumed)s seconds.",
|
||||||
|
{'state': 'power on', 'node_id': task.node.uuid,
|
||||||
|
'time_consumed': 1})
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_power.LOG, 'info')
|
||||||
|
@mock.patch.object(ilo_power, '_attach_boot_iso_if_needed',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'get_server_post_state', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__set_power_state_soft_reboot_fail_to_off(
|
||||||
|
self, get_post_mock, attach_boot_iso_mock,
|
||||||
|
log_mock, get_ilo_object_mock):
|
||||||
|
CONF.set_override('power_wait', 1, 'ilo')
|
||||||
|
exc = ilo_error.IloError('error')
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
ilo_mock_object.get_host_power_status.return_value = 'ON'
|
||||||
|
ilo_mock_object.press_pwr_btn.side_effect = exc
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
self.assertRaises(exception.IloOperationError,
|
||||||
|
ilo_power._set_power_state,
|
||||||
|
task, states.SOFT_REBOOT, timeout=3)
|
||||||
|
ilo_mock_object.press_pwr_btn.assert_called_once_with()
|
||||||
|
self.assertFalse(get_post_mock.called)
|
||||||
|
self.assertFalse(attach_boot_iso_mock.called)
|
||||||
|
self.assertFalse(log_mock.called)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_power.LOG, 'info')
|
||||||
|
@mock.patch.object(ilo_power, '_attach_boot_iso_if_needed',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'get_server_post_state', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__set_power_state_soft_reboot_fail_to_on(
|
||||||
|
self, get_post_mock, attach_boot_iso_mock,
|
||||||
|
log_mock, get_ilo_object_mock):
|
||||||
|
CONF.set_override('power_wait', 1, 'ilo')
|
||||||
|
exc = ilo_error.IloError('error')
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
ilo_mock_object.get_host_power_status.return_value = 'ON'
|
||||||
|
get_post_mock.side_effect = (
|
||||||
|
['FinishedPost', 'PowerOff', 'PowerOff', 'InPost',
|
||||||
|
'InPost', 'InPost', 'InPost', 'InPost'])
|
||||||
|
ilo_mock_object.press_pwr_btn.side_effect = [None, exc]
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
self.assertRaises(exception.PowerStateFailure,
|
||||||
|
ilo_power._set_power_state,
|
||||||
|
task, states.SOFT_REBOOT, timeout=3)
|
||||||
|
get_post_mock.assert_called_with(task.node)
|
||||||
|
ilo_mock_object.press_pwr_btn.assert_called_once_with()
|
||||||
|
ilo_mock_object.set_host_power.assert_called_once_with('ON')
|
||||||
|
attach_boot_iso_mock.assert_called_once_with(task)
|
||||||
|
self.assertFalse(log_mock.called)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_power.LOG, 'info')
|
||||||
|
@mock.patch.object(ilo_power, '_attach_boot_iso_if_needed',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'get_server_post_state', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__set_power_state_soft_reboot_timeout(
|
||||||
|
self, get_post_mock, attach_boot_iso_mock,
|
||||||
|
log_mock, get_ilo_object_mock):
|
||||||
|
CONF.set_override('power_wait', 1, 'ilo')
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
ilo_mock_object.get_host_power_status.return_value = 'ON'
|
||||||
|
get_post_mock.side_effect = ['FinishedPost', 'FinishedPost',
|
||||||
|
'PowerOff', 'InPost', 'InPost', 'InPost'
|
||||||
|
'InPost', 'InPost']
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
self.assertRaises(exception.PowerStateFailure,
|
||||||
|
ilo_power._set_power_state,
|
||||||
|
task, states.SOFT_REBOOT, timeout=2)
|
||||||
|
get_post_mock.assert_called_with(task.node)
|
||||||
|
ilo_mock_object.press_pwr_btn.assert_called_once_with()
|
||||||
|
ilo_mock_object.set_host_power.assert_called_once_with('ON')
|
||||||
|
attach_boot_iso_mock.assert_called_once_with(task)
|
||||||
|
self.assertFalse(log_mock.called)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_power.LOG, 'info')
|
||||||
|
@mock.patch.object(ilo_common, 'get_server_post_state', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__set_power_state_soft_power_off_ok(
|
||||||
|
self, get_post_mock, log_mock, get_ilo_object_mock):
|
||||||
|
CONF.set_override('power_wait', 1, 'ilo')
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
get_post_mock.side_effect = ['FinishedPost', 'FinishedPost', 'PowerOff'
|
||||||
|
'PowerOff', 'PowerOff']
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
ilo_power._set_power_state(task, states.SOFT_POWER_OFF, timeout=3)
|
||||||
|
get_post_mock.assert_called_with(task.node)
|
||||||
|
ilo_mock_object.press_pwr_btn.assert_called_once_with()
|
||||||
|
log_mock.assert_called_once_with(
|
||||||
|
"The node %(node_id)s operation of '%(state)s' "
|
||||||
|
"is completed in %(time_consumed)s seconds.",
|
||||||
|
{'state': 'soft power off', 'node_id': task.node.uuid,
|
||||||
|
'time_consumed': 2})
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_power.LOG, 'info')
|
||||||
|
@mock.patch.object(ilo_common, 'get_server_post_state', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__set_power_state_soft_power_off_fail(
|
||||||
|
self, get_post_mock, log_mock, get_ilo_object_mock):
|
||||||
|
CONF.set_override('power_wait', 1, 'ilo')
|
||||||
|
exc = ilo_error.IloError('error')
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
ilo_mock_object.get_host_power_status.return_value = 'ON'
|
||||||
|
ilo_mock_object.press_pwr_btn.side_effect = exc
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
self.assertRaises(exception.IloOperationError,
|
||||||
|
ilo_power._set_power_state,
|
||||||
|
task, states.SOFT_POWER_OFF, timeout=2)
|
||||||
|
ilo_mock_object.press_pwr_btn.assert_called_once_with()
|
||||||
|
self.assertFalse(get_post_mock.called)
|
||||||
|
self.assertFalse(log_mock.called)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_power.LOG, 'info')
|
||||||
|
@mock.patch.object(ilo_common, 'get_server_post_state', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__set_power_state_soft_power_off_timeout(
|
||||||
|
self, get_post_mock, log_mock, get_ilo_object_mock):
|
||||||
|
CONF.set_override('power_wait', 1, 'ilo')
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
ilo_mock_object.get_host_power_status.return_value = 'ON'
|
||||||
|
get_post_mock.side_effect = ['FinishedPost', 'InPost', 'InPost',
|
||||||
|
'InPost', 'InPost']
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
self.assertRaises(exception.PowerStateFailure,
|
||||||
|
ilo_power._set_power_state,
|
||||||
|
task, states.SOFT_POWER_OFF, timeout=2)
|
||||||
|
get_post_mock.assert_called_with(task.node)
|
||||||
|
ilo_mock_object.press_pwr_btn.assert_called_with()
|
||||||
|
self.assertFalse(log_mock.called)
|
||||||
|
|
||||||
@mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
|
@mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True,
|
@mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True,
|
||||||
@ -197,55 +390,50 @@ class IloPowerTestCase(test_common.BaseIloTest):
|
|||||||
task.driver.power.get_power_state(task))
|
task.driver.power.get_power_state(task))
|
||||||
mock_get_power.assert_called_once_with(task.node)
|
mock_get_power.assert_called_once_with(task.node)
|
||||||
|
|
||||||
@mock.patch.object(ilo_power.LOG, 'warning')
|
|
||||||
@mock.patch.object(ilo_power, '_set_power_state', spec_set=True,
|
@mock.patch.object(ilo_power, '_set_power_state', spec_set=True,
|
||||||
autospec=True)
|
autospec=True)
|
||||||
def test_set_power_state(self, mock_set_power, mock_log):
|
def _test_set_power_state(self, mock_set_power, timeout=None):
|
||||||
mock_set_power.return_value = states.POWER_ON
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
|
||||||
shared=False) as task:
|
|
||||||
task.driver.power.set_power_state(task, states.POWER_ON)
|
|
||||||
mock_set_power.assert_called_once_with(task, states.POWER_ON)
|
|
||||||
self.assertFalse(mock_log.called)
|
|
||||||
|
|
||||||
@mock.patch.object(ilo_power.LOG, 'warning')
|
|
||||||
@mock.patch.object(ilo_power, '_set_power_state', spec_set=True,
|
|
||||||
autospec=True)
|
|
||||||
def test_set_power_state_timeout(self, mock_set_power, mock_log):
|
|
||||||
mock_set_power.return_value = states.POWER_ON
|
mock_set_power.return_value = states.POWER_ON
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=False) as task:
|
shared=False) as task:
|
||||||
task.driver.power.set_power_state(task, states.POWER_ON,
|
task.driver.power.set_power_state(task, states.POWER_ON,
|
||||||
timeout=13)
|
timeout=timeout)
|
||||||
mock_set_power.assert_called_once_with(task, states.POWER_ON)
|
mock_set_power.assert_called_once_with(task, states.POWER_ON,
|
||||||
self.assertTrue(mock_log.called)
|
timeout=timeout)
|
||||||
|
|
||||||
|
def test_set_power_state_no_timeout(self):
|
||||||
|
self._test_set_power_state(timeout=None)
|
||||||
|
|
||||||
|
def test_set_power_state_timeout(self):
|
||||||
|
self._test_set_power_state(timeout=13)
|
||||||
|
|
||||||
@mock.patch.object(ilo_power.LOG, 'warning')
|
|
||||||
@mock.patch.object(ilo_power, '_set_power_state', spec_set=True,
|
@mock.patch.object(ilo_power, '_set_power_state', spec_set=True,
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch.object(ilo_power, '_get_power_state', spec_set=True,
|
@mock.patch.object(ilo_power, '_get_power_state', spec_set=True,
|
||||||
autospec=True)
|
autospec=True)
|
||||||
def test_reboot(self, mock_get_power, mock_set_power, mock_log):
|
def _test_reboot(
|
||||||
|
self, mock_get_power, mock_set_power,
|
||||||
|
timeout=None):
|
||||||
|
mock_get_power.return_value = states.POWER_ON
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=False) as task:
|
shared=False) as task:
|
||||||
mock_get_power.return_value = states.POWER_ON
|
task.driver.power.reboot(task, timeout=timeout)
|
||||||
mock_set_power.return_value = states.POWER_ON
|
|
||||||
task.driver.power.reboot(task)
|
|
||||||
mock_get_power.assert_called_once_with(task.node)
|
mock_get_power.assert_called_once_with(task.node)
|
||||||
mock_set_power.assert_called_once_with(task, states.REBOOT)
|
mock_set_power.assert_called_once_with(
|
||||||
self.assertFalse(mock_log.called)
|
task, states.REBOOT, timeout=timeout)
|
||||||
|
|
||||||
@mock.patch.object(ilo_power.LOG, 'warning')
|
def test_reboot_no_timeout(self):
|
||||||
@mock.patch.object(ilo_power, '_set_power_state', spec_set=True,
|
self._test_reboot(timeout=None)
|
||||||
autospec=True)
|
|
||||||
@mock.patch.object(ilo_power, '_get_power_state', spec_set=True,
|
def test_reboot_with_timeout(self):
|
||||||
autospec=True)
|
self._test_reboot(timeout=100)
|
||||||
def test_reboot_timeout(self, mock_get_power, mock_set_power, mock_log):
|
|
||||||
|
def test_get_supported_power_states(self):
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=False) as task:
|
shared=False) as task:
|
||||||
mock_get_power.return_value = states.POWER_ON
|
expected = [states.POWER_OFF, states.POWER_ON, states.REBOOT,
|
||||||
mock_set_power.return_value = states.POWER_ON
|
states.SOFT_POWER_OFF, states.SOFT_REBOOT]
|
||||||
task.driver.power.reboot(task, timeout=123)
|
self.assertEqual(
|
||||||
mock_get_power.assert_called_once_with(task.node)
|
sorted(expected),
|
||||||
mock_set_power.assert_called_once_with(task, states.REBOOT)
|
sorted(task.driver.power.
|
||||||
self.assertTrue(mock_log.called)
|
get_supported_power_states(task)))
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Adds support for ``soft power off`` and ``soft reboot`` operations to
|
||||||
|
``ilo`` power interface.
|
||||||
|
deprecations:
|
||||||
|
- |
|
||||||
|
The ``[ilo]/power_retry`` config is deprecated and will be removed in
|
||||||
|
the future release. Please use ``[conductor]/soft_power_off_timeout``
|
||||||
|
instead.
|
Loading…
Reference in New Issue
Block a user