209 lines
7.3 KiB
Python
209 lines
7.3 KiB
Python
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
iLO Power Driver
|
|
"""
|
|
|
|
from oslo.config import cfg
|
|
|
|
from ironic.common import exception
|
|
from ironic.common import states
|
|
from ironic.conductor import task_manager
|
|
from ironic.drivers import base
|
|
from ironic.drivers.modules.ilo import common as ilo_common
|
|
from ironic.openstack.common import importutils
|
|
from ironic.openstack.common import log as logging
|
|
from ironic.openstack.common import loopingcall
|
|
|
|
ilo_client = importutils.try_import('proliantutils.ilo.ribcl')
|
|
|
|
|
|
opts = [
|
|
cfg.IntOpt('power_retry',
|
|
default=6,
|
|
help='Number of times a power operation needs to be retried'),
|
|
cfg.IntOpt('power_wait',
|
|
default=2,
|
|
help='Amount of time in seconds to wait in between power '
|
|
'operations'),
|
|
]
|
|
|
|
CONF = cfg.CONF
|
|
CONF.register_opts(opts, group='ilo')
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def _get_power_state(node):
|
|
"""Returns the current power state of the node.
|
|
|
|
:param node: The node.
|
|
:returns: power state, one of :mod: `ironic.common.states`.
|
|
:raises: InvalidParameterValue if required iLO credentials are missing.
|
|
:raises: IloOperationError on an error from IloClient library.
|
|
"""
|
|
|
|
ilo_object = ilo_common.get_ilo_object(node)
|
|
|
|
# Check the current power state.
|
|
try:
|
|
power_status = ilo_object.get_host_power_status()
|
|
|
|
except ilo_client.IloError as ilo_exception:
|
|
LOG.error(_("iLO get_power_state failed for node %(node_id)s with "
|
|
"error: %(error)s."),
|
|
{'node_id': node.uuid, 'error': ilo_exception})
|
|
operation = _('iLO get_power_status')
|
|
raise exception.IloOperationError(operation=operation,
|
|
error=ilo_exception)
|
|
|
|
if power_status == "ON":
|
|
return states.POWER_ON
|
|
elif power_status == "OFF":
|
|
return states.POWER_OFF
|
|
else:
|
|
return states.ERROR
|
|
|
|
|
|
def _wait_for_state_change(node, target_state):
|
|
"""Wait for the power state change to get reflected."""
|
|
state = [None]
|
|
retries = [0]
|
|
|
|
def _wait(state):
|
|
|
|
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:
|
|
raise loopingcall.LoopingCallDone()
|
|
|
|
if retries[0] > CONF.ilo.power_retry:
|
|
state[0] = states.ERROR
|
|
raise loopingcall.LoopingCallDone()
|
|
|
|
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]
|
|
|
|
|
|
def _set_power_state(node, target_state):
|
|
"""Turns the server power on/off or do a reboot.
|
|
|
|
:param node: an ironic node object.
|
|
:param target_state: target state of the node.
|
|
: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.
|
|
"""
|
|
|
|
ilo_object = ilo_common.get_ilo_object(node)
|
|
|
|
# Trigger the operation based on the target state.
|
|
try:
|
|
if target_state == states.POWER_OFF:
|
|
ilo_object.hold_pwr_btn()
|
|
elif target_state == states.POWER_ON:
|
|
ilo_object.set_host_power('ON')
|
|
elif target_state == states.REBOOT:
|
|
ilo_object.reset_server()
|
|
target_state = states.POWER_ON
|
|
else:
|
|
msg = _("_set_power_state called with invalid power state "
|
|
"'%s'") % target_state
|
|
raise exception.InvalidParameterValue(msg)
|
|
|
|
except ilo_client.IloError as ilo_exception:
|
|
LOG.error(_("iLO set_power_state failed to set state to %(tstate)s "
|
|
" for node %(node_id)s with error: %(error)s"),
|
|
{'tstate': target_state, 'node_id': node.uuid,
|
|
'error': ilo_exception})
|
|
operation = _('iLO set_power_state')
|
|
raise exception.IloOperationError(operation=operation,
|
|
error=ilo_exception)
|
|
|
|
# Wait till the state change gets reflected.
|
|
state = _wait_for_state_change(node, target_state)
|
|
|
|
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)
|
|
|
|
|
|
class IloPower(base.PowerInterface):
|
|
|
|
def get_properties(self):
|
|
return ilo_common.COMMON_PROPERTIES
|
|
|
|
def validate(self, task):
|
|
"""Check if node.driver_info contains the required iLO credentials.
|
|
|
|
:param task: a TaskManager instance.
|
|
:param node: Single node object.
|
|
:raises: InvalidParameterValue if required iLO credentials are missing.
|
|
"""
|
|
ilo_common.parse_driver_info(task.node)
|
|
|
|
def get_power_state(self, task):
|
|
"""Gets the current power state.
|
|
|
|
:param task: a TaskManager instance.
|
|
:param node: The Node.
|
|
:returns: one of :mod:`ironic.common.states` POWER_OFF,
|
|
POWER_ON or ERROR.
|
|
:raises: InvalidParameterValue if required iLO credentials are missing.
|
|
:raises: IloOperationError on an error from IloClient library.
|
|
"""
|
|
return _get_power_state(task.node)
|
|
|
|
@task_manager.require_exclusive_lock
|
|
def set_power_state(self, task, power_state):
|
|
"""Turn the current power state on or off.
|
|
|
|
:param task: a TaskManager instance.
|
|
:param node: The Node.
|
|
:param power_state: The desired power state POWER_ON,POWER_OFF or
|
|
REBOOT from :mod:`ironic.common.states`.
|
|
: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 power_state.
|
|
"""
|
|
_set_power_state(task.node, power_state)
|
|
|
|
@task_manager.require_exclusive_lock
|
|
def reboot(self, task):
|
|
"""Reboot the node
|
|
|
|
:param task: a TaskManager instance.
|
|
:param node: The Node.
|
|
:raises: PowerStateFailure if the final state of the node is not
|
|
POWER_ON.
|
|
:raises: IloOperationError on an error from IloClient library.
|
|
"""
|
|
node = task.node
|
|
current_pstate = _get_power_state(node)
|
|
if current_pstate == states.POWER_ON:
|
|
_set_power_state(node, states.REBOOT)
|
|
elif current_pstate == states.POWER_OFF:
|
|
_set_power_state(node, states.POWER_ON)
|