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.')),
|
||||
cfg.IntOpt('power_retry',
|
||||
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 '
|
||||
'retried')),
|
||||
cfg.IntOpt('power_wait',
|
||||
|
@ -96,6 +96,27 @@ BOOT_MODE_GENERIC_TO_ILO = {'bios': 'legacy', 'uefi': 'uefi'}
|
||||
BOOT_MODE_ILO_TO_GENERIC = dict(
|
||||
(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):
|
||||
"""Copies the given image to the http web server.
|
||||
@ -785,3 +806,26 @@ def verify_image_checksum(image_location, expected_checksum):
|
||||
LOG.error(msg)
|
||||
raise exception.ImageRefValidationFailed(image_href=image_location,
|
||||
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
|
||||
|
||||
|
||||
def _wait_for_state_change(node, target_state):
|
||||
"""Wait for the power state change to get reflected."""
|
||||
def _wait_for_state_change(node, target_state,
|
||||
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]
|
||||
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):
|
||||
|
||||
if use_post_state:
|
||||
state[0] = ilo_common.get_server_post_state(node)
|
||||
else:
|
||||
state[0] = _get_power_state(node)
|
||||
|
||||
# NOTE(rameshg87): For reboot operations, initially the state
|
||||
# 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()
|
||||
|
||||
if retries[0] > CONF.ilo.power_retry:
|
||||
if retries[0] > max_retry:
|
||||
state[0] = states.ERROR
|
||||
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
|
||||
|
||||
# Start a timer and wait for the operation to complete.
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait, state)
|
||||
timer.start(interval=CONF.ilo.power_wait).wait()
|
||||
|
||||
return state[0]
|
||||
timer.start(interval=interval).wait()
|
||||
if state[0] == state_to_check:
|
||||
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.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
: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: IloOperationError on an error from IloClient library.
|
||||
:raises: PowerStateFailure if the power couldn't be set to target_state.
|
||||
"""
|
||||
|
||||
node = task.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.
|
||||
try:
|
||||
if target_state == states.POWER_OFF:
|
||||
@ -145,6 +212,8 @@ def _set_power_state(task, target_state):
|
||||
_attach_boot_iso_if_needed(task)
|
||||
ilo_object.reset_server()
|
||||
target_state = states.POWER_ON
|
||||
elif target_state in (states.SOFT_POWER_OFF, states.SOFT_REBOOT):
|
||||
ilo_object.press_pwr_btn()
|
||||
else:
|
||||
msg = _("_set_power_state called with invalid power state "
|
||||
"'%s'") % target_state
|
||||
@ -159,15 +228,58 @@ def _set_power_state(task, target_state):
|
||||
raise exception.IloOperationError(operation=operation,
|
||||
error=ilo_exception)
|
||||
|
||||
# Wait till the state change gets reflected.
|
||||
state = _wait_for_state_change(node, target_state)
|
||||
# Wait till the soft power state change gets reflected.
|
||||
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)
|
||||
LOG.error("iLO failed to change state to %(tstate)s "
|
||||
"within %(timeout)s sec",
|
||||
{'tstate': target_state, 'timeout': timeout})
|
||||
raise exception.PowerStateFailure(pstate=target_state)
|
||||
|
||||
def _can_get_server_post_state(node):
|
||||
"""Checks if POST state can be retrieved.
|
||||
|
||||
Returns True if the POST state of the server can be retrieved.
|
||||
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):
|
||||
@ -211,14 +323,7 @@ class IloPower(base.PowerInterface):
|
||||
:raises: IloOperationError on an error from IloClient library.
|
||||
:raises: PowerStateFailure if the power couldn't be set to power_state.
|
||||
"""
|
||||
# TODO(rloo): Support timeouts!
|
||||
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)
|
||||
_set_power_state(task, power_state, timeout=timeout)
|
||||
|
||||
@METRICS.timer('IloPower.reboot')
|
||||
@task_manager.require_exclusive_lock
|
||||
@ -231,16 +336,21 @@ class IloPower(base.PowerInterface):
|
||||
POWER_ON.
|
||||
: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
|
||||
current_pstate = _get_power_state(node)
|
||||
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:
|
||||
_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,
|
||||
file_like_object,
|
||||
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.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,
|
||||
autospec=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))
|
||||
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,
|
||||
autospec=True)
|
||||
def test_set_power_state(self, mock_set_power, mock_log):
|
||||
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):
|
||||
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,
|
||||
timeout=13)
|
||||
mock_set_power.assert_called_once_with(task, states.POWER_ON)
|
||||
self.assertTrue(mock_log.called)
|
||||
timeout=timeout)
|
||||
mock_set_power.assert_called_once_with(task, states.POWER_ON,
|
||||
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,
|
||||
autospec=True)
|
||||
@mock.patch.object(ilo_power, '_get_power_state', spec_set=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,
|
||||
shared=False) as task:
|
||||
mock_get_power.return_value = states.POWER_ON
|
||||
mock_set_power.return_value = states.POWER_ON
|
||||
task.driver.power.reboot(task)
|
||||
task.driver.power.reboot(task, timeout=timeout)
|
||||
mock_get_power.assert_called_once_with(task.node)
|
||||
mock_set_power.assert_called_once_with(task, states.REBOOT)
|
||||
self.assertFalse(mock_log.called)
|
||||
mock_set_power.assert_called_once_with(
|
||||
task, states.REBOOT, timeout=timeout)
|
||||
|
||||
@mock.patch.object(ilo_power.LOG, 'warning')
|
||||
@mock.patch.object(ilo_power, '_set_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(ilo_power, '_get_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
def test_reboot_timeout(self, mock_get_power, mock_set_power, mock_log):
|
||||
def test_reboot_no_timeout(self):
|
||||
self._test_reboot(timeout=None)
|
||||
|
||||
def test_reboot_with_timeout(self):
|
||||
self._test_reboot(timeout=100)
|
||||
|
||||
def test_get_supported_power_states(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
mock_get_power.return_value = states.POWER_ON
|
||||
mock_set_power.return_value = states.POWER_ON
|
||||
task.driver.power.reboot(task, timeout=123)
|
||||
mock_get_power.assert_called_once_with(task.node)
|
||||
mock_set_power.assert_called_once_with(task, states.REBOOT)
|
||||
self.assertTrue(mock_log.called)
|
||||
expected = [states.POWER_OFF, states.POWER_ON, states.REBOOT,
|
||||
states.SOFT_POWER_OFF, states.SOFT_REBOOT]
|
||||
self.assertEqual(
|
||||
sorted(expected),
|
||||
sorted(task.driver.power.
|
||||
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