summaryrefslogtreecommitdiff
path: root/ironic/drivers/modules/ipmitool.py
diff options
context:
space:
mode:
Diffstat (limited to 'ironic/drivers/modules/ipmitool.py')
-rw-r--r--ironic/drivers/modules/ipmitool.py72
1 files changed, 58 insertions, 14 deletions
diff --git a/ironic/drivers/modules/ipmitool.py b/ironic/drivers/modules/ipmitool.py
index 2826dd1e7..55fc1fa2d 100644
--- a/ironic/drivers/modules/ipmitool.py
+++ b/ironic/drivers/modules/ipmitool.py
@@ -111,6 +111,9 @@ ipmitool_command_options = {
'dual_bridge': ['ipmitool', '-m', '0', '-b', '0', '-t', '0',
'-B', '0', '-T', '0', '-h']}
+# Note(TheJulia): This string is hardcoded in ipmitool's lanplus driver.
+IPMITOOL_RETRYABLE_FAILURES = ['insufficient resources for session']
+
def _check_option_support(options):
"""Checks if the specific ipmitool options are supported on host.
@@ -335,32 +338,68 @@ def _exec_ipmitool(driver_info, command):
args.append(driver_info[name])
# specify retry timing more precisely, if supported
+ num_tries = max(
+ (CONF.ipmi.retry_timeout // CONF.ipmi.min_command_interval), 1)
+
if _is_option_supported('timing'):
- num_tries = max(
- (CONF.ipmi.retry_timeout // CONF.ipmi.min_command_interval), 1)
args.append('-R')
args.append(str(num_tries))
args.append('-N')
args.append(str(CONF.ipmi.min_command_interval))
- # 'ipmitool' command will prompt password if there is no '-f' option,
- # we set it to '\0' to write a password file to support empty password
- with _make_password_file(driver_info['password'] or '\0') as pw_file:
- args.append('-f')
- args.append(pw_file)
- args.extend(command.split(" "))
+ end_time = (_time() + CONF.ipmi.retry_timeout)
+
+ while True:
+ num_tries = num_tries - 1
# NOTE(deva): ensure that no communications are sent to a BMC more
# often than once every min_command_interval seconds.
time_till_next_poll = CONF.ipmi.min_command_interval - (
- time.time() - LAST_CMD_TIME.get(driver_info['address'], 0))
+ _time() - LAST_CMD_TIME.get(driver_info['address'], 0))
if time_till_next_poll > 0:
time.sleep(time_till_next_poll)
- try:
- out, err = utils.execute(*args)
- finally:
- LAST_CMD_TIME[driver_info['address']] = time.time()
- return out, err
+ # Resetting the list that will be utilized so the password arguments
+ # from any previous execution are preserved.
+ cmd_args = args[:]
+ # 'ipmitool' command will prompt password if there is no '-f'
+ # option, we set it to '\0' to write a password file to support
+ # empty password
+ with _make_password_file(
+ driver_info['password'] or '\0'
+ ) as pw_file:
+ cmd_args.append('-f')
+ cmd_args.append(pw_file)
+ cmd_args.extend(command.split(" "))
+ try:
+ out, err = utils.execute(*cmd_args)
+ return out, err
+ except processutils.ProcessExecutionError as e:
+ with excutils.save_and_reraise_exception() as ctxt:
+ err_list = [x for x in IPMITOOL_RETRYABLE_FAILURES
+ if x in e.message]
+ if ((_time() > end_time) or
+ (num_tries == 0) or
+ not err_list):
+ LOG.error(_LE('IPMI Error attempting to execute '
+ '"%(cmd)s" for node %(node)s. '
+ 'Error: %(error)s'),
+ {
+ 'node': driver_info['uuid'],
+ 'cmd': e.cmd,
+ 'error': e
+ })
+ else:
+ ctxt.reraise = False
+ LOG.warning(_LW('IPMI Error encountered, retrying '
+ '"%(cmd)s" for node %(node)s '
+ 'Error: %(error)s'),
+ {
+ 'node': driver_info['uuid'],
+ 'cmd': e.cmd,
+ 'error': e
+ })
+ finally:
+ LAST_CMD_TIME[driver_info['address']] = _time()
def _sleep_time(iter):
@@ -587,6 +626,11 @@ def send_raw(task, raw_bytes):
raise exception.IPMIFailure(cmd=cmd)
+def _time():
+ """Wrapper for time.time() enabling simplified unit testing."""
+ return time.time()
+
+
class IPMIPower(base.PowerInterface):
def __init__(self):