Remove shellinabox

It's abandoned for years now.

Also remove references to web console in the documentation since it
seems rely on shellinabox functionality.

Change-Id: I5e9eca79b4bdec582d2251ae7e339dfc50a17035
Signed-off-by: Clif Houck <me@clifhouck.com>
This commit is contained in:
Clif Houck
2026-02-11 11:30:59 -06:00
parent 57b40b2587
commit 56bb687d37
21 changed files with 42 additions and 1052 deletions
+1 -160
View File
@@ -7,10 +7,9 @@ Configuring Consoles
Overview
--------
There are three types of consoles which are available in Bare Metal service:
There are two types of consoles which are available in Bare Metal service:
* (`Node graphical console`_) for a graphical console from a NoVNC web browser
* (`Node web console`_) a terminal available from a web browser
* (`Node serial console`_) for serial console support
Node graphical console
@@ -51,164 +50,6 @@ displays an animation.
enabled_hardware_types = fake-hardware
enabled_console_interfaces = fake-graphical,no-console
Node web console
----------------
The web console can be configured in Bare Metal service in the following way:
* Install shellinabox in ironic conductor node. For RHEL/CentOS, shellinabox package
is not present in base repositories, the user must enable EPEL repository, you can
find more from `FedoraProject page`_.
.. warning::
Shell In A Box is considered abandoned by the Ironic community. The
original maintainer stopped maintaining the project and the project
was thus forked. The resulting
`fork <https://github.com/shellinabox/shellinabox>`_ has not received
updates in a number of years and is considered abandoned.
As such, shellinabox support has been deprecated by the Ironic community.
Installation example:
Ubuntu::
sudo apt-get install shellinabox
RHEL/CentOS/Fedora::
sudo dnf install shellinabox
You can find more about shellinabox on the `shellinabox page`_.
You can optionally use the SSL certificate in shellinabox. If you want to use the SSL
certificate in shellinabox, you should install openssl and generate the SSL certificate.
1. Install openssl, for example:
Ubuntu::
sudo apt-get install openssl
RHEL/CentOS/Fedora::
sudo dnf install openssl
2. Generate the SSL certificate, here is an example, you can find more about openssl on
the `openssl page`_::
cd /tmp/ca
openssl genrsa -des3 -out my.key 1024
openssl req -new -key my.key -out my.csr
cp my.key my.key.org
openssl rsa -in my.key.org -out my.key
openssl x509 -req -days 3650 -in my.csr -signkey my.key -out my.crt
cat my.crt my.key > certificate.pem
* Customize the console section in the Bare Metal service configuration
file (/etc/ironic/ironic.conf), if you want to use SSL certificate in
shellinabox, you should specify ``terminal_cert_dir``.
For example::
[console]
#
# Options defined in ironic.drivers.modules.console_utils
#
# Path to serial console terminal program. Used only by Shell
# In A Box console. (string value)
#terminal=shellinaboxd
# Directory containing the terminal SSL cert (PEM) for serial
# console access. Used only by Shell In A Box console. (string
# value)
terminal_cert_dir=/tmp/ca
# Directory for holding terminal pid files. If not specified,
# the temporary directory will be used. (string value)
#terminal_pid_dir=<None>
# Time interval (in seconds) for checking the status of
# console subprocess. (integer value)
#subprocess_checking_interval=1
# Time (in seconds) to wait for the console subprocess to
# start. (integer value)
#subprocess_timeout=10
* Append console parameters for bare metal PXE boot in the Bare Metal service
configuration file (/etc/ironic/ironic.conf). See the reference for
configuration in :ref:`kernel-boot-parameters`.
* Enable the ``ipmitool-shellinabox`` console interface, for example:
.. code-block:: ini
[DEFAULT]
enabled_console_interfaces = ipmitool-shellinabox,no-console
* Configure node web console.
If the node uses a hardware type, for example ``ipmi``, set the node's
console interface to ``ipmitool-shellinabox``::
baremetal node set <node> --console-interface ipmitool-shellinabox
Enable the web console, for example::
baremetal node set <node> \
--driver-info <terminal_port>=<customized_port>
baremetal node console enable <node>
Check whether the console is enabled, for example::
baremetal node validate <node>
Disable the web console, for example::
baremetal node console disable <node>
baremetal node unset <node> --driver-info <terminal_port>
The ``<terminal_port>`` is driver dependent. The actual name of this field can be
checked in driver properties, for example::
baremetal driver property list <driver>
For the ``ipmi`` hardware type, this option is ``ipmi_terminal_port``.
Give a customized port number to ``<customized_port>``,
for example ``8023``, this customized port is used in web console url.
Get web console information for a node as follows::
baremetal node console show <node>
+-----------------+----------------------------------------------------------------------+
| Property | Value |
+-----------------+----------------------------------------------------------------------+
| console_enabled | True |
| console_info | {u'url': u'http://<url>:<customized_port>', u'type': u'shellinabox'} |
+-----------------+----------------------------------------------------------------------+
You can open the web console using the above ``url`` through web browser. If
``console_enabled`` is ``false``, ``console_info`` is ``None``, web console is disabled.
If you want to launch the web console, see the ``Configure node web console`` part.
.. note::
An error message you may encounter when enabling the console can read
``Console subprocess failed to start. Timeout or error while waiting for
console subprocess to start for node`` along with
``[server] Failed to find any available port!``. This error is coming from
shellinabox itself, not from the communication with the BMC. One potential
cause for this issue is that there are already shellinabox daemons running
which block the configured port (remove them if appropriate and retry to
enable the console).
.. _`shellinabox page`: https://code.google.com/archive/p/shellinabox/
.. _`openssl page`: https://www.openssl.org/
.. _`FedoraProject page`: https://fedoraproject.org/wiki/Infrastructure/Mirroring
Node serial console
-------------------
+1 -1
View File
@@ -48,7 +48,7 @@ Please see :doc:`/install/configure-ipmi` for the required dependencies.
[DEFAULT]
enabled_hardware_types = ipmi
enabled_console_interfaces = ipmitool-socat,ipmitool-shellinabox,no-console
enabled_console_interfaces = ipmitool-socat,no-console
enabled_management_interfaces = ipmitool,noop
enabled_power_interfaces = ipmitool
enabled_vendor_interfaces = ipmitool,no-vendor
+2 -2
View File
@@ -61,7 +61,7 @@ hardware interfaces:
future. Instead, use ``irmc-pxe``.
* console
Supports ``ipmitool-socat``, ``ipmitool-shellinabox``, and ``no-console``.
Supports ``ipmitool-socat``, and ``no-console``.
The default is ``ipmitool-socat``.
* inspect
@@ -93,7 +93,7 @@ interfaces enabled for ``irmc`` hardware type.
enabled_hardware_types = irmc
enabled_bios_interfaces = irmc
enabled_boot_interfaces = irmc-virtual-media,irmc-pxe
enabled_console_interfaces = ipmitool-socat,ipmitool-shellinabox,no-console
enabled_console_interfaces = ipmitool-socat,no-console
enabled_deploy_interfaces = direct
enabled_inspect_interfaces = irmc,agent,no-inspect
enabled_management_interfaces = irmc
-2
View File
@@ -12,5 +12,3 @@ Advanced features
.. include:: include/disk-label.inc
.. include:: include/notifications.inc
.. include:: include/console.inc
+6 -6
View File
@@ -623,7 +623,7 @@ Consider the following configuration (shortened for simplicity):
[DEFAULT]
enabled_hardware_types = ipmi,redfish
enabled_console_interfaces = no-console,ipmitool-shellinabox
enabled_console_interfaces = no-console,ipmitool-socat
enabled_deploy_interfaces = direct
enabled_management_interfaces = ipmitool,redfish
enabled_power_interfaces = ipmitool,redfish
@@ -660,11 +660,11 @@ power
option has only one item: ``ipmitool``. It is used.
console
No default is configured. The ``ipmi`` hardware type supports the following
console interfaces: ``ipmitool-socat``, ``ipmitool-shellinabox`` and
``no-console`` (in this order). Of these three, only two are enabled:
``no-console`` and ``ipmitool-shellinabox`` (order does not matter). The
intersection contains ``ipmitool-shellinabox`` and ``no-console``.
The first item is used, and it is ``ipmitool-shellinabox``.
console interfaces: ``ipmitool-socat``, and ``no-console`` (in this
order). Both are enabled: ``no-console`` and ``ipmitool-socat``
(order does not matter). The intersection contains
``ipmitool-socat`` and ``no-console``.
The first item is used, and it is ``ipmitool-socat``.
management
Following the same calculation as *power*, the ``ipmitool`` management
interface is used.
-6
View File
@@ -1,6 +0,0 @@
Configuring node web console
----------------------------
See :ref:`console`.
.. TODO(dtantsur): move the installation documentation here
@@ -81,8 +81,8 @@ In order to change default console configuration in the Bare Metal
service configuration file (``[pxe]`` section in ``/etc/ironic/ironic.conf``),
include the serial port terminal and serial speed. Serial speed must be
the same as the serial configuration in the BIOS settings, so that the
operating system boot process can be seen in the serial console or web console.
Following examples represent possible parameters for serial and web console
operating system boot process can be seen in the serial console.
Following examples represent possible parameters for serial console
respectively.
* Node serial console. The console parameter ``console=ttyS0,115200n8``
@@ -93,15 +93,6 @@ respectively.
# Additional append parameters for baremetal PXE boot.
kernel_append_params = nofb vga=normal console=ttyS0,115200n8
* For node web console configuration is similar with the addition of ``ttyX``
parameter, see example::
[pxe]
# Additional append parameters for baremetal PXE boot.
kernel_append_params = nofb vga=normal console=tty0 console=ttyS0,115200n8
For detailed information on how to add consoles see the reference documents
`kernel params`_ and `serial console`_.
In case of local boot the Bare Metal service is not able to control kernel boot
+2 -10
View File
@@ -46,14 +46,6 @@ class PortRange(types.String):
opts = [
cfg.StrOpt('terminal',
default='shellinaboxd',
help=_('Path to serial console terminal program. Used only '
'by Shell In A Box console.')),
cfg.StrOpt('terminal_cert_dir',
help=_('Directory containing the terminal SSL cert (PEM) for '
'serial console access. Used only by Shell In A Box '
'console.')),
cfg.StrOpt('terminal_pid_dir',
help=_('Directory for holding terminal pid files. '
'If not specified, the temporary directory '
@@ -87,8 +79,8 @@ opts = [
'proxy service running on the host of ironic '
'conductor, in the form of <start>:<stop> or '
'comma-separated ranges like '
'<start>:<stop>,<start>:<stop>. This option is used by '
'both Shellinabox and Socat console')),
'<start>:<stop>,<start>:<stop>. This option is used '
'only by Socat console')),
]
+1 -1
View File
@@ -18,7 +18,7 @@ class IntelIPMIHardware(ipmi.IPMIHardware):
"""Intel IPMI hardware type.
Uses ``ipmitool`` to implement power and management.
Provides serial console implementations via ``shellinabox`` or ``socat``.
Provides serial console implementations via ``socat``.
Supports Intel SST-PP feature.
"""
@property
+2 -3
View File
@@ -24,15 +24,14 @@ class IPMIHardware(generic.GenericHardware):
"""IPMI hardware type.
Uses ``ipmitool`` to implement power and management.
Provides serial console implementations via ``shellinabox`` or ``socat``.
Provides serial console implementations via ``socat``.
"""
@property
def supported_console_interfaces(self):
"""List of supported console interfaces."""
return [
ipmitool.IPMISocatConsole,
ipmitool.IPMIShellinaboxConsole
ipmitool.IPMISocatConsole
] + super().supported_console_interfaces
@property
+1 -2
View File
@@ -62,8 +62,7 @@ class IRMCHardware(generic.GenericHardware):
def supported_console_interfaces(self):
"""List of supported console interfaces."""
return [
ipmitool.IPMISocatConsole,
ipmitool.IPMIShellinaboxConsole
ipmitool.IPMISocatConsole
] + super().supported_console_interfaces
@property
-127
View File
@@ -203,19 +203,6 @@ def release_port(port):
ALLOCATED_PORTS.discard(port)
def get_shellinabox_console_url(port):
"""Get a url to access the console via shellinaboxd.
:param port: the terminal port for the node.
"""
console_host = netutils.escape_ipv6(CONF.my_ip)
scheme = 'https' if CONF.console.terminal_cert_dir else 'http'
return '%(scheme)s://%(host)s:%(port)s' % {'scheme': scheme,
'host': console_host,
'port': port}
class _PopenNonblockingPipe(object):
def __init__(self, source):
self._source = source
@@ -251,120 +238,6 @@ class _PopenNonblockingPipe(object):
return self._finished
def start_shellinabox_console(node_uuid, port, console_cmd,
env_variables=None):
"""Open the serial console for a node.
:param node_uuid: the uuid for the node.
:param port: the terminal port for the node.
:param console_cmd: the shell command that gets the console.
:param env_variables: optional dict of environment variables to pass to
the subprocess (e.g. IPMI_PASSWORD for ipmitool -E).
:raises: ConsoleError if the directory for the PID file cannot be created
or an old process cannot be stopped.
:raises: ConsoleSubprocessFailed when invoking the subprocess failed.
"""
# make sure that the old console for this node is stopped
# and the files are cleared
try:
_stop_console(node_uuid)
except exception.NoConsolePid:
pass
_ensure_console_pid_dir_exists()
pid_file = _get_console_pid_file(node_uuid)
# put together the command and arguments for invoking the console
args = []
args.append(CONF.console.terminal)
if CONF.console.terminal_cert_dir:
args.append("-c")
args.append(CONF.console.terminal_cert_dir)
else:
args.append("-t")
args.append("-p")
args.append(str(port))
args.append("--background=%s" % pid_file)
args.append("-s")
args.append(console_cmd)
# run the command as a subprocess
try:
LOG.debug('Running subprocess: %s', ' '.join(args))
# use pipe here to catch the error in case shellinaboxd
# failed to start.
popen_kwargs = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE}
if env_variables is not None:
popen_kwargs['env'] = dict(os.environ, **env_variables)
obj = subprocess.Popen(args, **popen_kwargs)
except (OSError, ValueError) as e:
error = _("%(exec_error)s\n"
"Command: %(command)s") % {'exec_error': str(e),
'command': ' '.join(args)}
LOG.warning(error)
raise exception.ConsoleSubprocessFailed(error=error)
error_message = _(
"Timeout or error while waiting for console subprocess to start for "
"node: %(node)s.\nCommand: %(command)s.\n") % {
'node': node_uuid,
'command': ' '.join(args)}
stdout_pipe, stderr_pipe = (
_PopenNonblockingPipe(obj.stdout), _PopenNonblockingPipe(obj.stderr))
def _wait(node_uuid, popen_obj):
locals['returncode'] = popen_obj.poll()
# check if the console pid is created and the process is running.
# if it is, then the shellinaboxd is invoked successfully as a daemon.
# otherwise check the error.
if (locals['returncode'] == 0 and os.path.exists(pid_file)
and psutil.pid_exists(_get_console_pid(node_uuid))):
raise loopingcall.LoopingCallDone()
if locals['returncode'] is not None:
watched = (stdout_pipe, stderr_pipe)
while time.time() < expiration and not all(
(i.finished for i in watched)):
for pipe in watched:
pipe.read()
locals['errstr'] = error_message + _(
"Exit code: %(return_code)s.\nStdout: %(stdout)r\n"
"Stderr: %(stderr)r") % {
'return_code': locals['returncode'],
'stdout': stdout_pipe.output, 'stderr': stderr_pipe.output}
raise loopingcall.LoopingCallDone()
if time.time() > expiration:
locals['errstr'] = error_message
raise loopingcall.LoopingCallDone()
locals = {'returncode': None, 'errstr': ''}
expiration = time.time() + CONF.console.subprocess_timeout
timer = loopingcall.FixedIntervalLoopingCall(_wait, node_uuid, obj)
timer.start(interval=CONF.console.subprocess_checking_interval).wait()
if locals['errstr']:
LOG.warning(locals['errstr'])
raise exception.ConsoleSubprocessFailed(error=locals['errstr'])
def stop_shellinabox_console(node_uuid):
"""Close the serial console for a node.
:param node_uuid: the UUID of the node
:raises: ConsoleError if unable to stop the console process
"""
try:
_stop_console(node_uuid)
except exception.NoConsolePid:
LOG.warning("No console pid found for node %s while trying to "
"stop shellinabox console.", node_uuid)
def get_socat_console_url(port):
"""Get a URL to access the console via socat.
+2 -2
View File
@@ -24,8 +24,8 @@ from ironic.drivers.modules import ipmitool
METRICS = metrics_utils.get_metrics_logger(__name__)
class IloConsoleInterface(ipmitool.IPMIShellinaboxConsole):
"""A ConsoleInterface that uses ipmitool and shellinabox."""
class IloConsoleInterface(ipmitool.IPMISocatConsole):
"""A ConsoleInterface that uses ipmitool."""
def get_properties(self):
props = ilo_common.REQUIRED_PROPERTIES.copy()
-89
View File
@@ -1653,95 +1653,6 @@ class IPMIConsole(base.ConsoleInterface):
raise
class IPMIShellinaboxConsole(IPMIConsole):
"""A ConsoleInterface that uses ipmitool and shellinabox."""
# TODO(TheJulia): This interface is deprecated due to the shellinabox
# project being abandoned. It should be removed after the release of
# 2025.2 in advance of 2026.1's release.
# https://github.com/shellinabox/shellinabox
# https://github.com/shellinabox/shellinabox/issues/531
supported = False
def _get_ipmi_cmd(self, driver_info, pw_file=None, use_pwd_env=False):
"""Get ipmi command for ipmitool usage.
:param driver_info: driver info with the ipmitool parameters
:param pw_file: password file to be used in ipmitool command
:param use_pwd_env: when True, add -E so ipmitool reads password
from IPMI_PASSWORD environment variable
:returns: returns a command string for ipmitool
"""
command = super(IPMIShellinaboxConsole, self)._get_ipmi_cmd(
driver_info, pw_file, use_pwd_env=use_pwd_env)
return ("/:%(uid)s:%(gid)s:HOME:%(basic_command)s"
% {'uid': os.getuid(),
'gid': os.getgid(),
'basic_command': command})
@METRICS.timer('IPMIShellinaboxConsole.start_console')
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
"""
# Dealloc allocated port if any, so the same host can never has
# duplicated port.
_release_allocated_port(task)
driver_info = _parse_driver_info(task.node)
if not driver_info['port']:
driver_info['port'] = _allocate_port(task)
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 already deactivated,
# so we can ignore it.
pass
self._start_console(driver_info,
console_utils.start_shellinabox_console)
@METRICS.timer('IPMIShellinaboxConsole.stop_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
"""
try:
console_utils.stop_shellinabox_console(task.node.uuid)
finally:
utils.unlink_without_raise(_console_pwfile_path(task.node.uuid))
_release_allocated_port(task)
def _exec_stop_console(self, driver_info):
cmd = "sol deactivate"
_exec_ipmitool(driver_info, cmd, check_exit_code=[0, 1])
@METRICS.timer('IPMIShellinaboxConsole.get_console')
def get_console(self, task):
"""Get the type and connection information about the console."""
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 already deactivated,
# so we can ignore it.
pass
url = console_utils.get_shellinabox_console_url(driver_info['port'])
return {'type': 'shellinabox', 'url': url}
class IPMISocatConsole(IPMIConsole):
"""A ConsoleInterface that uses ipmitool and socat."""
@@ -28,7 +28,7 @@ class IloConsoleInterfaceTestCase(test_common.BaseIloTest):
boot_interface = 'ilo-virtual-media'
@mock.patch.object(ipmitool.IPMIShellinaboxConsole, 'validate',
@mock.patch.object(ipmitool.IPMISocatConsole, 'validate',
spec_set=True, autospec=True)
@mock.patch.object(ilo_common, 'update_ipmi_properties', spec_set=True,
autospec=True)
@@ -41,7 +41,7 @@ class IloConsoleInterfaceTestCase(test_common.BaseIloTest):
update_ipmi_mock.assert_called_once_with(task)
ipmi_validate_mock.assert_called_once_with(mock.ANY, task)
@mock.patch.object(ipmitool.IPMIShellinaboxConsole, 'validate',
@mock.patch.object(ipmitool.IPMISocatConsole, 'validate',
spec_set=True, autospec=True)
@mock.patch.object(ilo_common, 'update_ipmi_properties', spec_set=True,
autospec=True)
@@ -68,23 +68,6 @@ class IntelIPMIHardwareTestCase(db_base.DbTestCase):
with task_manager.acquire(self.context, node.id) as task:
self._validate_interfaces(task)
def test_override_with_shellinabox(self):
self.config(enabled_console_interfaces=['ipmitool-shellinabox',
'ipmitool-socat'])
node = obj_utils.create_test_node(
self.context, driver='intel-ipmi',
deploy_interface='direct',
raid_interface='agent',
console_interface='ipmitool-shellinabox',
vendor_interface='no-vendor')
with task_manager.acquire(self.context, node.id) as task:
self._validate_interfaces(
task,
deploy=agent.AgentDeploy,
console=ipmitool.IPMIShellinaboxConsole,
raid=agent.AgentRAID,
vendor=noop.NoVendor)
def test_override_with_cinder_storage(self):
self.config(enabled_storage_interfaces=['noop', 'cinder'])
node = obj_utils.create_test_node(
@@ -17,9 +17,6 @@
"""Test class for console_utils driver module."""
import errno
import fcntl
import ipaddress
import os
import random
import signal
@@ -27,7 +24,6 @@ import socket
import string
import subprocess
import tempfile
import time
from unittest import mock
from oslo_config import cfg
@@ -196,22 +192,6 @@ class ConsoleUtilsTestCase(db_base.DbTestCase):
self.assertFalse(mock_kill.called)
mock_unlink.assert_called_once_with(pid_file)
@mock.patch.object(utils, 'unlink_without_raise', autospec=True)
@mock.patch.object(os, 'kill', autospec=True)
@mock.patch.object(console_utils, '_get_console_pid', autospec=True)
def test__stop_console_shellinabox_not_running(self, mock_pid,
mock_kill, mock_unlink):
pid_file = console_utils._get_console_pid_file(self.info['uuid'])
mock_pid.return_value = 12345
mock_kill.side_effect = OSError(errno.ESRCH, 'message')
console_utils._stop_console(self.info['uuid'])
mock_pid.assert_called_once_with(self.info['uuid'])
mock_kill.assert_called_once_with(mock_pid.return_value,
signal.SIGTERM)
mock_unlink.assert_called_once_with(pid_file)
@mock.patch.object(utils, 'unlink_without_raise', autospec=True)
@mock.patch.object(os, 'kill', autospec=True)
@mock.patch.object(console_utils, '_get_console_pid', autospec=True)
@@ -229,24 +209,6 @@ class ConsoleUtilsTestCase(db_base.DbTestCase):
signal.SIGTERM)
mock_unlink.assert_called_once_with(pid_file)
def _get_shellinabox_console(self, scheme):
generated_url = (
console_utils.get_shellinabox_console_url(self.info['port']))
console_host = CONF.my_ip
if ipaddress.ip_address(console_host).version == 6:
console_host = '[%s]' % console_host
http_url = "%s://%s:%s" % (scheme, console_host, self.info['port'])
self.assertEqual(http_url, generated_url)
def test_get_shellinabox_console_url(self):
self._get_shellinabox_console('http')
def test_get_shellinabox_console_https_url(self):
# specify terminal_cert_dir in /etc/ironic/ironic.conf
self.config(terminal_cert_dir='/tmp', group='console')
# use https
self._get_shellinabox_console('https')
def test_make_persistent_password_file(self):
filepath = '%(tempdir)s/%(node_uuid)s' % {
'tempdir': tempfile.gettempdir(),
@@ -274,281 +236,6 @@ class ConsoleUtilsTestCase(db_base.DbTestCase):
filepath,
'password')
@mock.patch.object(fcntl, 'fcntl', autospec=True)
@mock.patch.object(console_utils, 'open',
mock.mock_open(read_data='12345\n'))
@mock.patch.object(os.path, 'exists', autospec=True)
@mock.patch.object(subprocess, 'Popen', autospec=True)
@mock.patch.object(psutil, 'pid_exists', autospec=True)
@mock.patch.object(console_utils, '_ensure_console_pid_dir_exists',
autospec=True)
@mock.patch.object(console_utils, '_stop_console', autospec=True)
def test_start_shellinabox_console(self, mock_stop,
mock_dir_exists,
mock_pid_exists,
mock_popen,
mock_path_exists, mock_fcntl):
mock_popen.return_value.poll.return_value = 0
self.mock_stdout.write(b'0')
self.mock_stdout.seek(0)
mock_popen.return_value.stdout = self.mock_stdout
self.mock_stderr.write(b'1')
self.mock_stderr.seek(0)
mock_popen.return_value.stderr = self.mock_stderr
mock_pid_exists.return_value = True
mock_path_exists.return_value = True
console_utils.start_shellinabox_console(self.info['uuid'],
self.info['port'],
'ls&')
mock_stop.assert_called_once_with(self.info['uuid'])
mock_dir_exists.assert_called_once_with()
mock_pid_exists.assert_called_once_with(12345)
mock_popen.assert_called_once_with(mock.ANY,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
mock_popen.return_value.poll.assert_called_once_with()
@mock.patch.object(console_utils, 'open',
mock.mock_open(read_data='12345\n'))
@mock.patch.object(os.path, 'exists', autospec=True)
@mock.patch.object(subprocess, 'Popen', autospec=True)
@mock.patch.object(psutil, 'pid_exists', autospec=True)
@mock.patch.object(console_utils, '_get_console_pid_file', autospec=True)
@mock.patch.object(console_utils, '_ensure_console_pid_dir_exists',
autospec=True)
@mock.patch.object(console_utils, '_stop_console', autospec=True)
@mock.patch.object(loopingcall.FixedIntervalLoopingCall, 'start',
autospec=True)
def test_start_shellinabox_console_with_env_variables(
self, mock_timer_start, mock_stop, mock_dir_exists,
mock_get_pid, mock_pid_exists, mock_popen, mock_path_exists):
"""Test start_shellinabox_console passes env_variables to Popen."""
mock_timer_start.return_value = mock.Mock()
mock_get_pid.return_value = '/tmp/%s.pid' % self.info['uuid']
mock_popen.return_value.poll.return_value = 0
mock_popen.return_value.stdout = self.mock_stdout
self.mock_stdout.seek(0)
mock_popen.return_value.stderr = self.mock_stderr
self.mock_stderr.seek(0)
mock_pid_exists.return_value = True
mock_path_exists.return_value = True
console_utils.start_shellinabox_console(
self.info['uuid'], self.info['port'], 'ls&',
env_variables={'SOME_VAR': 'value'})
mock_popen.assert_called_once()
call_kwargs = mock_popen.call_args[1]
self.assertIn('env', call_kwargs)
env = call_kwargs['env']
self.assertEqual(env.get('SOME_VAR'), 'value')
# Should be a copy of os.environ plus our var
for key, value in os.environ.items():
self.assertEqual(env.get(key), value,
'env should contain os.environ')
@mock.patch.object(fcntl, 'fcntl', autospec=True)
@mock.patch.object(console_utils, 'open',
mock.mock_open(read_data='12345\n'))
@mock.patch.object(os.path, 'exists', autospec=True)
@mock.patch.object(subprocess, 'Popen', autospec=True)
@mock.patch.object(psutil, 'pid_exists', autospec=True)
@mock.patch.object(console_utils, '_ensure_console_pid_dir_exists',
autospec=True)
@mock.patch.object(console_utils, '_stop_console', autospec=True)
def test_start_shellinabox_console_nopid(self, mock_stop,
mock_dir_exists,
mock_pid_exists,
mock_popen,
mock_path_exists, mock_fcntl):
# no existing PID file before starting
mock_stop.side_effect = exception.NoConsolePid('/tmp/blah')
mock_popen.return_value.poll.return_value = 0
self.mock_stdout.write(b'0')
self.mock_stdout.seek(0)
mock_popen.return_value.stdout = self.mock_stdout
self.mock_stderr.write(b'1')
self.mock_stderr.seek(0)
mock_popen.return_value.stderr = self.mock_stderr
mock_pid_exists.return_value = True
mock_path_exists.return_value = True
console_utils.start_shellinabox_console(self.info['uuid'],
self.info['port'],
'ls&')
mock_stop.assert_called_once_with(self.info['uuid'])
mock_dir_exists.assert_called_once_with()
mock_pid_exists.assert_called_once_with(12345)
mock_popen.assert_called_once_with(mock.ANY,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
mock_popen.return_value.poll.assert_called_once_with()
@mock.patch.object(time, 'sleep', autospec=True)
@mock.patch.object(os, 'read', autospec=True)
@mock.patch.object(fcntl, 'fcntl', autospec=True)
@mock.patch.object(subprocess, 'Popen', autospec=True)
@mock.patch.object(console_utils, '_ensure_console_pid_dir_exists',
autospec=True)
@mock.patch.object(console_utils, '_stop_console', autospec=True)
def test_start_shellinabox_console_fail(
self, mock_stop, mock_dir_exists, mock_popen, mock_fcntl,
mock_os_read, mock_sleep):
mock_popen.return_value.poll.return_value = 1
self.mock_stdout.write(b'0')
self.mock_stdout.seek(0)
mock_popen.return_value.stdout = self.mock_stdout
self.mock_stderr.write(b'1')
self.mock_stderr.seek(0)
mock_popen.return_value.stderr = self.mock_stderr
err_output = b'error output'
mock_os_read.side_effect = [err_output] * 2 + [OSError] * 2
mock_fcntl.side_effect = [1, mock.Mock()] * 2
self.assertRaisesRegex(
exception.ConsoleSubprocessFailed, "Stdout: %r" % err_output,
console_utils.start_shellinabox_console, self.info['uuid'],
self.info['port'], 'ls&')
mock_stop.assert_called_once_with(self.info['uuid'])
mock_sleep.assert_has_calls([mock.call(1), mock.call(1)])
mock_dir_exists.assert_called_once_with()
for obj in (self.mock_stdout, self.mock_stderr):
mock_fcntl.assert_has_calls([
mock.call(obj, fcntl.F_GETFL),
mock.call(obj, fcntl.F_SETFL, 1 | os.O_NONBLOCK)])
mock_popen.assert_called_once_with(mock.ANY,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
mock_popen.return_value.poll.assert_called_with()
@mock.patch.object(fcntl, 'fcntl', autospec=True)
@mock.patch.object(subprocess, 'Popen', autospec=True)
@mock.patch.object(console_utils, '_ensure_console_pid_dir_exists',
autospec=True)
@mock.patch.object(console_utils, '_stop_console', autospec=True)
def test_start_shellinabox_console_timeout(
self, mock_stop, mock_dir_exists, mock_popen, mock_fcntl):
self.config(subprocess_timeout=0, group='console')
self.config(subprocess_checking_interval=0, group='console')
mock_popen.return_value.poll.return_value = None
self.mock_stdout.write(b'0')
self.mock_stdout.seek(0)
mock_popen.return_value.stdout = self.mock_stdout
self.mock_stderr.write(b'1')
self.mock_stderr.seek(0)
mock_popen.return_value.stderr = self.mock_stderr
self.assertRaisesRegex(
exception.ConsoleSubprocessFailed, 'Timeout or error',
console_utils.start_shellinabox_console, self.info['uuid'],
self.info['port'], 'ls&')
mock_stop.assert_called_once_with(self.info['uuid'])
mock_dir_exists.assert_called_once_with()
mock_popen.assert_called_once_with(mock.ANY,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
mock_popen.return_value.poll.assert_called_with()
self.assertEqual(0, mock_popen.return_value.communicate.call_count)
@mock.patch.object(time, 'sleep', autospec=True)
@mock.patch.object(os, 'read', autospec=True)
@mock.patch.object(fcntl, 'fcntl', autospec=True)
@mock.patch.object(console_utils, 'open',
mock.mock_open(read_data='12345\n'))
@mock.patch.object(os.path, 'exists', autospec=True)
@mock.patch.object(subprocess, 'Popen', autospec=True)
@mock.patch.object(psutil, 'pid_exists', autospec=True)
@mock.patch.object(console_utils, '_ensure_console_pid_dir_exists',
autospec=True)
@mock.patch.object(console_utils, '_stop_console', autospec=True)
def test_start_shellinabox_console_fail_no_pid(
self, mock_stop, mock_dir_exists, mock_pid_exists, mock_popen,
mock_path_exists, mock_fcntl, mock_os_read, mock_sleep):
mock_popen.return_value.poll.return_value = 0
self.mock_stdout.write(b'0')
self.mock_stdout.seek(0)
mock_popen.return_value.stdout = self.mock_stdout
self.mock_stderr.write(b'1')
self.mock_stderr.seek(0)
mock_popen.return_value.stderr = self.mock_stderr
mock_pid_exists.return_value = False
mock_os_read.side_effect = [b'error output'] * 2 + [OSError] * 2
mock_fcntl.side_effect = [1, mock.Mock()] * 2
mock_path_exists.return_value = True
self.assertRaises(exception.ConsoleSubprocessFailed,
console_utils.start_shellinabox_console,
self.info['uuid'],
self.info['port'],
'ls&')
mock_stop.assert_called_once_with(self.info['uuid'])
mock_sleep.assert_has_calls([mock.call(1), mock.call(1)])
mock_dir_exists.assert_called_once_with()
for obj in (self.mock_stdout, self.mock_stderr):
mock_fcntl.assert_has_calls([
mock.call(obj, fcntl.F_GETFL),
mock.call(obj, fcntl.F_SETFL, 1 | os.O_NONBLOCK)])
mock_pid_exists.assert_called_with(12345)
mock_popen.assert_called_once_with(mock.ANY,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
mock_popen.return_value.poll.assert_called_with()
@mock.patch.object(subprocess, 'Popen', autospec=True)
@mock.patch.object(console_utils, '_ensure_console_pid_dir_exists',
autospec=True)
@mock.patch.object(console_utils, '_stop_console', autospec=True)
def test_start_shellinabox_console_fail_nopiddir(self, mock_stop,
mock_dir_exists,
mock_popen):
mock_dir_exists.side_effect = exception.ConsoleError(message='fail')
mock_popen.return_value.poll.return_value = 0
self.assertRaises(exception.ConsoleError,
console_utils.start_shellinabox_console,
self.info['uuid'],
self.info['port'],
'ls&')
mock_stop.assert_called_once_with(self.info['uuid'])
mock_dir_exists.assert_called_once_with()
self.assertFalse(mock_popen.called)
@mock.patch.object(console_utils, '_stop_console', autospec=True)
def test_stop_shellinabox_console(self, mock_stop):
console_utils.stop_shellinabox_console(self.info['uuid'])
mock_stop.assert_called_once_with(self.info['uuid'])
@mock.patch.object(console_utils, '_stop_console', autospec=True)
def test_stop_shellinabox_console_fail_nopid(self, mock_stop):
mock_stop.side_effect = exception.NoConsolePid('/tmp/blah')
console_utils.stop_shellinabox_console(self.info['uuid'])
mock_stop.assert_called_once_with(self.info['uuid'])
def test_get_socat_console_url_tcp(self):
self.config(my_ip="10.0.0.1")
url = console_utils.get_socat_console_url(self.info['port'])
@@ -180,7 +180,7 @@ class IPMIToolCheckInitTestCase(base.TestCase):
def test_console_init_calls(self, mock_check_dir, mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
ipmi.IPMIShellinaboxConsole()
ipmi.IPMISocatConsole()
mock_support.assert_called_with(mock.ANY)
mock_check_dir.assert_called_once_with()
@@ -191,7 +191,7 @@ class IPMIToolCheckInitTestCase(base.TestCase):
mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = True
ipmi.IPMIShellinaboxConsole()
ipmi.IPMISocatConsole()
mock_support.assert_called_with(mock.ANY)
self.assertFalse(mock_check_dir.called)
@@ -486,7 +486,6 @@ class Base(db_base.DbTestCase):
enabled_vendor_interfaces=['fake', 'ipmitool',
'no-vendor'],
enabled_console_interfaces=['fake', 'ipmitool-socat',
'ipmitool-shellinabox',
'no-console'])
self.config(debug=True, group="ipmi")
self.node = obj_utils.create_test_node(
@@ -4022,304 +4021,21 @@ class IPMIToolDriverTestCase(Base):
self.assertIsNone(info.get('allocated_ipmi_terminal_port'))
class IPMIToolShellinaboxTestCase(db_base.DbTestCase):
console_interface = 'ipmitool-shellinabox'
console_class = ipmi.IPMIShellinaboxConsole
class IPMIToolSocatDriverTestCase(db_base.DbTestCase):
console_interface = 'ipmitool-socat'
console_class = ipmi.IPMISocatConsole
def setUp(self):
super(IPMIToolShellinaboxTestCase, self).setUp()
super(IPMIToolSocatDriverTestCase, self).setUp()
self.config(enabled_console_interfaces=[self.console_interface,
'no-console'])
self.node = obj_utils.create_test_node(
self.context,
console_interface=self.console_interface,
driver_info=INFO_DICT)
self.context,
console_interface=self.console_interface,
driver_info=INFO_DICT)
self.info = ipmi._parse_driver_info(self.node)
self.console = self.console_class()
def test_console_validate(self):
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info['ipmi_terminal_port'] = 123
task.driver.console.validate(task)
def test_console_validate_missing_port(self):
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info.pop('ipmi_terminal_port', None)
self.assertRaises(exception.MissingParameterValue,
task.driver.console.validate, task)
def test_console_validate_missing_port_auto_allocate(self):
self.config(port_range='10000:20000', group='console')
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info.pop('ipmi_terminal_port', None)
task.driver.console.validate(task)
def test_console_validate_invalid_port(self):
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info['ipmi_terminal_port'] = ''
self.assertRaises(exception.InvalidParameterValue,
task.driver.console.validate, task)
def test_console_validate_wrong_ipmi_protocol_version(self):
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info['ipmi_terminal_port'] = 123
task.node.driver_info['ipmi_protocol_version'] = '1.5'
self.assertRaises(exception.InvalidParameterValue,
task.driver.console.validate, task)
def test__get_ipmi_cmd(self):
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
ipmi_cmd = self.console._get_ipmi_cmd(driver_info, 'pw_file')
expected_ipmi_cmd = ("/:%(uid)s:%(gid)s:HOME:ipmitool "
"-I lanplus -H %(address)s -L ADMINISTRATOR "
"-U %(user)s -f pw_file" %
{'uid': os.getuid(), 'gid': os.getgid(),
'address': driver_info['address'],
'user': driver_info['username']})
self.assertEqual(expected_ipmi_cmd, ipmi_cmd)
def test__get_ipmi_cmd_without_user(self):
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
driver_info['username'] = None
ipmi_cmd = self.console._get_ipmi_cmd(driver_info, 'pw_file')
expected_ipmi_cmd = ("/:%(uid)s:%(gid)s:HOME:ipmitool "
"-I lanplus -H %(address)s -L ADMINISTRATOR "
"-f pw_file" %
{'uid': os.getuid(), 'gid': os.getgid(),
'address': driver_info['address']})
self.assertEqual(expected_ipmi_cmd, ipmi_cmd)
@mock.patch.object(ipmi, '_allocate_port', autospec=True)
@mock.patch.object(ipmi.IPMIConsole, '_start_console', autospec=True)
@mock.patch.object(ipmi.IPMIShellinaboxConsole, "_exec_stop_console",
autospec=True)
def test_start_console(self, mock_exec_stop, mock_start, mock_alloc):
mock_start.return_value = None
mock_alloc.return_value = 10000
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.console.start_console(task)
driver_info = ipmi._parse_driver_info(task.node)
driver_info.update(port=10000)
mock_exec_stop.assert_called_once_with(self.console, driver_info)
mock_start.assert_called_once_with(
self.console, driver_info,
console_utils.start_shellinabox_console)
@mock.patch.object(ipmi, '_allocate_port', autospec=True)
@mock.patch.object(ipmi, '_parse_driver_info', autospec=True)
@mock.patch.object(ipmi.IPMIShellinaboxConsole, "_exec_stop_console",
autospec=True)
@mock.patch.object(ipmi.IPMIConsole, '_start_console', autospec=True)
def test_start_console_with_port(self, mock_start, mock_exec_stop,
mock_info, mock_alloc):
mock_start.return_value = None
mock_info.return_value = {'port': 10000}
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.console.start_console(task)
driver_info = ipmi._parse_driver_info(task.node)
mock_start.assert_called_once_with(
self.console, {'port': 10000},
console_utils.start_shellinabox_console)
mock_exec_stop.assert_called_once_with(self.console, driver_info)
mock_alloc.assert_not_called()
@mock.patch.object(ipmi, '_allocate_port', autospec=True)
@mock.patch.object(ipmi, '_parse_driver_info', autospec=True)
@mock.patch.object(ipmi.IPMIShellinaboxConsole, "_exec_stop_console",
autospec=True)
@mock.patch.object(ipmi.IPMIConsole, '_start_console', autospec=True)
def test_start_console_alloc_port(self, mock_start, mock_exec_stop,
mock_info, mock_alloc):
mock_start.return_value = None
mock_info.return_value = {'port': None}
mock_alloc.return_value = 1234
# Ensure allocated port is not reused
dii = self.node.driver_internal_info
dii['allocated_ipmi_terminal_port'] = 4321
self.node.driver_internal_info = dii
self.node.save()
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.console.start_console(task)
driver_info = ipmi._parse_driver_info(task.node)
mock_start.assert_called_once_with(
self.console, {'port': 1234},
console_utils.start_shellinabox_console)
mock_exec_stop.assert_called_once_with(self.console, driver_info)
mock_alloc.assert_called_once_with(mock.ANY)
@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.console._exec_stop_console(driver_info)
mock_exec.assert_called_once_with(
driver_info, 'sol deactivate', check_exit_code=[0, 1])
@mock.patch.object(ipmi, '_persist_ipmi_password', autospec=True)
@mock.patch.object(ipmi.IPMIConsole, '_get_ipmi_cmd', autospec=True)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test__start_console(self, mock_start, mock_ipmi_cmd, mock_persist):
mock_start.return_value = None
mock_persist.return_value = ('-f', '/tmp/console.pw')
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.console._start_console(
driver_info, console_utils.start_shellinabox_console)
mock_persist.assert_called_once_with(driver_info)
mock_start.assert_called_once_with(self.info['uuid'],
self.info['port'],
mock.ANY)
mock_ipmi_cmd.assert_called_once_with(
self.console, driver_info, pw_file='/tmp/console.pw',
use_pwd_env=False)
@mock.patch.object(ipmi, '_persist_ipmi_password', autospec=True)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test__start_console_fail(self, mock_start, mock_persist):
mock_start.side_effect = exception.ConsoleSubprocessFailed(
error='error')
mock_persist.return_value = ('-f', '/tmp/console.pw')
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.assertRaises(exception.ConsoleSubprocessFailed,
self.console._start_console,
driver_info,
console_utils.start_shellinabox_console)
@mock.patch.object(ipmi, '_persist_ipmi_password', autospec=True)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test__start_console_fail_nodir(self, mock_start, mock_persist):
mock_start.side_effect = exception.ConsoleError()
mock_persist.return_value = ('-f', '/tmp/console.pw')
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.assertRaises(exception.ConsoleError,
self.console._start_console,
driver_info,
console_utils.start_shellinabox_console)
mock_start.assert_called_once_with(self.node.uuid, mock.ANY, mock.ANY)
@mock.patch.object(ipmi, '_persist_ipmi_password', autospec=True)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test__start_console_empty_password(self, mock_start, mock_persist):
mock_persist.return_value = ('-f', '/tmp/console.pw')
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.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(ipmi, '_persist_ipmi_password', autospec=True)
@mock.patch.object(ipmi.IPMIConsole, '_get_ipmi_cmd', autospec=True)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test__start_console_store_cred_in_env(self, mock_start, mock_ipmi_cmd,
mock_persist):
"""Test _start_console with store_cred_in_env=True uses env."""
mock_start.return_value = None
mock_persist.return_value = ('-E', {'IPMI_PASSWORD': 'secret'})
self.config(store_cred_in_env=True, group='ipmi')
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.console._start_console(
driver_info, console_utils.start_shellinabox_console)
mock_persist.assert_called_once_with(driver_info)
mock_ipmi_cmd.assert_called_once_with(
self.console, driver_info, pw_file=None,
use_pwd_env=True)
mock_start.assert_called_once_with(
self.info['uuid'], self.info['port'], mock.ANY,
env_variables={'IPMI_PASSWORD': 'secret'})
@mock.patch.object(ipmi, '_release_allocated_port', autospec=True)
@mock.patch.object(console_utils, 'stop_shellinabox_console',
autospec=True)
def test_stop_console(self, mock_stop, mock_release):
mock_stop.return_value = None
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.console.stop_console(task)
mock_stop.assert_called_once_with(self.info['uuid'])
mock_release.assert_called_once_with(mock.ANY)
@mock.patch.object(console_utils, 'stop_shellinabox_console',
autospec=True)
def test_stop_console_fail(self, mock_stop):
mock_stop.side_effect = exception.ConsoleError()
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.ConsoleError,
self.console.stop_console,
task)
mock_stop.assert_called_once_with(self.node.uuid)
@mock.patch.object(console_utils, 'get_shellinabox_console_url',
autospec=True)
@mock.patch.object(ipmi.IPMIShellinaboxConsole, "_exec_stop_console",
autospec=True)
def test_get_console(self, mock_exec_stop, mock_get):
url = 'http://localhost:4201'
mock_get.return_value = url
expected = {'type': 'shellinabox', 'url': url}
with task_manager.acquire(self.context,
self.node.uuid) as task:
console_info = self.console.get_console(task)
driver_info = ipmi._parse_driver_info(task.node)
mock_exec_stop.assert_called_once_with(self.console, driver_info)
self.assertEqual(expected, console_info)
mock_get.assert_called_once_with(self.info['port'])
class IPMIToolSocatDriverTestCase(IPMIToolShellinaboxTestCase):
console_interface = 'ipmitool-shellinabox'
console_class = ipmi.IPMISocatConsole
def test__get_ipmi_cmd(self):
with task_manager.acquire(self.context,
self.node.uuid) as task:
+4 -4
View File
@@ -67,18 +67,18 @@ class IPMIHardwareTestCase(db_base.DbTestCase):
with task_manager.acquire(self.context, node.id) as task:
self._validate_interfaces(task)
def test_override_with_shellinabox(self):
self.config(enabled_console_interfaces=['ipmitool-shellinabox',
def test_override_with_socat(self):
self.config(enabled_console_interfaces=['fake',
'ipmitool-socat'])
node = obj_utils.create_test_node(
self.context, driver='ipmi',
raid_interface='agent',
console_interface='ipmitool-shellinabox',
console_interface='ipmitool-socat',
vendor_interface='no-vendor')
with task_manager.acquire(self.context, node.id) as task:
self._validate_interfaces(
task,
console=ipmitool.IPMIShellinaboxConsole,
console=ipmitool.IPMISocatConsole,
raid=agent.AgentRAID,
vendor=noop.NoVendor)
-1
View File
@@ -74,7 +74,6 @@ http-ipxe = "ironic.drivers.modules.ipxe:iPXEHttpBoot"
fake = "ironic.drivers.modules.fake:FakeConsole"
fake-graphical = "ironic.drivers.modules.fake:FakeGraphicalConsole"
ilo = "ironic.drivers.modules.ilo.console:IloConsoleInterface"
ipmitool-shellinabox = "ironic.drivers.modules.ipmitool:IPMIShellinaboxConsole"
ipmitool-socat = "ironic.drivers.modules.ipmitool:IPMISocatConsole"
no-console = "ironic.drivers.modules.noop:NoConsole"
redfish-graphical = "ironic.drivers.modules.redfish.graphical_console:RedfishGraphicalConsole"
@@ -0,0 +1,7 @@
---
upgrade:
- |
Removes all functionality related to shellinabox. This includes
ipmitool-shellinabox console interface, and node web console support since
it relied on shellinabox. Note that shellinabox has not been maintained
in years and is considered abandoned.