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:
Shivanand Tendulker 2018-09-11 23:32:16 -04:00
parent 1dabeb28f8
commit f4fc39de7c
6 changed files with 469 additions and 71 deletions

View File

@ -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',

View File

@ -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)

View File

@ -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]

View File

@ -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()

View File

@ -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)))

View File

@ -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.