Add IloDriver and its IloPower module
Add a new ironic driver for managing HP Proliant Gen8 servers using iLO4. This commit introduces the power module for IloDriver. Implements: blueprint ironic-ilo-power-driver Change-Id: I8d521f67fb14a6132626782b05cd490cd42ba476 Co-Authors: Anusha Ramineni<anusha.iiitm@gmail.com>
This commit is contained in:
parent
1326235151
commit
e59e5cdb1f
@ -715,6 +715,32 @@
|
||||
#auth_strategy=keystone
|
||||
|
||||
|
||||
[ilo]
|
||||
|
||||
#
|
||||
# Options defined in ironic.drivers.modules.ilo.common
|
||||
#
|
||||
|
||||
# Timeout (in seconds) for iLO operations (integer value)
|
||||
#client_timeout=60
|
||||
|
||||
# Port to be used for iLO operations (integer value)
|
||||
#client_port=443
|
||||
|
||||
|
||||
#
|
||||
# Options defined in ironic.drivers.modules.ilo.power
|
||||
#
|
||||
|
||||
# Number of times a power operation needs to be retried
|
||||
# (integer value)
|
||||
#power_retry=6
|
||||
|
||||
# Amount of time in seconds to wait in between power
|
||||
# operations (integer value)
|
||||
#power_wait=2
|
||||
|
||||
|
||||
[ipmi]
|
||||
|
||||
#
|
||||
|
@ -377,3 +377,7 @@ class ConsoleSubprocessFailed(IronicException):
|
||||
|
||||
class PasswordFileFailedToCreate(IronicException):
|
||||
message = _("Failed to create the password file. %(error)s")
|
||||
|
||||
|
||||
class IloOperationError(IronicException):
|
||||
message = _("%(operation)s failed, error: %(error)s")
|
||||
|
42
ironic/drivers/ilo.py
Normal file
42
ironic/drivers/ilo.py
Normal file
@ -0,0 +1,42 @@
|
||||
# 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 Driver for managing HP Proliant Gen8 and above servers.
|
||||
"""
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules.ilo import power
|
||||
from ironic.openstack.common import importutils
|
||||
|
||||
|
||||
class IloDriver(base.BaseDriver):
|
||||
"""IloDriver using IloClient interface.
|
||||
|
||||
This driver implements the `core` functionality using
|
||||
:class:ironic.drivers.modules.ilo.power.IloPower for power management.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
if not importutils.try_import('proliantutils'):
|
||||
raise exception.DriverLoadError(
|
||||
driver=self.__class__.__name__,
|
||||
reason=_("Unable to import proliantutils library"))
|
||||
|
||||
self.power = power.IloPower()
|
||||
self.deploy = None
|
||||
self.rescue = None
|
||||
self.console = None
|
||||
self.vendor = None
|
0
ironic/drivers/modules/ilo/__init__.py
Normal file
0
ironic/drivers/modules/ilo/__init__.py
Normal file
137
ironic/drivers/modules/ilo/common.py
Normal file
137
ironic/drivers/modules/ilo/common.py
Normal file
@ -0,0 +1,137 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Common functionalities shared between different iLO modules.
|
||||
"""
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.openstack.common import importutils
|
||||
from ironic.openstack.common import log as logging
|
||||
|
||||
ilo_client = importutils.try_import('proliantutils.ilo.ribcl')
|
||||
|
||||
|
||||
STANDARD_LICENSE = 1
|
||||
ESSENTIALS_LICENSE = 2
|
||||
ADVANCED_LICENSE = 3
|
||||
|
||||
|
||||
opts = [
|
||||
cfg.IntOpt('client_timeout',
|
||||
default=60,
|
||||
help='Timeout (in seconds) for iLO operations'),
|
||||
cfg.IntOpt('client_port',
|
||||
default=443,
|
||||
help='Port to be used for iLO operations'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(opts, group='ilo')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parse_driver_info(node):
|
||||
"""Gets the driver specific Node deployment info.
|
||||
|
||||
This method validates whether the 'driver_info' property of the
|
||||
supplied node contains the required information for this driver.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:returns: a dict containing information from driver_info
|
||||
and default values.
|
||||
:raises: InvalidParameterValue if some mandatory information
|
||||
is missing on the node or on invalid inputs.
|
||||
"""
|
||||
info = node.driver_info
|
||||
d_info = {}
|
||||
|
||||
error_msgs = []
|
||||
for param in ('ilo_address', 'ilo_username', 'ilo_password'):
|
||||
try:
|
||||
d_info[param] = info[param]
|
||||
except KeyError:
|
||||
error_msgs.append(_("'%s' not supplied to IloDriver.") % param)
|
||||
|
||||
for param in ('client_port', 'client_timeout'):
|
||||
value = info.get(param, CONF.ilo.get(param))
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
error_msgs.append(_("'%s' is not an integer.") % param)
|
||||
continue
|
||||
d_info[param] = value
|
||||
|
||||
if error_msgs:
|
||||
msg = (_("The following errors were encountered while parsing "
|
||||
"driver_info:\n%s") % "\n".join(error_msgs))
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
return d_info
|
||||
|
||||
|
||||
def get_ilo_object(node):
|
||||
"""Gets an IloClient object from proliantutils library.
|
||||
|
||||
Given an ironic node object, this method gives back a IloClient object
|
||||
to do operations on the iLO.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:returns: an IloClient object.
|
||||
:raises: InvalidParameterValue if some mandatory information
|
||||
is missing on the node or on invalid inputs.
|
||||
"""
|
||||
driver_info = parse_driver_info(node)
|
||||
ilo_object = ilo_client.IloClient(driver_info['ilo_address'],
|
||||
driver_info['ilo_username'],
|
||||
driver_info['ilo_password'],
|
||||
driver_info['client_timeout'],
|
||||
driver_info['client_port'])
|
||||
return ilo_object
|
||||
|
||||
|
||||
def get_ilo_license(node):
|
||||
"""Gives the current installed license on the node.
|
||||
|
||||
Given an ironic node object, this method queries the iLO
|
||||
for currently installed license and returns it back.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:returns: a constant defined in this module which
|
||||
refers to the current license installed on the node.
|
||||
:raises: InvalidParameterValue if some mandatory information
|
||||
is missing on the node or on invalid inputs.
|
||||
:raises: IloOperationError if it failed to retrieve the
|
||||
installed licenses from the iLO.
|
||||
"""
|
||||
# Get the ilo client object, and then the license from the iLO
|
||||
ilo_object = get_ilo_object(node)
|
||||
try:
|
||||
license_info = ilo_object.get_all_licenses()
|
||||
except ilo_client.IloError as ilo_exception:
|
||||
raise exception.IloOperationError(operation=_('iLO license check'),
|
||||
error=str(ilo_exception))
|
||||
|
||||
# Check the license to see if the given license exists
|
||||
current_license_type = license_info['LICENSE_TYPE']
|
||||
|
||||
if current_license_type.endswith("Advanced"):
|
||||
return ADVANCED_LICENSE
|
||||
elif current_license_type.endswith("Essentials"):
|
||||
return ESSENTIALS_LICENSE
|
||||
else:
|
||||
return STANDARD_LICENSE
|
205
ironic/drivers/modules/ilo/power.py
Normal file
205
ironic/drivers/modules/ilo/power.py
Normal file
@ -0,0 +1,205 @@
|
||||
# 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 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)
|
@ -70,6 +70,14 @@ def get_test_seamicro_info():
|
||||
}
|
||||
|
||||
|
||||
def get_test_ilo_info():
|
||||
return {
|
||||
"ilo_address": "1.2.3.4",
|
||||
"ilo_username": "admin",
|
||||
"ilo_password": "fake",
|
||||
}
|
||||
|
||||
|
||||
def get_test_node(**kw):
|
||||
properties = {
|
||||
"cpu_arch": "x86_64",
|
||||
|
0
ironic/tests/drivers/ilo/__init__.py
Normal file
0
ironic/tests/drivers/ilo/__init__.py
Normal file
156
ironic/tests/drivers/ilo/test_common.py
Normal file
156
ironic/tests/drivers/ilo/test_common.py
Normal file
@ -0,0 +1,156 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Test class for common methods used by iLO modules."""
|
||||
|
||||
import mock
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.drivers.modules.ilo import common as ilo_common
|
||||
from ironic.openstack.common import context
|
||||
from ironic.openstack.common import importutils
|
||||
from ironic.tests import base
|
||||
from ironic.tests.db import utils as db_utils
|
||||
from ironic.tests.objects import utils as obj_utils
|
||||
|
||||
ilo_client = importutils.try_import('proliantutils.ilo.ribcl')
|
||||
|
||||
|
||||
INFO_DICT = db_utils.get_test_ilo_info()
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class IloCommonMethodsTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(IloCommonMethodsTestCase, self).setUp()
|
||||
self.dbapi = dbapi.get_instance()
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def test_parse_driver_info(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
info = ilo_common.parse_driver_info(node)
|
||||
|
||||
self.assertIsNotNone(info.get('ilo_address'))
|
||||
self.assertIsNotNone(info.get('ilo_username'))
|
||||
self.assertIsNotNone(info.get('ilo_password'))
|
||||
self.assertIsNotNone(info.get('client_timeout'))
|
||||
self.assertIsNotNone(info.get('client_port'))
|
||||
|
||||
def test_parse_driver_info_missing_address(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
del node.driver_info['ilo_address']
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
ilo_common.parse_driver_info, node)
|
||||
|
||||
def test_parse_driver_info_missing_username(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
del node.driver_info['ilo_username']
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
ilo_common.parse_driver_info, node)
|
||||
|
||||
def test_parse_driver_info_missing_password(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
del node.driver_info['ilo_password']
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
ilo_common.parse_driver_info, node)
|
||||
|
||||
def test_parse_driver_info_invalid_timeout(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
node.driver_info['client_timeout'] = 'qwe'
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
ilo_common.parse_driver_info, node)
|
||||
|
||||
def test_parse_driver_info_invalid_port(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
node.driver_info['client_port'] = 'qwe'
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
ilo_common.parse_driver_info, node)
|
||||
|
||||
def test_parse_driver_info_missing_multiple_params(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
del node.driver_info['ilo_password']
|
||||
node.driver_info['client_port'] = 'qwe'
|
||||
try:
|
||||
ilo_common.parse_driver_info(node)
|
||||
self.fail("parse_driver_info did not throw exception.")
|
||||
except exception.InvalidParameterValue as e:
|
||||
self.assertIn('ilo_password', str(e))
|
||||
self.assertIn('client_port', str(e))
|
||||
|
||||
@mock.patch.object(ilo_common, 'ilo_client')
|
||||
def test_get_ilo_object(self, ilo_client_mock):
|
||||
info = INFO_DICT
|
||||
info['client_timeout'] = 60
|
||||
info['client_port'] = 443
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
ilo_client_mock.IloClient.return_value = 'ilo_object'
|
||||
returned_ilo_object = ilo_common.get_ilo_object(node)
|
||||
ilo_client_mock.IloClient.assert_called_with(
|
||||
INFO_DICT['ilo_address'],
|
||||
INFO_DICT['ilo_username'],
|
||||
INFO_DICT['ilo_password'],
|
||||
info['client_timeout'],
|
||||
info['client_port'])
|
||||
self.assertEqual('ilo_object', returned_ilo_object)
|
||||
|
||||
@mock.patch.object(ilo_common, 'ilo_client')
|
||||
def test_get_ilo_license(self, ilo_client_mock):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
ilo_advanced_license = {'LICENSE_TYPE': 'iLO 3 Advanced'}
|
||||
ilo_standard_license = {'LICENSE_TYPE': 'iLO 3'}
|
||||
|
||||
ilo_mock_object = ilo_client_mock.IloClient.return_value
|
||||
ilo_mock_object.get_all_licenses.return_value = ilo_advanced_license
|
||||
|
||||
license = ilo_common.get_ilo_license(node)
|
||||
self.assertEqual(license, ilo_common.ADVANCED_LICENSE)
|
||||
|
||||
ilo_mock_object.get_all_licenses.return_value = ilo_standard_license
|
||||
license = ilo_common.get_ilo_license(node)
|
||||
self.assertEqual(license, ilo_common.STANDARD_LICENSE)
|
||||
|
||||
@mock.patch.object(ilo_common, 'ilo_client')
|
||||
def test_get_ilo_license_fail(self, ilo_client_mock):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=INFO_DICT)
|
||||
ilo_client_mock.IloError = Exception
|
||||
ilo_mock_object = ilo_client_mock.IloClient.return_value
|
||||
ilo_mock_object.get_all_licenses.side_effect = [Exception()]
|
||||
self.assertRaises(exception.IloOperationError,
|
||||
ilo_common.get_ilo_license,
|
||||
node)
|
195
ironic/tests/drivers/ilo/test_power.py
Normal file
195
ironic/tests/drivers/ilo/test_power.py
Normal file
@ -0,0 +1,195 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Test class for IloPower module."""
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.drivers.modules.ilo import common as ilo_common
|
||||
from ironic.drivers.modules.ilo import power as ilo_power
|
||||
from ironic.openstack.common import context
|
||||
from ironic.openstack.common import importutils
|
||||
from ironic.tests import base
|
||||
from ironic.tests.conductor import utils as mgr_utils
|
||||
from ironic.tests.db import utils as db_utils
|
||||
from ironic.tests.objects import utils as obj_utils
|
||||
|
||||
ilo_client = importutils.try_import('proliantutils.ilo.ribcl')
|
||||
|
||||
INFO_DICT = db_utils.get_test_ilo_info()
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@mock.patch.object(ilo_common, 'ilo_client')
|
||||
@mock.patch.object(ilo_power, 'ilo_client')
|
||||
class IloPowerInternalMethodsTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(IloPowerInternalMethodsTestCase, self).setUp()
|
||||
driver_info = INFO_DICT
|
||||
mgr_utils.mock_the_extension_manager(driver="ilo")
|
||||
n = db_utils.get_test_node(
|
||||
driver='ilo',
|
||||
driver_info=driver_info,
|
||||
instance_uuid='instance_uuid_123')
|
||||
self.dbapi = dbapi.get_instance()
|
||||
self.node = self.dbapi.create_node(n)
|
||||
self.context = context.get_admin_context()
|
||||
CONF.set_override('power_retry', 2, 'ilo')
|
||||
CONF.set_override('power_wait', 0, 'ilo')
|
||||
|
||||
def test__get_power_state(self, power_ilo_client_mock,
|
||||
common_ilo_client_mock):
|
||||
ilo_mock_object = common_ilo_client_mock.IloClient.return_value
|
||||
ilo_mock_object.get_host_power_status.return_value = 'ON'
|
||||
|
||||
self.assertEqual(
|
||||
states.POWER_ON, ilo_power._get_power_state(self.node))
|
||||
|
||||
ilo_mock_object.get_host_power_status.return_value = 'OFF'
|
||||
self.assertEqual(
|
||||
states.POWER_OFF, ilo_power._get_power_state(self.node))
|
||||
|
||||
ilo_mock_object.get_host_power_status.return_value = 'ERROR'
|
||||
self.assertEqual(states.ERROR, ilo_power._get_power_state(self.node))
|
||||
|
||||
def test__get_power_state_fail(self, power_ilo_client_mock,
|
||||
common_ilo_client_mock):
|
||||
power_ilo_client_mock.IloError = Exception
|
||||
ilo_mock_object = common_ilo_client_mock.IloClient.return_value
|
||||
ilo_mock_object.get_host_power_status.side_effect = [Exception()]
|
||||
|
||||
self.assertRaises(exception.IloOperationError,
|
||||
ilo_power._get_power_state,
|
||||
self.node)
|
||||
ilo_mock_object.get_host_power_status.assert_called_once_with()
|
||||
|
||||
def test__set_power_state_invalid_state(self, power_ilo_client_mock,
|
||||
common_ilo_client_mock):
|
||||
power_ilo_client_mock.IloError = Exception
|
||||
self.assertRaises(exception.IloOperationError,
|
||||
ilo_power._set_power_state,
|
||||
self.node,
|
||||
states.ERROR)
|
||||
|
||||
def test__set_power_state_reboot_fail(self, power_ilo_client_mock,
|
||||
common_ilo_client_mock):
|
||||
power_ilo_client_mock.IloError = Exception
|
||||
ilo_mock_object = common_ilo_client_mock.IloClient.return_value
|
||||
ilo_mock_object.reset_server.side_effect = Exception()
|
||||
|
||||
self.assertRaises(exception.IloOperationError,
|
||||
ilo_power._set_power_state,
|
||||
self.node,
|
||||
states.REBOOT)
|
||||
ilo_mock_object.reset_server.assert_called_once_with()
|
||||
|
||||
def test__set_power_state_reboot_ok(self, power_ilo_client_mock,
|
||||
common_ilo_client_mock):
|
||||
power_ilo_client_mock.IloError = Exception
|
||||
ilo_mock_object = common_ilo_client_mock.IloClient.return_value
|
||||
ilo_mock_object.get_host_power_status.side_effect = ['ON', 'OFF', 'ON']
|
||||
|
||||
ilo_power._set_power_state(self.node, states.REBOOT)
|
||||
|
||||
ilo_mock_object.reset_server.assert_called_once_with()
|
||||
|
||||
def test__set_power_state_off_fail(self, power_ilo_client_mock,
|
||||
common_ilo_client_mock):
|
||||
power_ilo_client_mock.IloError = Exception
|
||||
ilo_mock_object = common_ilo_client_mock.IloClient.return_value
|
||||
ilo_mock_object.get_host_power_status.return_value = 'ON'
|
||||
|
||||
self.assertRaises(exception.PowerStateFailure,
|
||||
ilo_power._set_power_state,
|
||||
self.node,
|
||||
states.POWER_OFF)
|
||||
|
||||
ilo_mock_object.get_host_power_status.assert_called_with()
|
||||
ilo_mock_object.hold_pwr_btn.assert_called_once_with()
|
||||
|
||||
def test__set_power_state_on_ok(self, power_ilo_client_mock,
|
||||
common_ilo_client_mock):
|
||||
power_ilo_client_mock.IloError = Exception
|
||||
ilo_mock_object = common_ilo_client_mock.IloClient.return_value
|
||||
ilo_mock_object.get_host_power_status.side_effect = ['OFF', 'ON']
|
||||
|
||||
target_state = states.POWER_ON
|
||||
ilo_power._set_power_state(self.node, target_state)
|
||||
ilo_mock_object.get_host_power_status.assert_called_with()
|
||||
ilo_mock_object.set_host_power.assert_called_once_with('ON')
|
||||
|
||||
|
||||
class IloPowerTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.context = context.get_admin_context()
|
||||
super(IloPowerTestCase, self).setUp()
|
||||
driver_info = INFO_DICT
|
||||
mgr_utils.mock_the_extension_manager(driver="ilo")
|
||||
self.dbapi = dbapi.get_instance()
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='ilo',
|
||||
driver_info=driver_info)
|
||||
|
||||
@mock.patch.object(ilo_common, 'parse_driver_info')
|
||||
def test_validate(self, mock_drvinfo):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.driver.power.validate(task)
|
||||
mock_drvinfo.assert_once_called_with(task.node)
|
||||
|
||||
@mock.patch.object(ilo_common, 'parse_driver_info')
|
||||
def test_validate_fail(self, mock_drvinfo):
|
||||
side_effect = exception.InvalidParameterValue("Invalid Input")
|
||||
mock_drvinfo.side_effect = side_effect
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.power.validate,
|
||||
task)
|
||||
|
||||
@mock.patch.object(ilo_power, '_get_power_state')
|
||||
def test_get_power_state(self, mock_get_power):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
mock_get_power.return_value = states.POWER_ON
|
||||
self.assertEqual(states.POWER_ON,
|
||||
task.driver.power.get_power_state(task))
|
||||
mock_get_power.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(ilo_power, '_set_power_state')
|
||||
def test_set_power_state(self, mock_set_power):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
mock_set_power.return_value = states.POWER_ON
|
||||
task.driver.power.set_power_state(task, states.POWER_ON)
|
||||
mock_set_power.assert_called_once_with(task.node, states.POWER_ON)
|
||||
|
||||
@mock.patch.object(ilo_power, '_set_power_state')
|
||||
@mock.patch.object(ilo_power, '_get_power_state')
|
||||
def test_reboot(self, mock_get_power, mock_set_power):
|
||||
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)
|
||||
mock_get_power.assert_called_once_with(task.node)
|
||||
mock_set_power.assert_called_once_with(task.node, states.REBOOT)
|
@ -23,6 +23,7 @@ Any external library required by a third-party driver should be mocked here.
|
||||
Current list of mocked libraries:
|
||||
seamicroclient
|
||||
ipminative
|
||||
proliantutils
|
||||
"""
|
||||
|
||||
import sys
|
||||
@ -74,3 +75,11 @@ if not pyghmi:
|
||||
|
||||
if 'ironic.drivers.modules.ipminative' in sys.modules:
|
||||
reload(sys.modules['ironic.drivers.modules.ipminative'])
|
||||
|
||||
proliantutils = importutils.try_import('proliantutils')
|
||||
if not proliantutils:
|
||||
mock_proliant_utils = mock.MagicMock()
|
||||
sys.modules['proliantutils'] = mock_proliant_utils
|
||||
|
||||
if 'ironic.drivers.ilo' in sys.modules:
|
||||
reload(sys.modules['ironic.drivers.ilo'])
|
||||
|
Loading…
x
Reference in New Issue
Block a user