Merge "Add retry options to iBoot power driver"

This commit is contained in:
Jenkins 2015-09-09 19:56:47 +00:00 committed by Gerrit Code Review
commit fcef218289
3 changed files with 127 additions and 26 deletions

View File

@ -946,6 +946,20 @@
#auth_strategy=keystone
[iboot]
#
# Options defined in ironic.drivers.modules.iboot
#
# Maximum retries for iBoot operations (integer value)
#max_retry=3
# Time between retry attempts for iBoot operations (integer
# value)
#retry_interval=1
[ilo]
#

View File

@ -19,7 +19,9 @@
Ironic iBoot PDU power manager.
"""
from oslo_config import cfg
from oslo_log import log as logging
from oslo_service import loopingcall
from oslo_utils import importutils
from ironic.common import exception
@ -31,6 +33,20 @@ from ironic.drivers import base
iboot = importutils.try_import('iboot')
opts = [
cfg.IntOpt('max_retry',
default=3,
help=_('Maximum retries for iBoot operations')),
cfg.IntOpt('retry_interval',
default=1,
help=_('Time between retry attempts for iBoot operations')),
]
CONF = cfg.CONF
opt_group = cfg.OptGroup(name='iboot',
title='Options for the iBoot power driver')
CONF.register_group(opt_group)
CONF.register_opts(opts, opt_group)
LOG = logging.getLogger(__name__)
@ -95,30 +111,66 @@ def _get_connection(driver_info):
def _switch(driver_info, enabled):
conn = _get_connection(driver_info)
relay_id = driver_info['relay_id']
return conn.switch(relay_id, enabled)
def _wait_for_switch(mutable):
if mutable['retries'] > CONF.iboot.max_retry:
LOG.warning(_LW(
'Reached maximum number of attempts ( %(attempts)d ) to set '
'power state for node %(node)s to "%(op)s"'),
{'attempts': mutable['retries'], 'node': driver_info['uuid'],
'op': states.POWER_ON if enabled else states.POWER_OFF})
raise loopingcall.LoopingCallDone()
try:
mutable['retries'] += 1
mutable['response'] = conn.switch(relay_id, enabled)
if mutable['response']:
raise loopingcall.LoopingCallDone()
except (TypeError, IndexError):
LOG.warning(_LW("Cannot call set power state for node '%(node)s' "
"at relay '%(relay)s'. iBoot switch() failed."),
{'node': driver_info['uuid'], 'relay': relay_id})
mutable = {'response': False, 'retries': 0}
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_switch,
mutable)
timer.start(interval=CONF.iboot.retry_interval).wait()
return mutable['response']
def _power_status(driver_info):
conn = _get_connection(driver_info)
relay_id = driver_info['relay_id']
try:
response = conn.get_relays()
status = response[relay_id - 1]
except TypeError:
msg = (_("Cannot get power status for node '%(node)s'. iBoot "
"get_relays() failed.") % {'node': driver_info['uuid']})
LOG.error(msg)
raise exception.IBootOperationError(message=msg)
except IndexError:
LOG.warning(_LW("Cannot get power status for node '%(node)s' at relay "
"'%(relay)s'. iBoot get_relays() failed."),
{'node': driver_info['uuid'], 'relay': relay_id})
return states.ERROR
if status:
return states.POWER_ON
else:
return states.POWER_OFF
def _wait_for_power_status(mutable):
if mutable['retries'] > CONF.iboot.max_retry:
LOG.warning(_LW(
'Reached maximum number of attempts ( %(attempts)d ) to get '
'power state for node %(node)s'),
{'attempts': mutable['retries'], 'node': driver_info['uuid']})
raise loopingcall.LoopingCallDone()
try:
mutable['retries'] += 1
response = conn.get_relays()
status = response[relay_id - 1]
if status:
mutable['state'] = states.POWER_ON
else:
mutable['state'] = states.POWER_OFF
raise loopingcall.LoopingCallDone()
except (TypeError, IndexError):
LOG.warning(_LW("Cannot get power state for node '%(node)s' at "
"relay '%(relay)s'. iBoot get_relays() failed."),
{'node': driver_info['uuid'], 'relay': relay_id})
mutable = {'state': states.ERROR, 'retries': 0}
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_status,
mutable)
timer.start(interval=CONF.iboot.retry_interval).wait()
return mutable['state']
class IBootPower(base.PowerInterface):

View File

@ -37,6 +37,11 @@ INFO_DICT = db_utils.get_test_iboot_info()
class IBootPrivateMethodTestCase(db_base.DbTestCase):
def setUp(self):
super(IBootPrivateMethodTestCase, self).setUp()
self.config(max_retry=0, group='iboot')
self.config(retry_interval=0, group='iboot')
def test__parse_driver_info_good(self):
node = obj_utils.create_test_node(
self.context,
@ -169,10 +174,8 @@ class IBootPrivateMethodTestCase(db_base.DbTestCase):
driver_info=INFO_DICT)
info = iboot._parse_driver_info(node)
self.assertRaises(exception.IBootOperationError,
iboot._power_status,
info)
status = iboot._power_status(info)
self.assertEqual(states.ERROR, status)
mock_get_conn.assert_called_once_with(info)
mock_connection.get_relays.assert_called_once_with()
@ -189,10 +192,8 @@ class IBootPrivateMethodTestCase(db_base.DbTestCase):
driver_info=INFO_DICT)
info = iboot._parse_driver_info(node)
self.assertRaises(exception.IBootOperationError,
iboot._power_status,
info)
status = iboot._power_status(info)
self.assertEqual(states.ERROR, status)
mock_get_conn.assert_called_once_with(info)
mock_connection.get_relays.assert_called_once_with()
@ -231,6 +232,26 @@ class IBootPrivateMethodTestCase(db_base.DbTestCase):
mock_get_conn.assert_called_once_with(info)
mock_connection.get_relays.assert_called_once_with()
@mock.patch.object(iboot, '_get_connection', autospec=True)
def test__power_status_retries(self, mock_get_conn):
self.config(max_retry=1, group='iboot')
mock_connection = mock.MagicMock(spec_set=['get_relays'])
side_effect = TypeError("Surprise!")
mock_connection.get_relays.side_effect = side_effect
mock_get_conn.return_value = mock_connection
node = obj_utils.create_test_node(
self.context,
driver='fake_iboot',
driver_info=INFO_DICT)
info = iboot._parse_driver_info(node)
status = iboot._power_status(info)
self.assertEqual(states.ERROR, status)
mock_get_conn.assert_called_once_with(info)
self.assertEqual(2, mock_connection.get_relays.call_count)
class IBootDriverTestCase(db_base.DbTestCase):
@ -318,6 +339,20 @@ class IBootDriverTestCase(db_base.DbTestCase):
self.assertEqual(manager.mock_calls, expected)
@mock.patch.object(iboot, '_power_status', autospec=True)
@mock.patch.object(iboot, '_get_connection', autospec=True)
def test__switch_retries(self, mock_get_conn, mock_power_status):
self.config(max_retry=1, group='iboot')
mock_power_status.return_value = states.POWER_ON
mock_connection = mock.MagicMock(spec_set=['switch'])
side_effect = TypeError("Surprise!")
mock_connection.switch.side_effect = side_effect
mock_get_conn.return_value = mock_connection
iboot._switch(self.info, False)
self.assertEqual(2, mock_connection.switch.call_count)
@mock.patch.object(iboot, '_power_status', autospec=True)
def test_get_power_state(self, mock_power_status):
mock_power_status.return_value = states.POWER_ON