IPMITool: add IPMISocatConsole and IPMIConsole class
IPMISocatConsole is a new console interface class using ipmitool and socat. It has the same APIs of IPMIShellinaboxConsole class and provides TCP4/TCP6 connection service to connect serial-on-LAN of nodes. IPMIConsole is a new console interface class using ipmitool and used as parent class of IPMIShellinaboxConsole and IPMISocatConsol class. This patch set implements new console driver interfaces IPMISocatConsole. To use PXE + IPMItool + socat, specify pxe_ipmitool_socat. To use IPA + IPMItool + socat, specify agent_ipmitool_socat. Spec: https://review.openstack.org/#/c/319505/ Partial-Bug: #1553083 Change-Id: I35a7dcb7e89baf16d096501fd44dbc12adc8c61e
This commit is contained in:
parent
22a80f77c5
commit
857372a226
@ -66,6 +66,25 @@ class AgentAndIPMIToolDriver(base.BaseDriver):
|
|||||||
'AgentAndIPMIToolDriver')
|
'AgentAndIPMIToolDriver')
|
||||||
|
|
||||||
|
|
||||||
|
class AgentAndIPMIToolAndSocatDriver(AgentAndIPMIToolDriver):
|
||||||
|
"""Agent + IPMITool + socat driver.
|
||||||
|
|
||||||
|
This driver implements the `core` functionality, combining
|
||||||
|
:class:`ironic.drivers.modules.ipmitool.IPMIPower` (for power on/off and
|
||||||
|
reboot) with :class:`ironic.drivers.modules.agent.AgentDeploy` (for
|
||||||
|
image deployment) and with
|
||||||
|
:class:`ironic.drivers.modules.ipmitool.IPMISocatConsole`.
|
||||||
|
This driver uses the socat console interface instead of the shellinabox
|
||||||
|
one.
|
||||||
|
Implementations are in those respective classes; this class is merely the
|
||||||
|
glue between them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
AgentAndIPMIToolDriver.__init__(self)
|
||||||
|
self.console = ipmitool.IPMISocatConsole()
|
||||||
|
|
||||||
|
|
||||||
class AgentAndIPMINativeDriver(base.BaseDriver):
|
class AgentAndIPMINativeDriver(base.BaseDriver):
|
||||||
"""Agent + IPMINative driver.
|
"""Agent + IPMINative driver.
|
||||||
|
|
||||||
|
@ -90,6 +90,17 @@ class FakeIPMIToolDriver(base.BaseDriver):
|
|||||||
self.management = ipmitool.IPMIManagement()
|
self.management = ipmitool.IPMIManagement()
|
||||||
|
|
||||||
|
|
||||||
|
class FakeIPMIToolSocatDriver(base.BaseDriver):
|
||||||
|
"""Example implementation of a Driver."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.power = ipmitool.IPMIPower()
|
||||||
|
self.console = ipmitool.IPMISocatConsole()
|
||||||
|
self.deploy = fake.FakeDeploy()
|
||||||
|
self.vendor = ipmitool.VendorPassthru()
|
||||||
|
self.management = ipmitool.IPMIManagement()
|
||||||
|
|
||||||
|
|
||||||
class FakePXEDriver(base.BaseDriver):
|
class FakePXEDriver(base.BaseDriver):
|
||||||
"""Example implementation of a Driver."""
|
"""Example implementation of a Driver."""
|
||||||
|
|
||||||
|
@ -355,11 +355,12 @@ def _parse_driver_info(node):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _exec_ipmitool(driver_info, command):
|
def _exec_ipmitool(driver_info, command, check_exit_code=None):
|
||||||
"""Execute the ipmitool command.
|
"""Execute the ipmitool command.
|
||||||
|
|
||||||
:param driver_info: the ipmitool parameters for accessing a node.
|
:param driver_info: the ipmitool parameters for accessing a node.
|
||||||
:param command: the ipmitool command to be executed.
|
:param command: the ipmitool command to be executed.
|
||||||
|
:param check_exit_code: Single bool, int, or list of allowed exit codes.
|
||||||
:returns: (stdout, stderr) from executing the command.
|
:returns: (stdout, stderr) from executing the command.
|
||||||
:raises: PasswordFileFailedToCreate from creating or writing to the
|
:raises: PasswordFileFailedToCreate from creating or writing to the
|
||||||
temporary file.
|
temporary file.
|
||||||
@ -414,6 +415,9 @@ def _exec_ipmitool(driver_info, command):
|
|||||||
# Resetting the list that will be utilized so the password arguments
|
# Resetting the list that will be utilized so the password arguments
|
||||||
# from any previous execution are preserved.
|
# from any previous execution are preserved.
|
||||||
cmd_args = args[:]
|
cmd_args = args[:]
|
||||||
|
extra_args = {}
|
||||||
|
if check_exit_code is not None:
|
||||||
|
extra_args['check_exit_code'] = check_exit_code
|
||||||
# 'ipmitool' command will prompt password if there is no '-f'
|
# 'ipmitool' command will prompt password if there is no '-f'
|
||||||
# option, we set it to '\0' to write a password file to support
|
# option, we set it to '\0' to write a password file to support
|
||||||
# empty password
|
# empty password
|
||||||
@ -422,7 +426,7 @@ def _exec_ipmitool(driver_info, command):
|
|||||||
cmd_args.append(pw_file)
|
cmd_args.append(pw_file)
|
||||||
cmd_args.extend(command.split(" "))
|
cmd_args.extend(command.split(" "))
|
||||||
try:
|
try:
|
||||||
out, err = utils.execute(*cmd_args)
|
out, err = utils.execute(*cmd_args, **extra_args)
|
||||||
return out, err
|
return out, err
|
||||||
except processutils.ProcessExecutionError as e:
|
except processutils.ProcessExecutionError as e:
|
||||||
with excutils.save_and_reraise_exception() as ctxt:
|
with excutils.save_and_reraise_exception() as ctxt:
|
||||||
@ -1090,8 +1094,8 @@ class VendorPassthru(base.VendorInterface):
|
|||||||
_parse_driver_info(task.node)
|
_parse_driver_info(task.node)
|
||||||
|
|
||||||
|
|
||||||
class IPMIShellinaboxConsole(base.ConsoleInterface):
|
class IPMIConsole(base.ConsoleInterface):
|
||||||
"""A ConsoleInterface that uses ipmitool and shellinabox."""
|
"""A base ConsoleInterface that uses ipmitool."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
try:
|
try:
|
||||||
@ -1128,10 +1132,11 @@ class IPMIShellinaboxConsole(base.ConsoleInterface):
|
|||||||
"Check the 'ipmi_protocol_version' parameter in "
|
"Check the 'ipmi_protocol_version' parameter in "
|
||||||
"node's driver_info"))
|
"node's driver_info"))
|
||||||
|
|
||||||
def start_console(self, task):
|
def _start_console(self, driver_info, start_method):
|
||||||
"""Start a remote console for the node.
|
"""Start a remote console for the node.
|
||||||
|
|
||||||
:param task: a task from TaskManager
|
:param task: a task from TaskManager
|
||||||
|
:param start_method: console_utils method to start console
|
||||||
:raises: InvalidParameterValue if required ipmi parameters are missing
|
:raises: InvalidParameterValue if required ipmi parameters are missing
|
||||||
:raises: PasswordFileFailedToCreate if unable to create a file
|
:raises: PasswordFileFailedToCreate if unable to create a file
|
||||||
containing the password
|
containing the password
|
||||||
@ -1139,8 +1144,6 @@ class IPMIShellinaboxConsole(base.ConsoleInterface):
|
|||||||
created
|
created
|
||||||
:raises: ConsoleSubprocessFailed when invoking the subprocess failed
|
:raises: ConsoleSubprocessFailed when invoking the subprocess failed
|
||||||
"""
|
"""
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
|
|
||||||
path = _console_pwfile_path(driver_info['uuid'])
|
path = _console_pwfile_path(driver_info['uuid'])
|
||||||
pw_file = console_utils.make_persistent_password_file(
|
pw_file = console_utils.make_persistent_password_file(
|
||||||
path, driver_info['password'] or '\0')
|
path, driver_info['password'] or '\0')
|
||||||
@ -1162,13 +1165,30 @@ class IPMIShellinaboxConsole(base.ConsoleInterface):
|
|||||||
ipmi_cmd += " -v"
|
ipmi_cmd += " -v"
|
||||||
ipmi_cmd += " sol activate"
|
ipmi_cmd += " sol activate"
|
||||||
try:
|
try:
|
||||||
console_utils.start_shellinabox_console(driver_info['uuid'],
|
start_method(driver_info['uuid'], driver_info['port'], ipmi_cmd)
|
||||||
driver_info['port'],
|
|
||||||
ipmi_cmd)
|
|
||||||
except (exception.ConsoleError, exception.ConsoleSubprocessFailed):
|
except (exception.ConsoleError, exception.ConsoleSubprocessFailed):
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
ironic_utils.unlink_without_raise(path)
|
ironic_utils.unlink_without_raise(path)
|
||||||
|
|
||||||
|
|
||||||
|
class IPMIShellinaboxConsole(IPMIConsole):
|
||||||
|
"""A ConsoleInterface that uses ipmitool and shellinabox."""
|
||||||
|
|
||||||
|
def start_console(self, task):
|
||||||
|
"""Start a remote console for the node.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager
|
||||||
|
:raises: InvalidParameterValue if required ipmi parameters are missing
|
||||||
|
:raises: PasswordFileFailedToCreate if unable to create a file
|
||||||
|
containing the password
|
||||||
|
:raises: ConsoleError if the directory for the PID file cannot be
|
||||||
|
created
|
||||||
|
:raises: ConsoleSubprocessFailed when invoking the subprocess failed
|
||||||
|
"""
|
||||||
|
driver_info = _parse_driver_info(task.node)
|
||||||
|
self._start_console(driver_info,
|
||||||
|
console_utils.start_shellinabox_console)
|
||||||
|
|
||||||
def stop_console(self, task):
|
def stop_console(self, task):
|
||||||
"""Stop the remote console session for the node.
|
"""Stop the remote console session for the node.
|
||||||
|
|
||||||
@ -1186,3 +1206,55 @@ class IPMIShellinaboxConsole(base.ConsoleInterface):
|
|||||||
driver_info = _parse_driver_info(task.node)
|
driver_info = _parse_driver_info(task.node)
|
||||||
url = console_utils.get_shellinabox_console_url(driver_info['port'])
|
url = console_utils.get_shellinabox_console_url(driver_info['port'])
|
||||||
return {'type': 'shellinabox', 'url': url}
|
return {'type': 'shellinabox', 'url': url}
|
||||||
|
|
||||||
|
|
||||||
|
class IPMISocatConsole(IPMIConsole):
|
||||||
|
"""A ConsoleInterface that uses ipmitool and socat."""
|
||||||
|
|
||||||
|
def start_console(self, task):
|
||||||
|
"""Start a remote console for the node.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager
|
||||||
|
:raises: InvalidParameterValue if required ipmi parameters are missing
|
||||||
|
:raises: PasswordFileFailedToCreate if unable to create a file
|
||||||
|
containing the password
|
||||||
|
:raises: ConsoleError if the directory for the PID file cannot be
|
||||||
|
created
|
||||||
|
:raises: ConsoleSubprocessFailed when invoking the subprocess failed
|
||||||
|
"""
|
||||||
|
driver_info = _parse_driver_info(task.node)
|
||||||
|
try:
|
||||||
|
self._exec_stop_console(driver_info)
|
||||||
|
except OSError:
|
||||||
|
# We need to drop any existing sol sessions with sol deactivate.
|
||||||
|
# OSError is raised when sol session is deactive, so we can
|
||||||
|
# ignore it.
|
||||||
|
pass
|
||||||
|
self._start_console(driver_info, console_utils.start_socat_console)
|
||||||
|
|
||||||
|
def stop_console(self, task):
|
||||||
|
"""Stop the remote console session for the node.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager
|
||||||
|
:raises: ConsoleError if unable to stop the console
|
||||||
|
"""
|
||||||
|
driver_info = _parse_driver_info(task.node)
|
||||||
|
try:
|
||||||
|
console_utils.stop_socat_console(task.node.uuid)
|
||||||
|
finally:
|
||||||
|
ironic_utils.unlink_without_raise(
|
||||||
|
_console_pwfile_path(task.node.uuid))
|
||||||
|
self._exec_stop_console(driver_info)
|
||||||
|
|
||||||
|
def _exec_stop_console(self, driver_info):
|
||||||
|
cmd = "sol deactivate"
|
||||||
|
_exec_ipmitool(driver_info, cmd, check_exit_code=[0, 1])
|
||||||
|
|
||||||
|
def get_console(self, task):
|
||||||
|
"""Get the type and connection information about the console.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager
|
||||||
|
"""
|
||||||
|
driver_info = _parse_driver_info(task.node)
|
||||||
|
url = console_utils.get_socat_console_url(driver_info['port'])
|
||||||
|
return {'type': 'socat', 'url': url}
|
||||||
|
@ -85,6 +85,25 @@ class PXEAndIPMIToolDriver(base.BaseDriver):
|
|||||||
self.raid = agent.AgentRAID()
|
self.raid = agent.AgentRAID()
|
||||||
|
|
||||||
|
|
||||||
|
class PXEAndIPMIToolAndSocatDriver(PXEAndIPMIToolDriver):
|
||||||
|
"""PXE + IPMITool + socat driver.
|
||||||
|
|
||||||
|
This driver implements the `core` functionality, combining
|
||||||
|
:class:`ironic.drivers.modules.ipmi.IPMI` for power on/off
|
||||||
|
and reboot with
|
||||||
|
:class:`ironic.drivers.modules.iscsi_deploy.ISCSIDeploy` (for
|
||||||
|
image deployment) and with
|
||||||
|
:class:`ironic.drivers.modules.ipmitool.IPMISocatConsole`.
|
||||||
|
This driver uses the socat console interface instead of the shellinabox
|
||||||
|
one.
|
||||||
|
Implementations are in those respective
|
||||||
|
classes; this class is merely the glue between them.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
PXEAndIPMIToolDriver.__init__(self)
|
||||||
|
self.console = ipmitool.IPMISocatConsole()
|
||||||
|
|
||||||
|
|
||||||
class PXEAndSSHDriver(base.BaseDriver):
|
class PXEAndSSHDriver(base.BaseDriver):
|
||||||
"""PXE + SSH driver.
|
"""PXE + SSH driver.
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import tempfile
|
|||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
|
|
||||||
|
from ironic_lib import utils as ironic_utils
|
||||||
import mock
|
import mock
|
||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -108,7 +109,7 @@ class IPMIToolCheckInitTestCase(base.TestCase):
|
|||||||
ipmi.TMP_DIR_CHECKED = True
|
ipmi.TMP_DIR_CHECKED = True
|
||||||
ipmi.IPMIPower()
|
ipmi.IPMIPower()
|
||||||
mock_support.assert_called_with(mock.ANY)
|
mock_support.assert_called_with(mock.ANY)
|
||||||
self.assertEqual(0, mock_check_dir.call_count)
|
self.assertFalse(mock_check_dir.called)
|
||||||
|
|
||||||
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
@mock.patch.object(utils, 'check_dir', autospec=True)
|
@mock.patch.object(utils, 'check_dir', autospec=True)
|
||||||
@ -130,7 +131,7 @@ class IPMIToolCheckInitTestCase(base.TestCase):
|
|||||||
|
|
||||||
ipmi.IPMIManagement()
|
ipmi.IPMIManagement()
|
||||||
mock_support.assert_called_with(mock.ANY)
|
mock_support.assert_called_with(mock.ANY)
|
||||||
self.assertEqual(0, mock_check_dir.call_count)
|
self.assertFalse(mock_check_dir.called)
|
||||||
|
|
||||||
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
@mock.patch.object(utils, 'check_dir', autospec=True)
|
@mock.patch.object(utils, 'check_dir', autospec=True)
|
||||||
@ -150,7 +151,7 @@ class IPMIToolCheckInitTestCase(base.TestCase):
|
|||||||
ipmi.TMP_DIR_CHECKED = True
|
ipmi.TMP_DIR_CHECKED = True
|
||||||
ipmi.VendorPassthru()
|
ipmi.VendorPassthru()
|
||||||
mock_support.assert_called_with(mock.ANY)
|
mock_support.assert_called_with(mock.ANY)
|
||||||
self.assertEqual(0, mock_check_dir.call_count)
|
self.assertFalse(mock_check_dir.called)
|
||||||
|
|
||||||
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
@mock.patch.object(utils, 'check_dir', autospec=True)
|
@mock.patch.object(utils, 'check_dir', autospec=True)
|
||||||
@ -170,7 +171,29 @@ class IPMIToolCheckInitTestCase(base.TestCase):
|
|||||||
ipmi.TMP_DIR_CHECKED = True
|
ipmi.TMP_DIR_CHECKED = True
|
||||||
ipmi.IPMIShellinaboxConsole()
|
ipmi.IPMIShellinaboxConsole()
|
||||||
mock_support.assert_called_with(mock.ANY)
|
mock_support.assert_called_with(mock.ANY)
|
||||||
self.assertEqual(0, mock_check_dir.call_count)
|
self.assertFalse(mock_check_dir.called)
|
||||||
|
|
||||||
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
|
@mock.patch.object(utils, 'check_dir', autospec=True)
|
||||||
|
def test_console_init_calls_for_socat(self, mock_check_dir, mock_support):
|
||||||
|
with mock.patch.object(ipmi, 'TMP_DIR_CHECKED'):
|
||||||
|
mock_support.return_value = True
|
||||||
|
ipmi.TMP_DIR_CHECKED = None
|
||||||
|
ipmi.IPMISocatConsole()
|
||||||
|
mock_support.assert_called_with(mock.ANY)
|
||||||
|
mock_check_dir.assert_called_once_with()
|
||||||
|
|
||||||
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
|
@mock.patch.object(utils, 'check_dir', autospec=True)
|
||||||
|
def test_console_init_calls_for_socat_already_checked(self,
|
||||||
|
mock_check_dir,
|
||||||
|
mock_support):
|
||||||
|
with mock.patch.object(ipmi, 'TMP_DIR_CHECKED'):
|
||||||
|
mock_support.return_value = True
|
||||||
|
ipmi.TMP_DIR_CHECKED = True
|
||||||
|
ipmi.IPMISocatConsole()
|
||||||
|
mock_support.assert_called_with(mock.ANY)
|
||||||
|
self.assertFalse(mock_check_dir.call_count)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
@ -977,14 +1000,14 @@ class IPMIToolPrivateMethodTestCase(db_base.DbTestCase):
|
|||||||
@mock.patch.object(utils, 'execute', autospec=True)
|
@mock.patch.object(utils, 'execute', autospec=True)
|
||||||
def test__exec_ipmitool_with_single_bridging(self,
|
def test__exec_ipmitool_with_single_bridging(self,
|
||||||
mock_exec,
|
mock_exec,
|
||||||
mock_support,
|
mock_pass,
|
||||||
mock_sleep):
|
mock_support):
|
||||||
single_bridge_info = dict(BRIDGE_INFO_DICT)
|
single_bridge_info = dict(BRIDGE_INFO_DICT)
|
||||||
single_bridge_info['ipmi_bridging'] = 'single'
|
single_bridge_info['ipmi_bridging'] = 'single'
|
||||||
node = obj_utils.get_test_node(self.context, driver='fake_ipmitool',
|
node = obj_utils.get_test_node(self.context, driver='fake_ipmitool',
|
||||||
driver_info=single_bridge_info)
|
driver_info=single_bridge_info)
|
||||||
# when support for single bridge command is called returns True
|
# when support for single bridge command is called returns True
|
||||||
mock_support.return_value = True
|
mock_pass.return_value = True
|
||||||
info = ipmi._parse_driver_info(node)
|
info = ipmi._parse_driver_info(node)
|
||||||
info['transit_channel'] = info['transit_address'] = None
|
info['transit_channel'] = info['transit_address'] = None
|
||||||
|
|
||||||
@ -1004,17 +1027,17 @@ class IPMIToolPrivateMethodTestCase(db_base.DbTestCase):
|
|||||||
expected = [mock.call('single_bridge'),
|
expected = [mock.call('single_bridge'),
|
||||||
mock.call('timing')]
|
mock.call('timing')]
|
||||||
# When support for timing command is called returns False
|
# When support for timing command is called returns False
|
||||||
mock_support.return_value = False
|
mock_pass.return_value = False
|
||||||
mock_exec.return_value = (None, None)
|
mock_exec.return_value = (None, None)
|
||||||
ipmi._exec_ipmitool(info, 'A B C')
|
ipmi._exec_ipmitool(info, 'A B C')
|
||||||
self.assertEqual(expected, mock_support.call_args_list)
|
self.assertEqual(expected, mock_pass.call_args_list)
|
||||||
mock_exec.assert_called_once_with(*args)
|
mock_exec.assert_called_once_with(*args)
|
||||||
|
|
||||||
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
|
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
|
||||||
@mock.patch.object(utils, 'execute', autospec=True)
|
@mock.patch.object(utils, 'execute', autospec=True)
|
||||||
def test__exec_ipmitool_exception(
|
def test__exec_ipmitool_exception(
|
||||||
self, mock_exec, mock_support, mock_sleep):
|
self, mock_exec, mock_pass, mock_support):
|
||||||
args = [
|
args = [
|
||||||
'ipmitool',
|
'ipmitool',
|
||||||
'-I', 'lanplus',
|
'-I', 'lanplus',
|
||||||
@ -1025,12 +1048,12 @@ class IPMIToolPrivateMethodTestCase(db_base.DbTestCase):
|
|||||||
'A', 'B', 'C',
|
'A', 'B', 'C',
|
||||||
]
|
]
|
||||||
|
|
||||||
mock_support.return_value = False
|
mock_pass.return_value = False
|
||||||
mock_exec.side_effect = processutils.ProcessExecutionError("x")
|
mock_exec.side_effect = processutils.ProcessExecutionError("x")
|
||||||
self.assertRaises(processutils.ProcessExecutionError,
|
self.assertRaises(processutils.ProcessExecutionError,
|
||||||
ipmi._exec_ipmitool,
|
ipmi._exec_ipmitool,
|
||||||
self.info, 'A B C')
|
self.info, 'A B C')
|
||||||
mock_support.assert_called_once_with('timing')
|
mock_pass.assert_called_once_with('timing')
|
||||||
mock_exec.assert_called_once_with(*args)
|
mock_exec.assert_called_once_with(*args)
|
||||||
self.assertEqual(1, mock_exec.call_count)
|
self.assertEqual(1, mock_exec.call_count)
|
||||||
|
|
||||||
@ -1117,7 +1140,7 @@ class IPMIToolPrivateMethodTestCase(db_base.DbTestCase):
|
|||||||
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
|
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
|
||||||
@mock.patch.object(utils, 'execute', autospec=True)
|
@mock.patch.object(utils, 'execute', autospec=True)
|
||||||
def test__exec_ipmitool_IPMI_version_1_5(
|
def test__exec_ipmitool_IPMI_version_1_5(
|
||||||
self, mock_exec, mock_support, mock_sleep):
|
self, mock_exec, mock_pass, mock_support):
|
||||||
self.info['protocol_version'] = '1.5'
|
self.info['protocol_version'] = '1.5'
|
||||||
# Assert it uses "-I lan" (1.5) instead of "-I lanplus" (2.0)
|
# Assert it uses "-I lan" (1.5) instead of "-I lanplus" (2.0)
|
||||||
args = [
|
args = [
|
||||||
@ -1130,17 +1153,17 @@ class IPMIToolPrivateMethodTestCase(db_base.DbTestCase):
|
|||||||
'A', 'B', 'C',
|
'A', 'B', 'C',
|
||||||
]
|
]
|
||||||
|
|
||||||
mock_support.return_value = False
|
mock_pass.return_value = False
|
||||||
mock_exec.return_value = (None, None)
|
mock_exec.return_value = (None, None)
|
||||||
ipmi._exec_ipmitool(self.info, 'A B C')
|
ipmi._exec_ipmitool(self.info, 'A B C')
|
||||||
mock_support.assert_called_once_with('timing')
|
mock_pass.assert_called_once_with('timing')
|
||||||
mock_exec.assert_called_once_with(*args)
|
mock_exec.assert_called_once_with(*args)
|
||||||
|
|
||||||
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
|
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
|
||||||
@mock.patch.object(utils, 'execute', autospec=True)
|
@mock.patch.object(utils, 'execute', autospec=True)
|
||||||
def test__exec_ipmitool_with_port(self, mock_exec, mock_support,
|
def test__exec_ipmitool_with_port(self, mock_exec, mock_pass,
|
||||||
mock_sleep):
|
mock_support):
|
||||||
self.info['dest_port'] = '1623'
|
self.info['dest_port'] = '1623'
|
||||||
ipmi.LAST_CMD_TIME = {}
|
ipmi.LAST_CMD_TIME = {}
|
||||||
args = [
|
args = [
|
||||||
@ -1154,14 +1177,34 @@ class IPMIToolPrivateMethodTestCase(db_base.DbTestCase):
|
|||||||
'A', 'B', 'C',
|
'A', 'B', 'C',
|
||||||
]
|
]
|
||||||
|
|
||||||
mock_support.return_value = False
|
mock_pass.return_value = False
|
||||||
mock_exec.return_value = (None, None)
|
mock_exec.return_value = (None, None)
|
||||||
|
|
||||||
ipmi._exec_ipmitool(self.info, 'A B C')
|
ipmi._exec_ipmitool(self.info, 'A B C')
|
||||||
|
|
||||||
mock_support.assert_called_once_with('timing')
|
mock_pass.assert_called_once_with('timing')
|
||||||
mock_exec.assert_called_once_with(*args)
|
mock_exec.assert_called_once_with(*args)
|
||||||
self.assertFalse(mock_sleep.called)
|
self.assertFalse(mock_support.called)
|
||||||
|
|
||||||
|
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
|
||||||
|
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
|
||||||
|
@mock.patch.object(utils, 'execute', autospec=True)
|
||||||
|
def test__exec_ipmitool_with_check_exit_code(self, mock_exec,
|
||||||
|
mock_pass, mock_support):
|
||||||
|
args = [
|
||||||
|
'ipmitool',
|
||||||
|
'-I', 'lanplus',
|
||||||
|
'-H', self.info['address'],
|
||||||
|
'-L', self.info['priv_level'],
|
||||||
|
'-U', self.info['username'],
|
||||||
|
'-f', awesome_password_filename,
|
||||||
|
'A', 'B', 'C',
|
||||||
|
]
|
||||||
|
mock_pass.return_value = False
|
||||||
|
mock_exec.return_value = (None, None)
|
||||||
|
ipmi._exec_ipmitool(self.info, 'A B C', check_exit_code=[0, 1])
|
||||||
|
mock_pass.assert_called_once_with('timing')
|
||||||
|
mock_exec.assert_called_once_with(*args, check_exit_code=[0, 1])
|
||||||
|
|
||||||
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
|
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
|
||||||
def test__power_status_on(self, mock_exec, mock_sleep):
|
def test__power_status_on(self, mock_exec, mock_sleep):
|
||||||
@ -1222,13 +1265,18 @@ class IPMIToolPrivateMethodTestCase(db_base.DbTestCase):
|
|||||||
|
|
||||||
class IPMIToolDriverTestCase(db_base.DbTestCase):
|
class IPMIToolDriverTestCase(db_base.DbTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self, terminal=None):
|
||||||
super(IPMIToolDriverTestCase, self).setUp()
|
super(IPMIToolDriverTestCase, self).setUp()
|
||||||
mgr_utils.mock_the_extension_manager(driver="fake_ipmitool")
|
if terminal is None:
|
||||||
self.driver = driver_factory.get_driver("fake_ipmitool")
|
self.driver_name = "fake_ipmitool"
|
||||||
|
else:
|
||||||
|
self.driver_name = "fake_ipmitool_socat"
|
||||||
|
|
||||||
|
mgr_utils.mock_the_extension_manager(driver=self.driver_name)
|
||||||
|
self.driver = driver_factory.get_driver(self.driver_name)
|
||||||
|
|
||||||
self.node = obj_utils.create_test_node(self.context,
|
self.node = obj_utils.create_test_node(self.context,
|
||||||
driver='fake_ipmitool',
|
driver=self.driver_name,
|
||||||
driver_info=INFO_DICT)
|
driver_info=INFO_DICT)
|
||||||
self.info = ipmi._parse_driver_info(self.node)
|
self.info = ipmi._parse_driver_info(self.node)
|
||||||
|
|
||||||
@ -1290,7 +1338,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
|
|
||||||
mock_on.return_value = states.POWER_ON
|
mock_on.return_value = states.POWER_ON
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.power.set_power_state(task,
|
self.driver.power.set_power_state(task,
|
||||||
states.POWER_ON)
|
states.POWER_ON)
|
||||||
|
|
||||||
@ -1306,7 +1354,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
|
|
||||||
mock_on.return_value = states.POWER_ON
|
mock_on.return_value = states.POWER_ON
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.power.set_power_state(task,
|
self.driver.power.set_power_state(task,
|
||||||
states.POWER_ON)
|
states.POWER_ON)
|
||||||
mock_next_boot.assert_called_once_with(task, self.info)
|
mock_next_boot.assert_called_once_with(task, self.info)
|
||||||
@ -1322,7 +1370,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
mock_off.return_value = states.POWER_OFF
|
mock_off.return_value = states.POWER_OFF
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.power.set_power_state(task,
|
self.driver.power.set_power_state(task,
|
||||||
states.POWER_OFF)
|
states.POWER_OFF)
|
||||||
|
|
||||||
@ -1336,7 +1384,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
|
|
||||||
mock_on.return_value = states.ERROR
|
mock_on.return_value = states.ERROR
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.assertRaises(exception.PowerStateFailure,
|
self.assertRaises(exception.PowerStateFailure,
|
||||||
self.driver.power.set_power_state,
|
self.driver.power.set_power_state,
|
||||||
task,
|
task,
|
||||||
@ -1346,7 +1394,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
self.assertFalse(mock_off.called)
|
self.assertFalse(mock_off.called)
|
||||||
|
|
||||||
def test_set_power_invalid_state(self):
|
def test_set_power_invalid_state(self):
|
||||||
with task_manager.acquire(self.context, self.node['uuid']) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
self.assertRaises(exception.InvalidParameterValue,
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
self.driver.power.set_power_state,
|
self.driver.power.set_power_state,
|
||||||
task,
|
task,
|
||||||
@ -1357,7 +1405,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
mock_exec.return_value = [None, None]
|
mock_exec.return_value = [None, None]
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.vendor.send_raw(task, http_method='POST',
|
self.driver.vendor.send_raw(task, http_method='POST',
|
||||||
raw_bytes='0x00 0x01')
|
raw_bytes='0x00 0x01')
|
||||||
|
|
||||||
@ -1368,7 +1416,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
mock_exec.side_effect = exception.PasswordFileFailedToCreate('error')
|
mock_exec.side_effect = exception.PasswordFileFailedToCreate('error')
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.assertRaises(exception.IPMIFailure,
|
self.assertRaises(exception.IPMIFailure,
|
||||||
self.driver.vendor.send_raw,
|
self.driver.vendor.send_raw,
|
||||||
task,
|
task,
|
||||||
@ -1380,7 +1428,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
mock_exec.return_value = [None, None]
|
mock_exec.return_value = [None, None]
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.vendor.bmc_reset(task, 'POST')
|
self.driver.vendor.bmc_reset(task, 'POST')
|
||||||
|
|
||||||
mock_exec.assert_called_once_with(self.info, 'bmc reset warm')
|
mock_exec.assert_called_once_with(self.info, 'bmc reset warm')
|
||||||
@ -1390,7 +1438,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
mock_exec.return_value = [None, None]
|
mock_exec.return_value = [None, None]
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.vendor.bmc_reset(task, 'POST', warm=False)
|
self.driver.vendor.bmc_reset(task, 'POST', warm=False)
|
||||||
|
|
||||||
mock_exec.assert_called_once_with(self.info, 'bmc reset cold')
|
mock_exec.assert_called_once_with(self.info, 'bmc reset cold')
|
||||||
@ -1400,7 +1448,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
mock_exec.side_effect = processutils.ProcessExecutionError()
|
mock_exec.side_effect = processutils.ProcessExecutionError()
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.assertRaises(exception.IPMIFailure,
|
self.assertRaises(exception.IPMIFailure,
|
||||||
self.driver.vendor.bmc_reset,
|
self.driver.vendor.bmc_reset,
|
||||||
task, 'POST')
|
task, 'POST')
|
||||||
@ -1418,7 +1466,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
mock.call.power_on(self.info)]
|
mock.call.power_on(self.info)]
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.power.reboot(task)
|
self.driver.power.reboot(task)
|
||||||
mock_next_boot.assert_called_once_with(task, self.info)
|
mock_next_boot.assert_called_once_with(task, self.info)
|
||||||
|
|
||||||
@ -1436,7 +1484,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
mock.call.power_on(self.info)]
|
mock.call.power_on(self.info)]
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.assertRaises(exception.PowerStateFailure,
|
self.assertRaises(exception.PowerStateFailure,
|
||||||
self.driver.power.reboot,
|
self.driver.power.reboot,
|
||||||
task)
|
task)
|
||||||
@ -1446,28 +1494,28 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
@mock.patch.object(ipmi, '_parse_driver_info', autospec=True)
|
@mock.patch.object(ipmi, '_parse_driver_info', autospec=True)
|
||||||
def test_vendor_passthru_validate__parse_driver_info_fail(self, info_mock):
|
def test_vendor_passthru_validate__parse_driver_info_fail(self, info_mock):
|
||||||
info_mock.side_effect = exception.InvalidParameterValue("bad")
|
info_mock.side_effect = exception.InvalidParameterValue("bad")
|
||||||
with task_manager.acquire(self.context, self.node['uuid']) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
self.assertRaises(exception.InvalidParameterValue,
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
self.driver.vendor.validate,
|
self.driver.vendor.validate,
|
||||||
task, method='send_raw', raw_bytes='0x00 0x01')
|
task, method='send_raw', raw_bytes='0x00 0x01')
|
||||||
info_mock.assert_called_once_with(task.node)
|
info_mock.assert_called_once_with(task.node)
|
||||||
|
|
||||||
def test_vendor_passthru_validate__send_raw_bytes_good(self):
|
def test_vendor_passthru_validate__send_raw_bytes_good(self):
|
||||||
with task_manager.acquire(self.context, self.node['uuid']) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
self.driver.vendor.validate(task,
|
self.driver.vendor.validate(task,
|
||||||
method='send_raw',
|
method='send_raw',
|
||||||
http_method='POST',
|
http_method='POST',
|
||||||
raw_bytes='0x00 0x01')
|
raw_bytes='0x00 0x01')
|
||||||
|
|
||||||
def test_vendor_passthru_validate__send_raw_bytes_fail(self):
|
def test_vendor_passthru_validate__send_raw_bytes_fail(self):
|
||||||
with task_manager.acquire(self.context, self.node['uuid']) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
self.assertRaises(exception.MissingParameterValue,
|
self.assertRaises(exception.MissingParameterValue,
|
||||||
self.driver.vendor.validate,
|
self.driver.vendor.validate,
|
||||||
task, method='send_raw')
|
task, method='send_raw')
|
||||||
|
|
||||||
@mock.patch.object(ipmi.VendorPassthru, 'send_raw', autospec=True)
|
@mock.patch.object(ipmi.VendorPassthru, 'send_raw', autospec=True)
|
||||||
def test_vendor_passthru_call_send_raw_bytes(self, raw_bytes_mock):
|
def test_vendor_passthru_call_send_raw_bytes(self, raw_bytes_mock):
|
||||||
with task_manager.acquire(self.context, self.node['uuid'],
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=False) as task:
|
shared=False) as task:
|
||||||
self.driver.vendor.send_raw(task, http_method='POST',
|
self.driver.vendor.send_raw(task, http_method='POST',
|
||||||
raw_bytes='0x00 0x01')
|
raw_bytes='0x00 0x01')
|
||||||
@ -1476,18 +1524,18 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
raw_bytes='0x00 0x01')
|
raw_bytes='0x00 0x01')
|
||||||
|
|
||||||
def test_vendor_passthru_validate__bmc_reset_good(self):
|
def test_vendor_passthru_validate__bmc_reset_good(self):
|
||||||
with task_manager.acquire(self.context, self.node['uuid']) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
self.driver.vendor.validate(task,
|
self.driver.vendor.validate(task,
|
||||||
method='bmc_reset')
|
method='bmc_reset')
|
||||||
|
|
||||||
def test_vendor_passthru_validate__bmc_reset_warm_good(self):
|
def test_vendor_passthru_validate__bmc_reset_warm_good(self):
|
||||||
with task_manager.acquire(self.context, self.node['uuid']) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
self.driver.vendor.validate(task,
|
self.driver.vendor.validate(task,
|
||||||
method='bmc_reset',
|
method='bmc_reset',
|
||||||
warm=True)
|
warm=True)
|
||||||
|
|
||||||
def test_vendor_passthru_validate__bmc_reset_cold_good(self):
|
def test_vendor_passthru_validate__bmc_reset_cold_good(self):
|
||||||
with task_manager.acquire(self.context, self.node['uuid']) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
self.driver.vendor.validate(task,
|
self.driver.vendor.validate(task,
|
||||||
method='bmc_reset',
|
method='bmc_reset',
|
||||||
warm=False)
|
warm=False)
|
||||||
@ -1496,7 +1544,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
def _vendor_passthru_call_bmc_reset(self, warm, expected,
|
def _vendor_passthru_call_bmc_reset(self, warm, expected,
|
||||||
mock_exec):
|
mock_exec):
|
||||||
mock_exec.return_value = [None, None]
|
mock_exec.return_value = [None, None]
|
||||||
with task_manager.acquire(self.context, self.node['uuid'],
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=False) as task:
|
shared=False) as task:
|
||||||
self.driver.vendor.bmc_reset(task, 'POST', warm=warm)
|
self.driver.vendor.bmc_reset(task, 'POST', warm=warm)
|
||||||
mock_exec.assert_called_once_with(
|
mock_exec.assert_called_once_with(
|
||||||
@ -1553,49 +1601,66 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
self.assertRaises(exception.InvalidParameterValue,
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
task.driver.console.validate, task)
|
task.driver.console.validate, task)
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
@mock.patch.object(ipmi.IPMIConsole, '_start_console', autospec=True)
|
||||||
autospec=True)
|
def test_start_console(self, mock_start):
|
||||||
def test_start_console(self, mock_exec):
|
mock_start.return_value = None
|
||||||
mock_exec.return_value = None
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node['uuid']) as task:
|
|
||||||
self.driver.console.start_console(task)
|
|
||||||
|
|
||||||
mock_exec.assert_called_once_with(self.info['uuid'],
|
|
||||||
self.info['port'],
|
|
||||||
mock.ANY)
|
|
||||||
self.assertTrue(mock_exec.called)
|
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
|
||||||
autospec=True)
|
|
||||||
def test_start_console_fail(self, mock_exec):
|
|
||||||
mock_exec.side_effect = exception.ConsoleSubprocessFailed(
|
|
||||||
error='error')
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node['uuid']) as task:
|
|
||||||
self.assertRaises(exception.ConsoleSubprocessFailed,
|
|
||||||
self.driver.console.start_console,
|
|
||||||
task)
|
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
|
||||||
autospec=True)
|
|
||||||
def test_start_console_fail_nodir(self, mock_exec):
|
|
||||||
mock_exec.side_effect = exception.ConsoleError()
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node.uuid) as task:
|
self.node.uuid) as task:
|
||||||
|
self.driver.console.start_console(task)
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
mock_start.assert_called_once_with(
|
||||||
|
self.driver.console, driver_info,
|
||||||
|
console_utils.start_shellinabox_console)
|
||||||
|
|
||||||
|
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
||||||
|
autospec=True)
|
||||||
|
def test__start_console(self, mock_start):
|
||||||
|
mock_start.return_value = None
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.driver.console._start_console(
|
||||||
|
driver_info, console_utils.start_shellinabox_console)
|
||||||
|
|
||||||
|
mock_start.assert_called_once_with(self.info['uuid'],
|
||||||
|
self.info['port'],
|
||||||
|
mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
||||||
|
autospec=True)
|
||||||
|
def test__start_console_fail(self, mock_start):
|
||||||
|
mock_start.side_effect = exception.ConsoleSubprocessFailed(
|
||||||
|
error='error')
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.assertRaises(exception.ConsoleSubprocessFailed,
|
||||||
|
self.driver.console._start_console,
|
||||||
|
driver_info,
|
||||||
|
console_utils.start_shellinabox_console)
|
||||||
|
|
||||||
|
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
||||||
|
autospec=True)
|
||||||
|
def test__start_console_fail_nodir(self, mock_start):
|
||||||
|
mock_start.side_effect = exception.ConsoleError()
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
self.assertRaises(exception.ConsoleError,
|
self.assertRaises(exception.ConsoleError,
|
||||||
self.driver.console.start_console,
|
self.driver.console._start_console,
|
||||||
task)
|
driver_info,
|
||||||
mock_exec.assert_called_once_with(self.node.uuid, mock.ANY, mock.ANY)
|
console_utils.start_shellinabox_console)
|
||||||
|
mock_start.assert_called_once_with(self.node.uuid, mock.ANY, mock.ANY)
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'make_persistent_password_file',
|
@mock.patch.object(console_utils, 'make_persistent_password_file',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
def test_start_console_empty_password(self, mock_exec, mock_pass):
|
def test__start_console_empty_password(self, mock_start, mock_pass):
|
||||||
driver_info = self.node.driver_info
|
driver_info = self.node.driver_info
|
||||||
del driver_info['ipmi_password']
|
del driver_info['ipmi_password']
|
||||||
self.node.driver_info = driver_info
|
self.node.driver_info = driver_info
|
||||||
@ -1603,24 +1668,25 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node.uuid) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.console.start_console(task)
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.driver.console._start_console(
|
||||||
|
driver_info, console_utils.start_shellinabox_console)
|
||||||
|
|
||||||
mock_pass.assert_called_once_with(mock.ANY, '\0')
|
mock_pass.assert_called_once_with(mock.ANY, '\0')
|
||||||
mock_exec.assert_called_once_with(self.info['uuid'],
|
mock_start.assert_called_once_with(self.info['uuid'],
|
||||||
self.info['port'],
|
self.info['port'],
|
||||||
mock.ANY)
|
mock.ANY)
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'stop_shellinabox_console',
|
@mock.patch.object(console_utils, 'stop_shellinabox_console',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
def test_stop_console(self, mock_exec):
|
def test_stop_console(self, mock_stop):
|
||||||
mock_exec.return_value = None
|
mock_stop.return_value = None
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
self.driver.console.stop_console(task)
|
self.driver.console.stop_console(task)
|
||||||
|
|
||||||
mock_exec.assert_called_once_with(self.info['uuid'])
|
mock_stop.assert_called_once_with(self.info['uuid'])
|
||||||
self.assertTrue(mock_exec.called)
|
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'stop_shellinabox_console',
|
@mock.patch.object(console_utils, 'stop_shellinabox_console',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@ -1637,18 +1703,17 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
|
|
||||||
@mock.patch.object(console_utils, 'get_shellinabox_console_url',
|
@mock.patch.object(console_utils, 'get_shellinabox_console_url',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
def test_get_console(self, mock_exec):
|
def test_get_console(self, mock_get):
|
||||||
url = 'http://localhost:4201'
|
url = 'http://localhost:4201'
|
||||||
mock_exec.return_value = url
|
mock_get.return_value = url
|
||||||
expected = {'type': 'shellinabox', 'url': url}
|
expected = {'type': 'shellinabox', 'url': url}
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
with task_manager.acquire(self.context,
|
||||||
self.node['uuid']) as task:
|
self.node.uuid) as task:
|
||||||
console_info = self.driver.console.get_console(task)
|
console_info = self.driver.console.get_console(task)
|
||||||
|
|
||||||
self.assertEqual(expected, console_info)
|
self.assertEqual(expected, console_info)
|
||||||
mock_exec.assert_called_once_with(self.info['port'])
|
mock_get.assert_called_once_with(self.info['port'])
|
||||||
self.assertTrue(mock_exec.called)
|
|
||||||
|
|
||||||
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
|
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
|
||||||
def test_management_interface_set_boot_device_ok(self, mock_exec):
|
def test_management_interface_set_boot_device_ok(self, mock_exec):
|
||||||
@ -1810,7 +1875,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
# Missing IPMI driver_info information
|
# Missing IPMI driver_info information
|
||||||
node = obj_utils.create_test_node(self.context,
|
node = obj_utils.create_test_node(self.context,
|
||||||
uuid=uuidutils.generate_uuid(),
|
uuid=uuidutils.generate_uuid(),
|
||||||
driver='fake_ipmitool')
|
driver=self.driver_name)
|
||||||
with task_manager.acquire(self.context, node.uuid) as task:
|
with task_manager.acquire(self.context, node.uuid) as task:
|
||||||
self.assertRaises(exception.MissingParameterValue,
|
self.assertRaises(exception.MissingParameterValue,
|
||||||
task.driver.management.validate, task)
|
task.driver.management.validate, task)
|
||||||
@ -2046,3 +2111,154 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||||||
ret = ipmi.send_raw(task, 'fake raw')
|
ret = ipmi.send_raw(task, 'fake raw')
|
||||||
|
|
||||||
self.assertEqual(fake_ret, ret)
|
self.assertEqual(fake_ret, ret)
|
||||||
|
|
||||||
|
|
||||||
|
class IPMIToolSocatDriverTestCase(IPMIToolDriverTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(IPMIToolSocatDriverTestCase, self).setUp(terminal="socat")
|
||||||
|
|
||||||
|
@mock.patch.object(ipmi.IPMIConsole, '_start_console', autospec=True)
|
||||||
|
@mock.patch.object(ipmi.IPMISocatConsole, '_exec_stop_console',
|
||||||
|
autospec=True)
|
||||||
|
def test_start_console(self, mock_stop, mock_start):
|
||||||
|
mock_start.return_value = None
|
||||||
|
mock_stop.return_value = None
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
self.driver.console.start_console(task)
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
mock_stop.assert_called_once_with(self.driver.console, driver_info)
|
||||||
|
mock_start.assert_called_once_with(
|
||||||
|
self.driver.console, driver_info,
|
||||||
|
console_utils.start_socat_console)
|
||||||
|
|
||||||
|
@mock.patch.object(console_utils, 'start_socat_console',
|
||||||
|
autospec=True)
|
||||||
|
def test__start_console(self, mock_start):
|
||||||
|
mock_start.return_value = None
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.driver.console._start_console(
|
||||||
|
driver_info, console_utils.start_socat_console)
|
||||||
|
|
||||||
|
mock_start.assert_called_once_with(self.info['uuid'],
|
||||||
|
self.info['port'],
|
||||||
|
mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch.object(console_utils, 'start_socat_console',
|
||||||
|
autospec=True)
|
||||||
|
def test__start_console_fail(self, mock_start):
|
||||||
|
mock_start.side_effect = exception.ConsoleSubprocessFailed(
|
||||||
|
error='error')
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.assertRaises(exception.ConsoleSubprocessFailed,
|
||||||
|
self.driver.console._start_console,
|
||||||
|
driver_info,
|
||||||
|
console_utils.start_socat_console)
|
||||||
|
|
||||||
|
mock_start.assert_called_once_with(self.info['uuid'],
|
||||||
|
self.info['port'],
|
||||||
|
mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch.object(console_utils, 'start_socat_console',
|
||||||
|
autospec=True)
|
||||||
|
def test__start_console_fail_nodir(self, mock_start):
|
||||||
|
mock_start.side_effect = exception.ConsoleError()
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.assertRaises(exception.ConsoleError,
|
||||||
|
self.driver.console._start_console,
|
||||||
|
driver_info,
|
||||||
|
console_utils.start_socat_console)
|
||||||
|
mock_start.assert_called_once_with(self.node.uuid, mock.ANY, mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch.object(console_utils, 'make_persistent_password_file',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(console_utils, 'start_socat_console',
|
||||||
|
autospec=True)
|
||||||
|
def test__start_console_empty_password(self, mock_start, mock_pass):
|
||||||
|
driver_info = self.node.driver_info
|
||||||
|
del driver_info['ipmi_password']
|
||||||
|
self.node.driver_info = driver_info
|
||||||
|
self.node.save()
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.driver.console._start_console(
|
||||||
|
driver_info, console_utils.start_socat_console)
|
||||||
|
|
||||||
|
mock_pass.assert_called_once_with(mock.ANY, '\0')
|
||||||
|
mock_start.assert_called_once_with(self.info['uuid'],
|
||||||
|
self.info['port'],
|
||||||
|
mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch.object(ipmi.IPMISocatConsole, '_exec_stop_console',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(console_utils, 'stop_socat_console',
|
||||||
|
autospec=True)
|
||||||
|
def test_stop_console(self, mock_stop, mock_exec_stop):
|
||||||
|
mock_stop.return_value = None
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.driver.console.stop_console(task)
|
||||||
|
|
||||||
|
mock_stop.assert_called_once_with(self.info['uuid'])
|
||||||
|
mock_exec_stop.assert_called_once_with(self.driver.console,
|
||||||
|
driver_info)
|
||||||
|
|
||||||
|
@mock.patch.object(ipmi.IPMISocatConsole, '_exec_stop_console',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ironic_utils, 'unlink_without_raise',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(console_utils, 'stop_socat_console',
|
||||||
|
autospec=True)
|
||||||
|
def test_stop_console_fail(self, mock_stop, mock_unlink, mock_exec_stop):
|
||||||
|
mock_stop.side_effect = exception.ConsoleError()
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
self.assertRaises(exception.ConsoleError,
|
||||||
|
self.driver.console.stop_console,
|
||||||
|
task)
|
||||||
|
|
||||||
|
mock_stop.assert_called_once_with(self.node.uuid)
|
||||||
|
mock_unlink.assert_called_once_with(
|
||||||
|
ipmi._console_pwfile_path(self.node.uuid))
|
||||||
|
self.assertFalse(mock_exec_stop.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
|
||||||
|
def test__exec_stop_console(self, mock_exec):
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
|
||||||
|
driver_info = ipmi._parse_driver_info(task.node)
|
||||||
|
self.driver.console._exec_stop_console(driver_info)
|
||||||
|
|
||||||
|
mock_exec.assert_called_once_with(
|
||||||
|
driver_info, 'sol deactivate', check_exit_code=[0, 1])
|
||||||
|
|
||||||
|
@mock.patch.object(console_utils, 'get_socat_console_url',
|
||||||
|
autospec=True)
|
||||||
|
def test_get_console(self, mock_get_url):
|
||||||
|
url = 'tcp://localhost:4201'
|
||||||
|
mock_get_url.return_value = url
|
||||||
|
expected = {'type': 'socat', 'url': url}
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context,
|
||||||
|
self.node.uuid) as task:
|
||||||
|
console_info = self.driver.console.get_console(task)
|
||||||
|
|
||||||
|
self.assertEqual(expected, console_info)
|
||||||
|
mock_get_url.assert_called_once_with(self.info['port'])
|
||||||
|
@ -25,8 +25,45 @@ from ironic.drivers.modules import agent as agent_module
|
|||||||
from ironic.drivers.modules.amt import management as amt_management
|
from ironic.drivers.modules.amt import management as amt_management
|
||||||
from ironic.drivers.modules.amt import power as amt_power
|
from ironic.drivers.modules.amt import power as amt_power
|
||||||
from ironic.drivers.modules import iboot
|
from ironic.drivers.modules import iboot
|
||||||
|
from ironic.drivers.modules import ipmitool
|
||||||
from ironic.drivers.modules import pxe
|
from ironic.drivers.modules import pxe
|
||||||
from ironic.drivers.modules import wol
|
from ironic.drivers.modules import wol
|
||||||
|
from ironic.drivers import utils
|
||||||
|
from ironic.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class AgentAndIPMIToolDriverTestCase(base.TestCase):
|
||||||
|
|
||||||
|
def test___init__(self):
|
||||||
|
driver = agent.AgentAndIPMIToolDriver()
|
||||||
|
|
||||||
|
self.assertIsInstance(driver.power, ipmitool.IPMIPower)
|
||||||
|
self.assertIsInstance(driver.console, ipmitool.IPMIShellinaboxConsole)
|
||||||
|
self.assertIsInstance(driver.boot, pxe.PXEBoot)
|
||||||
|
self.assertIsInstance(driver.deploy, agent_module.AgentDeploy)
|
||||||
|
self.assertIsInstance(driver.management, ipmitool.IPMIManagement)
|
||||||
|
self.assertIsInstance(driver.agent_vendor,
|
||||||
|
agent_module.AgentVendorInterface)
|
||||||
|
self.assertIsInstance(driver.ipmi_vendor, ipmitool.VendorPassthru)
|
||||||
|
self.assertIsInstance(driver.vendor, utils.MixinVendorInterface)
|
||||||
|
self.assertIsInstance(driver.raid, agent_module.AgentRAID)
|
||||||
|
|
||||||
|
|
||||||
|
class AgentAndIPMIToolAndSocatDriverTestCase(base.TestCase):
|
||||||
|
|
||||||
|
def test___init__(self):
|
||||||
|
driver = agent.AgentAndIPMIToolAndSocatDriver()
|
||||||
|
|
||||||
|
self.assertIsInstance(driver.power, ipmitool.IPMIPower)
|
||||||
|
self.assertIsInstance(driver.console, ipmitool.IPMISocatConsole)
|
||||||
|
self.assertIsInstance(driver.boot, pxe.PXEBoot)
|
||||||
|
self.assertIsInstance(driver.deploy, agent_module.AgentDeploy)
|
||||||
|
self.assertIsInstance(driver.management, ipmitool.IPMIManagement)
|
||||||
|
self.assertIsInstance(driver.agent_vendor,
|
||||||
|
agent_module.AgentVendorInterface)
|
||||||
|
self.assertIsInstance(driver.ipmi_vendor, ipmitool.VendorPassthru)
|
||||||
|
self.assertIsInstance(driver.vendor, utils.MixinVendorInterface)
|
||||||
|
self.assertIsInstance(driver.raid, agent_module.AgentRAID)
|
||||||
|
|
||||||
|
|
||||||
class AgentAndAMTDriverTestCase(testtools.TestCase):
|
class AgentAndAMTDriverTestCase(testtools.TestCase):
|
||||||
|
@ -65,6 +65,19 @@ class PXEDriversTestCase(testtools.TestCase):
|
|||||||
self.assertIsInstance(driver.vendor, utils.MixinVendorInterface)
|
self.assertIsInstance(driver.vendor, utils.MixinVendorInterface)
|
||||||
self.assertIsInstance(driver.raid, agent.AgentRAID)
|
self.assertIsInstance(driver.raid, agent.AgentRAID)
|
||||||
|
|
||||||
|
def test_pxe_ipmitool_socat_driver(self):
|
||||||
|
driver = pxe.PXEAndIPMIToolAndSocatDriver()
|
||||||
|
|
||||||
|
self.assertIsInstance(driver.power, ipmitool.IPMIPower)
|
||||||
|
self.assertIsInstance(driver.console, ipmitool.IPMISocatConsole)
|
||||||
|
self.assertIsInstance(driver.boot, pxe_module.PXEBoot)
|
||||||
|
self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy)
|
||||||
|
self.assertIsInstance(driver.management, ipmitool.IPMIManagement)
|
||||||
|
self.assertIsNone(driver.inspect)
|
||||||
|
# TODO(rameshg87): Need better way of asserting the routes.
|
||||||
|
self.assertIsInstance(driver.vendor, utils.MixinVendorInterface)
|
||||||
|
self.assertIsInstance(driver.raid, agent.AgentRAID)
|
||||||
|
|
||||||
def test_pxe_ssh_driver(self):
|
def test_pxe_ssh_driver(self):
|
||||||
driver = pxe.PXEAndSSHDriver()
|
driver = pxe.PXEAndSSHDriver()
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Adds support for socat-based serial console to ipmitool-based drivers.
|
||||||
|
These are available by using the agent_ipmitool_socat and
|
||||||
|
pxe_ipmitool_socat drivers.
|
@ -40,6 +40,7 @@ ironic.drivers =
|
|||||||
agent_iboot = ironic.drivers.agent:AgentAndIBootDriver
|
agent_iboot = ironic.drivers.agent:AgentAndIBootDriver
|
||||||
agent_ilo = ironic.drivers.ilo:IloVirtualMediaAgentDriver
|
agent_ilo = ironic.drivers.ilo:IloVirtualMediaAgentDriver
|
||||||
agent_ipmitool = ironic.drivers.agent:AgentAndIPMIToolDriver
|
agent_ipmitool = ironic.drivers.agent:AgentAndIPMIToolDriver
|
||||||
|
agent_ipmitool_socat = ironic.drivers.agent:AgentAndIPMIToolAndSocatDriver
|
||||||
agent_irmc = ironic.drivers.irmc:IRMCVirtualMediaAgentDriver
|
agent_irmc = ironic.drivers.irmc:IRMCVirtualMediaAgentDriver
|
||||||
agent_pxe_oneview = ironic.drivers.oneview:AgentPXEOneViewDriver
|
agent_pxe_oneview = ironic.drivers.oneview:AgentPXEOneViewDriver
|
||||||
agent_pyghmi = ironic.drivers.agent:AgentAndIPMINativeDriver
|
agent_pyghmi = ironic.drivers.agent:AgentAndIPMINativeDriver
|
||||||
@ -51,6 +52,7 @@ ironic.drivers =
|
|||||||
fake_agent = ironic.drivers.fake:FakeAgentDriver
|
fake_agent = ironic.drivers.fake:FakeAgentDriver
|
||||||
fake_inspector = ironic.drivers.fake:FakeIPMIToolInspectorDriver
|
fake_inspector = ironic.drivers.fake:FakeIPMIToolInspectorDriver
|
||||||
fake_ipmitool = ironic.drivers.fake:FakeIPMIToolDriver
|
fake_ipmitool = ironic.drivers.fake:FakeIPMIToolDriver
|
||||||
|
fake_ipmitool_socat = ironic.drivers.fake:FakeIPMIToolSocatDriver
|
||||||
fake_ipminative = ironic.drivers.fake:FakeIPMINativeDriver
|
fake_ipminative = ironic.drivers.fake:FakeIPMINativeDriver
|
||||||
fake_ssh = ironic.drivers.fake:FakeSSHDriver
|
fake_ssh = ironic.drivers.fake:FakeSSHDriver
|
||||||
fake_pxe = ironic.drivers.fake:FakePXEDriver
|
fake_pxe = ironic.drivers.fake:FakePXEDriver
|
||||||
@ -71,6 +73,7 @@ ironic.drivers =
|
|||||||
iscsi_irmc = ironic.drivers.irmc:IRMCVirtualMediaIscsiDriver
|
iscsi_irmc = ironic.drivers.irmc:IRMCVirtualMediaIscsiDriver
|
||||||
iscsi_pxe_oneview = ironic.drivers.oneview:ISCSIPXEOneViewDriver
|
iscsi_pxe_oneview = ironic.drivers.oneview:ISCSIPXEOneViewDriver
|
||||||
pxe_ipmitool = ironic.drivers.pxe:PXEAndIPMIToolDriver
|
pxe_ipmitool = ironic.drivers.pxe:PXEAndIPMIToolDriver
|
||||||
|
pxe_ipmitool_socat = ironic.drivers.pxe:PXEAndIPMIToolAndSocatDriver
|
||||||
pxe_ipminative = ironic.drivers.pxe:PXEAndIPMINativeDriver
|
pxe_ipminative = ironic.drivers.pxe:PXEAndIPMINativeDriver
|
||||||
pxe_ssh = ironic.drivers.pxe:PXEAndSSHDriver
|
pxe_ssh = ironic.drivers.pxe:PXEAndSSHDriver
|
||||||
pxe_vbox = ironic.drivers.pxe:PXEAndVirtualBoxDriver
|
pxe_vbox = ironic.drivers.pxe:PXEAndVirtualBoxDriver
|
||||||
|
Loading…
Reference in New Issue
Block a user