Fix iRMC driver to use certification file in HTTPS
This patch modifies iRMC driver to use certification file
when it connects to iRMC via HTTPS
Conflicts:
doc/source/admin/drivers/irmc.rst
driver-requirements.txt
ironic/drivers/modules/irmc/common.py
ironic/tests/unit/drivers/modules/irmc/test_common.py
ironic/tests/unit/drivers/modules/irmc/test_power.py
releasenotes/notes/irmc-add-certification-file-option-34e7a0062c768e58.yaml
Change-Id: If69ce1cf2789d9d60fb8e544596cf7d29eab514d
Co-authored-by: Kobayashi Daisuke <kobayashi.da-06@fujitsu.com>
Co-authored-by: Song Shukun <song.shukun@jp.fujitsu.com>
Story: 2009801
Task: 44345
(cherry picked from commit 64d7a7f307
)
This commit is contained in:
parent
ee00efc858
commit
6c0152afa1
@ -111,6 +111,9 @@ Here is a command example to enroll a node with ``irmc`` hardware type.
|
||||
Node configuration
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Configuration via ``driver_info``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Each node is configured for ``irmc`` hardware type by setting the following
|
||||
ironic node object's properties:
|
||||
|
||||
@ -126,6 +129,44 @@ Node configuration
|
||||
UEFI Secure Boot is required. Please refer to `UEFI Secure Boot Support`_
|
||||
for more information.
|
||||
|
||||
* If ``port`` in ``[irmc]`` section of ``/etc/ironic/ironic.conf`` or
|
||||
``driver_info/irmc_port`` is set to 443, ``driver_info/irmc_verify_ca``
|
||||
will take effect:
|
||||
|
||||
``driver_info/irmc_verify_ca`` property takes one of 4 value (default value
|
||||
is ``True``):
|
||||
|
||||
- ``True``: When set to ``True``, which certification file iRMC driver uses
|
||||
is determined by ``requests`` Python module.
|
||||
|
||||
Value of ``driver_info/irmc_verify_ca`` is passed to ``verify`` argument
|
||||
of functions defined in ``requests`` Python module. So which certification
|
||||
will be used is depend on behavior of ``requests`` module.
|
||||
(maybe certification provided by ``certifi`` Python module)
|
||||
|
||||
- ``False``: When set to ``False``, iRMC driver won't verify server
|
||||
certification with certification file during HTTPS connection with iRMC.
|
||||
Just stop to verify server certification, but does HTTPS.
|
||||
|
||||
.. warning::
|
||||
When set to ``False``, user must notice that it can result in
|
||||
vulnerable situation. Stopping verification of server certification
|
||||
during HTTPS connection means it cannot prevent Man-in-the-middle
|
||||
attack. When set to ``False``, Ironic user must take enough care
|
||||
around infrastructure environment in terms of security.
|
||||
(e.g. make sure network between Ironic conductor and iRMC is secure)
|
||||
|
||||
- string representing filesystem path to directory which contains
|
||||
certification file: In this case, iRMC driver uses certification file
|
||||
stored at specified directory. Ironic conductor must be able to access
|
||||
that directory. For iRMC to recongnize certification file, Ironic user
|
||||
must run ``openssl rehash <path_to_dir>``.
|
||||
|
||||
- string representing filesystem path to certification file: In this case,
|
||||
iRMC driver uses certification file specified. Ironic conductor must have
|
||||
access to that file.
|
||||
|
||||
|
||||
* The following properties are also required if ``irmc-virtual-media`` boot
|
||||
interface is used:
|
||||
|
||||
@ -140,6 +181,10 @@ Node configuration
|
||||
``irmc_deploy_iso`` and ``irmc_boot_iso`` accordingly before the Xena
|
||||
release.
|
||||
|
||||
|
||||
Configuration via ``ironic.conf``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* All of the nodes are configured by setting the following configuration
|
||||
options in the ``[irmc]`` section of ``/etc/ironic/ironic.conf``:
|
||||
|
||||
@ -176,6 +221,10 @@ Node configuration
|
||||
- ``snmp_security``: SNMP security name required for version ``v3``.
|
||||
Optional.
|
||||
|
||||
|
||||
Override ``ironic.conf`` configuration via ``driver_info``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Each node can be further configured by setting the following ironic
|
||||
node object's properties which override the parameter values in
|
||||
``[irmc]`` section of ``/etc/ironic/ironic.conf``:
|
||||
@ -189,6 +238,7 @@ Node configuration
|
||||
- ``driver_info/irmc_snmp_community`` property overrides ``snmp_community``.
|
||||
- ``driver_info/irmc_snmp_security`` property overrides ``snmp_security``.
|
||||
|
||||
|
||||
Optional functionalities for the ``irmc`` hardware type
|
||||
=======================================================
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
# These are available on pypi
|
||||
proliantutils>=2.13.0
|
||||
pysnmp>=4.3.0,<5.0.0
|
||||
python-scciclient>=0.8.0
|
||||
python-scciclient>=0.8.0,<0.13.0
|
||||
python-dracclient>=5.1.0,<9.0.0
|
||||
python-xclarityclient>=0.1.6
|
||||
|
||||
|
@ -15,8 +15,12 @@
|
||||
"""
|
||||
Common functionalities shared between different iRMC modules.
|
||||
"""
|
||||
import os
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
from oslo_utils import strutils
|
||||
from packaging import version
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
@ -25,6 +29,7 @@ from ironic.conf import CONF
|
||||
|
||||
scci = importutils.try_import('scciclient.irmc.scci')
|
||||
elcm = importutils.try_import('scciclient.irmc.elcm')
|
||||
scci_mod = importutils.try_import('scciclient')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
REQUIRED_PROPERTIES = {
|
||||
@ -53,9 +58,36 @@ OPTIONAL_PROPERTIES = {
|
||||
'irmc_snmp_security': _("SNMP security name required for version 'v3'. "
|
||||
"Optional."),
|
||||
}
|
||||
OPTIONAL_DRIVER_INFO_PROPERTIES = {
|
||||
'irmc_verify_ca': _('Either a Boolean value, a path to a CA_BUNDLE '
|
||||
'file or directory with certificates of trusted '
|
||||
'CAs. If set to True the driver will verify the '
|
||||
'host certificates; if False the driver will '
|
||||
'ignore verifying the SSL certificate. If it\'s '
|
||||
'a path the driver will use the specified '
|
||||
'certificate or one of the certificates in the '
|
||||
'directory. Defaults to True. Optional'),
|
||||
}
|
||||
|
||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
|
||||
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
|
||||
COMMON_PROPERTIES.update(OPTIONAL_DRIVER_INFO_PROPERTIES)
|
||||
|
||||
SCCI_CERTIFICATION_SUPPORT_VERSION_RANGES = [
|
||||
{'min': '0.8.2', 'upp': '0.9.0'},
|
||||
{'min': '0.9.4', 'upp': '0.10.0'},
|
||||
{'min': '0.10.1', 'upp': '0.11.0'},
|
||||
{'min': '0.11.3', 'upp': '0.12.0'},
|
||||
{'min': '0.12.0', 'upp': '0.13.0'}]
|
||||
|
||||
|
||||
def scci_support_certification():
|
||||
scciclient_version = version.parse(scci_mod.__version__)
|
||||
for rangev in SCCI_CERTIFICATION_SUPPORT_VERSION_RANGES:
|
||||
if (version.parse(rangev['min']) <= scciclient_version
|
||||
< version.parse(rangev['upp'])):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def parse_driver_info(node):
|
||||
@ -84,7 +116,9 @@ def parse_driver_info(node):
|
||||
# corresponding config names don't have 'irmc_' prefix
|
||||
opt = {param: info.get(param, CONF.irmc.get(param[len('irmc_'):]))
|
||||
for param in OPTIONAL_PROPERTIES}
|
||||
d_info = dict(req, **opt)
|
||||
opt_driver_info = {param: info.get(param)
|
||||
for param in OPTIONAL_DRIVER_INFO_PROPERTIES}
|
||||
d_info = dict(req, **opt, **opt_driver_info)
|
||||
d_info['irmc_port'] = utils.validate_network_port(
|
||||
d_info['irmc_port'], 'irmc_port')
|
||||
|
||||
@ -128,6 +162,38 @@ def parse_driver_info(node):
|
||||
else:
|
||||
error_msgs.append(
|
||||
_("'irmc_snmp_security' has to be set for SNMP version 3."))
|
||||
|
||||
verify_ca = d_info.get('irmc_verify_ca')
|
||||
if verify_ca is None:
|
||||
d_info['irmc_verify_ca'] = verify_ca = CONF.webserver_verify_ca
|
||||
|
||||
# Check if verify_ca is a Boolean or a file/directory in the file-system
|
||||
if isinstance(verify_ca, str):
|
||||
if ((os.path.isdir(verify_ca) and os.path.isabs(verify_ca))
|
||||
or (os.path.isfile(verify_ca) and os.path.isabs(verify_ca))):
|
||||
# If it's fullpath and dir/file, we don't need to do anything
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
d_info['irmc_verify_ca'] = strutils.bool_from_string(
|
||||
verify_ca, strict=True)
|
||||
except ValueError:
|
||||
error_msgs.append(
|
||||
_('Invalid value type set in driver_info/'
|
||||
'irmc_verify_ca on node %(node)s. '
|
||||
'The value should be a Boolean or the path '
|
||||
'to a file/directory, not "%(value)s"'
|
||||
) % {'value': verify_ca, 'node': node.uuid})
|
||||
elif isinstance(verify_ca, bool):
|
||||
# If it's a boolean it's grand, we don't need to do anything
|
||||
pass
|
||||
else:
|
||||
error_msgs.append(
|
||||
_('Invalid value type set in driver_info/irmc_verify_ca '
|
||||
'on node %(node)s. The value should be a Boolean or the path '
|
||||
'to a file/directory, not "%(value)s"') % {'value': verify_ca,
|
||||
'node': node.uuid})
|
||||
|
||||
if error_msgs:
|
||||
msg = (_("The following errors were encountered while parsing "
|
||||
"driver_info:\n%s") % "\n".join(error_msgs))
|
||||
@ -147,16 +213,30 @@ def get_irmc_client(node):
|
||||
:raises: InvalidParameterValue on invalid inputs.
|
||||
:raises: MissingParameterValue if some mandatory information
|
||||
is missing on the node
|
||||
:raises: IRMCOperationError if iRMC operation failed
|
||||
"""
|
||||
driver_info = parse_driver_info(node)
|
||||
|
||||
scci_client = scci.get_client(
|
||||
driver_info['irmc_address'],
|
||||
driver_info['irmc_username'],
|
||||
driver_info['irmc_password'],
|
||||
port=driver_info['irmc_port'],
|
||||
auth_method=driver_info['irmc_auth_method'],
|
||||
client_timeout=driver_info['irmc_client_timeout'])
|
||||
if scci_support_certification():
|
||||
scci_client = scci.get_client(
|
||||
driver_info['irmc_address'],
|
||||
driver_info['irmc_username'],
|
||||
driver_info['irmc_password'],
|
||||
port=driver_info['irmc_port'],
|
||||
auth_method=driver_info['irmc_auth_method'],
|
||||
verify=driver_info.get('irmc_verify_ca'),
|
||||
client_timeout=driver_info['irmc_client_timeout'])
|
||||
else:
|
||||
if driver_info['irmc_port'] == 443:
|
||||
LOG.warning("Installed version of python-scciclient doesn't "
|
||||
"support certification on HTTPS connection.")
|
||||
scci_client = scci.get_client(
|
||||
driver_info['irmc_address'],
|
||||
driver_info['irmc_username'],
|
||||
driver_info['irmc_password'],
|
||||
port=driver_info['irmc_port'],
|
||||
auth_method=driver_info['irmc_auth_method'],
|
||||
client_timeout=driver_info['irmc_client_timeout'])
|
||||
return scci_client
|
||||
|
||||
|
||||
@ -192,13 +272,27 @@ def get_irmc_report(node):
|
||||
"""
|
||||
driver_info = parse_driver_info(node)
|
||||
|
||||
return scci.get_report(
|
||||
driver_info['irmc_address'],
|
||||
driver_info['irmc_username'],
|
||||
driver_info['irmc_password'],
|
||||
port=driver_info['irmc_port'],
|
||||
auth_method=driver_info['irmc_auth_method'],
|
||||
client_timeout=driver_info['irmc_client_timeout'])
|
||||
if scci_support_certification():
|
||||
report = scci.get_report(
|
||||
driver_info['irmc_address'],
|
||||
driver_info['irmc_username'],
|
||||
driver_info['irmc_password'],
|
||||
port=driver_info['irmc_port'],
|
||||
auth_method=driver_info['irmc_auth_method'],
|
||||
verify=driver_info.get('irmc_verify_ca'),
|
||||
client_timeout=driver_info['irmc_client_timeout'])
|
||||
else:
|
||||
if driver_info['irmc_port'] == 443:
|
||||
LOG.warning("Installed version of python-scciclient doesn't "
|
||||
"support certification on HTTPS connection.")
|
||||
report = scci.get_report(
|
||||
driver_info['irmc_address'],
|
||||
driver_info['irmc_username'],
|
||||
driver_info['irmc_password'],
|
||||
port=driver_info['irmc_port'],
|
||||
auth_method=driver_info['irmc_auth_method'],
|
||||
client_timeout=driver_info['irmc_client_timeout'])
|
||||
return report
|
||||
|
||||
|
||||
def get_secure_boot_mode(node):
|
||||
|
@ -63,6 +63,7 @@ PARSED_IFNO = {
|
||||
'irmc_snmp_version': 'v2c',
|
||||
'irmc_snmp_security': None,
|
||||
'irmc_sensor_method': 'ipmitool',
|
||||
'irmc_verify_ca': True,
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
Test class for common methods used by iRMC modules.
|
||||
"""
|
||||
|
||||
import os
|
||||
from unittest import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
@ -68,6 +69,7 @@ class IRMCValidateParametersTestCase(BaseIRMCTest):
|
||||
self.assertEqual(161, info['irmc_snmp_port'])
|
||||
self.assertEqual('public', info['irmc_snmp_community'])
|
||||
self.assertFalse(info['irmc_snmp_security'])
|
||||
self.assertTrue(info['irmc_verify_ca'])
|
||||
|
||||
def test_parse_driver_option_default(self):
|
||||
self.node.driver_info = {
|
||||
@ -81,6 +83,7 @@ class IRMCValidateParametersTestCase(BaseIRMCTest):
|
||||
self.assertEqual(443, info['irmc_port'])
|
||||
self.assertEqual(60, info['irmc_client_timeout'])
|
||||
self.assertEqual('ipmitool', info['irmc_sensor_method'])
|
||||
self.assertEqual(True, info['irmc_verify_ca'])
|
||||
|
||||
def test_parse_driver_info_missing_address(self):
|
||||
del self.node.driver_info['irmc_address']
|
||||
@ -153,25 +156,176 @@ class IRMCValidateParametersTestCase(BaseIRMCTest):
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
irmc_common.parse_driver_info, self.node)
|
||||
|
||||
@mock.patch.object(os.path, 'isabs', return_value=True, autospec=True)
|
||||
@mock.patch.object(os.path, 'isdir', return_value=True, autospec=True)
|
||||
def test_parse_driver_info_dir_path_verify_ca(self, mock_isdir,
|
||||
mock_isabs):
|
||||
fake_path = 'absolute/path/to/a/valid/CA'
|
||||
self.node.driver_info['irmc_verify_ca'] = fake_path
|
||||
info = irmc_common.parse_driver_info(self.node)
|
||||
self.assertEqual(fake_path, info['irmc_verify_ca'])
|
||||
mock_isdir.assert_called_once_with(fake_path)
|
||||
mock_isabs.assert_called_once_with(fake_path)
|
||||
|
||||
@mock.patch.object(os.path, 'isabs', return_value=True, autospec=True)
|
||||
@mock.patch.object(os.path, 'isfile', return_value=True, autospec=True)
|
||||
def test_parse_driver_info_file_path_verify_ca(self, mock_isfile,
|
||||
mock_isabs):
|
||||
fake_path = 'absolute/path/to/a/valid/ca.pem'
|
||||
self.node.driver_info['irmc_verify_ca'] = fake_path
|
||||
info = irmc_common.parse_driver_info(self.node)
|
||||
self.assertEqual(fake_path, info['irmc_verify_ca'])
|
||||
mock_isfile.assert_called_once_with(fake_path)
|
||||
mock_isabs.assert_called_once_with(fake_path)
|
||||
|
||||
def test_parse_driver_info_string_bool_verify_ca(self):
|
||||
self.node.driver_info['irmc_verify_ca'] = "False"
|
||||
info = irmc_common.parse_driver_info(self.node)
|
||||
self.assertFalse(info['irmc_verify_ca'])
|
||||
|
||||
def test_parse_driver_info_invalid_verify_ca(self):
|
||||
self.node.driver_info['irmc_verify_ca'] = "1234"
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
irmc_common.parse_driver_info, self.node)
|
||||
self.node.driver_info['irmc_verify_ca'] = 1234
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
irmc_common.parse_driver_info, self.node)
|
||||
|
||||
|
||||
class IRMCCommonMethodsTestCase(BaseIRMCTest):
|
||||
|
||||
@mock.patch.object(irmc_common, 'LOG', autospec=True)
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_get_irmc_client(self, mock_scci):
|
||||
self.info['irmc_port'] = 80
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
mock_scci.get_client.return_value = 'get_client'
|
||||
returned_mock_scci_get_client = irmc_common.get_irmc_client(self.node)
|
||||
mock_scci.get_client.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_client', returned_mock_scci_get_client)
|
||||
def test_get_irmc_client_cert_support_http(self, mock_scci,
|
||||
mock_scciclient, mock_LOG):
|
||||
scci_version_list = ['0.8.2', '0.8.3.1', '0.9.4', '0.10.1', '0.10.2',
|
||||
'0.11.3', '0.11.4', '0.12.0', '0.12.1']
|
||||
for ver in scci_version_list:
|
||||
with self.subTest(ver=ver):
|
||||
mock_scciclient.__version__ = ver
|
||||
self.info['irmc_port'] = 80
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
self.info['irmc_verify_ca'] = True
|
||||
mock_scci.get_client.return_value = 'get_client'
|
||||
returned_mock_scci_get_client = irmc_common.get_irmc_client(
|
||||
self.node)
|
||||
mock_scci.get_client.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
verify=self.info['irmc_verify_ca'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_client', returned_mock_scci_get_client)
|
||||
mock_LOG.warning.assert_not_called()
|
||||
mock_LOG.warning.reset_mock()
|
||||
|
||||
@mock.patch.object(irmc_common, 'LOG', autospec=True)
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_get_irmc_client_cert_support_https(self, mock_scci,
|
||||
mock_scciclient, mock_LOG):
|
||||
scci_version_list = ['0.8.2', '0.8.3.1', '0.9.4', '0.10.1', '0.10.2',
|
||||
'0.11.3', '0.11.4', '0.12.0', '0.12.1']
|
||||
self.node.driver_info = {
|
||||
"irmc_address": "1.2.3.4",
|
||||
"irmc_username": "admin0",
|
||||
"irmc_password": "fake0",
|
||||
"irmc_port": "443",
|
||||
"irmc_auth_method": "digest",
|
||||
}
|
||||
|
||||
for ver in scci_version_list:
|
||||
with self.subTest(ver=ver):
|
||||
mock_scciclient.__version__ = ver
|
||||
self.info['irmc_port'] = 443
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
self.info['irmc_verify_ca'] = True
|
||||
mock_scci.get_client.return_value = 'get_client'
|
||||
returned_mock_scci_get_client = irmc_common.get_irmc_client(
|
||||
self.node)
|
||||
mock_scci.get_client.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
verify=self.info['irmc_verify_ca'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_client', returned_mock_scci_get_client)
|
||||
mock_LOG.warning.assert_not_called()
|
||||
mock_LOG.warning.reset_mock()
|
||||
|
||||
@mock.patch.object(irmc_common, 'LOG', autospec=True)
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_get_irmc_client_no_cert_support_http(self, mock_scci,
|
||||
mock_scciclient, mock_LOG):
|
||||
scci_version_list = ['0.8.0', '0.8.1', '0.9.0', '0.9.3', '0.10.0',
|
||||
'0.11.2']
|
||||
|
||||
for ver in scci_version_list:
|
||||
with self.subTest(ver=ver):
|
||||
mock_scciclient.__version__ = ver
|
||||
self.info['irmc_port'] = 80
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
mock_scci.get_client.return_value = 'get_client'
|
||||
returned_mock_scci_get_client = irmc_common.get_irmc_client(
|
||||
self.node)
|
||||
mock_scci.get_client.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_client', returned_mock_scci_get_client)
|
||||
mock_LOG.warning.assert_not_called()
|
||||
mock_LOG.warning.reset_mock()
|
||||
|
||||
@mock.patch.object(irmc_common, 'LOG', autospec=True)
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_get_irmc_client_no_cert_support_https(self, mock_scci,
|
||||
mock_scciclient, mock_LOG):
|
||||
scci_version_list = ['0.8.0', '0.8.1', '0.9.0', '0.9.3', '0.10.0',
|
||||
'0.11.2']
|
||||
self.node.driver_info = {
|
||||
"irmc_address": "1.2.3.4",
|
||||
"irmc_username": "admin0",
|
||||
"irmc_password": "fake0",
|
||||
"irmc_port": "443",
|
||||
"irmc_auth_method": "digest",
|
||||
}
|
||||
|
||||
for ver in scci_version_list:
|
||||
with self.subTest(ver=ver):
|
||||
mock_scciclient.__version__ = ver
|
||||
self.info['irmc_port'] = 443
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
mock_scci.get_client.return_value = 'get_client'
|
||||
returned_mock_scci_get_client = irmc_common.get_irmc_client(
|
||||
self.node)
|
||||
mock_scci.get_client.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_client', returned_mock_scci_get_client)
|
||||
mock_LOG.warning.assert_called_once()
|
||||
mock_LOG.warning.reset_mock()
|
||||
|
||||
def test_update_ipmi_properties(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
@ -187,22 +341,139 @@ class IRMCCommonMethodsTestCase(BaseIRMCTest):
|
||||
expected_info = dict(self.info, **ipmi_info)
|
||||
self.assertEqual(expected_info, actual_info)
|
||||
|
||||
@mock.patch.object(irmc_common, 'LOG', autospec=True)
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_get_irmc_report(self, mock_scci):
|
||||
self.info['irmc_port'] = 80
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
mock_scci.get_report.return_value = 'get_report'
|
||||
returned_mock_scci_get_report = irmc_common.get_irmc_report(self.node)
|
||||
mock_scci.get_report.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_report', returned_mock_scci_get_report)
|
||||
def test_get_irmc_report_cert_support_http(self, mock_scci,
|
||||
mock_scciclient, mock_LOG):
|
||||
scci_version_list = ['0.8.2', '0.8.3.1', '0.9.4', '0.10.1', '0.10.2',
|
||||
'0.11.3', '0.11.4', '0.12.0', '0.12.1']
|
||||
|
||||
for ver in scci_version_list:
|
||||
with self.subTest(ver=ver):
|
||||
mock_scciclient.__version__ = ver
|
||||
self.info['irmc_port'] = 80
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
self.info['irmc_verify_ca'] = True
|
||||
mock_scci.get_report.return_value = 'get_report'
|
||||
returned_mock_scci_get_report = irmc_common.get_irmc_report(
|
||||
self.node)
|
||||
mock_scci.get_report.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
verify=self.info['irmc_verify_ca'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_report', returned_mock_scci_get_report)
|
||||
mock_LOG.warning.assert_not_called()
|
||||
mock_LOG.warning.reset_mock()
|
||||
|
||||
@mock.patch.object(irmc_common, 'LOG', autospec=True)
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_get_irmc_report_cert_support_https(self, mock_scci,
|
||||
mock_scciclient, mock_LOG):
|
||||
scci_version_list = ['0.8.2', '0.8.3.1', '0.9.4', '0.10.1', '0.10.2',
|
||||
'0.11.3', '0.11.4', '0.12.0', '0.12.1']
|
||||
self.node.driver_info = {
|
||||
"irmc_address": "1.2.3.4",
|
||||
"irmc_username": "admin0",
|
||||
"irmc_password": "fake0",
|
||||
"irmc_port": "443",
|
||||
"irmc_auth_method": "digest",
|
||||
}
|
||||
|
||||
for ver in scci_version_list:
|
||||
with self.subTest(ver=ver):
|
||||
mock_scciclient.__version__ = ver
|
||||
self.info['irmc_port'] = 443
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
self.info['irmc_verify_ca'] = True
|
||||
mock_scci.get_report.return_value = 'get_report'
|
||||
returned_mock_scci_get_report = irmc_common.get_irmc_report(
|
||||
self.node)
|
||||
mock_scci.get_report.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
verify=self.info['irmc_verify_ca'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_report', returned_mock_scci_get_report)
|
||||
mock_LOG.warning.assert_not_called()
|
||||
mock_LOG.warning.reset_mock()
|
||||
|
||||
@mock.patch.object(irmc_common, 'LOG', autospec=True)
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_get_irmc_report_no_cert_support_http(self, mock_scci,
|
||||
mock_scciclient, mock_LOG):
|
||||
scci_version_list = ['0.8.0', '0.8.1', '0.9.0', '0.9.3', '0.10.0',
|
||||
'0.11.2']
|
||||
|
||||
for ver in scci_version_list:
|
||||
with self.subTest(ver=ver):
|
||||
mock_scciclient.__version__ = ver
|
||||
self.info['irmc_port'] = 80
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
mock_scci.get_report.return_value = 'get_report'
|
||||
returned_mock_scci_get_report = irmc_common.get_irmc_report(
|
||||
self.node)
|
||||
mock_scci.get_report.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_report', returned_mock_scci_get_report)
|
||||
mock_LOG.warning.assert_not_called()
|
||||
mock_LOG.warning.reset_mock()
|
||||
|
||||
@mock.patch.object(irmc_common, 'LOG', autospec=True)
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_get_irmc_report_no_cert_support_https(self, mock_scci,
|
||||
mock_scciclient, mock_LOG):
|
||||
scci_version_list = ['0.8.0', '0.8.1', '0.9.0', '0.9.3', '0.10.0',
|
||||
'0.11.2']
|
||||
self.node.driver_info = {
|
||||
"irmc_address": "1.2.3.4",
|
||||
"irmc_username": "admin0",
|
||||
"irmc_password": "fake0",
|
||||
"irmc_port": "443",
|
||||
"irmc_auth_method": "digest",
|
||||
}
|
||||
|
||||
for ver in scci_version_list:
|
||||
with self.subTest(ver=ver):
|
||||
mock_scciclient.__version__ = ver
|
||||
self.info['irmc_port'] = 443
|
||||
self.info['irmc_auth_method'] = 'digest'
|
||||
self.info['irmc_client_timeout'] = 60
|
||||
mock_scci.get_report.return_value = 'get_report'
|
||||
returned_mock_scci_get_report = irmc_common.get_irmc_report(
|
||||
self.node)
|
||||
mock_scci.get_report.assert_called_with(
|
||||
self.info['irmc_address'],
|
||||
self.info['irmc_username'],
|
||||
self.info['irmc_password'],
|
||||
port=self.info['irmc_port'],
|
||||
auth_method=self.info['irmc_auth_method'],
|
||||
client_timeout=self.info['irmc_client_timeout'])
|
||||
self.assertEqual('get_report', returned_mock_scci_get_report)
|
||||
mock_LOG.warning.assert_called_once()
|
||||
mock_LOG.warning.reset_mock()
|
||||
|
||||
def test_out_range_port(self):
|
||||
self.assertRaises(ValueError, cfg.CONF.set_override,
|
||||
|
@ -211,6 +211,7 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
|
||||
_wait_power_state_mock.assert_called_once_with(task, target_state,
|
||||
timeout=None)
|
||||
|
||||
@mock.patch.object(irmc_common, 'scci_mod', spec_set=['__version__'])
|
||||
@mock.patch.object(irmc_power, '_wait_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed',
|
||||
@ -218,7 +219,8 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
|
||||
def test__set_power_state_invalid_target_state(
|
||||
self,
|
||||
attach_boot_iso_if_needed_mock,
|
||||
_wait_power_state_mock):
|
||||
_wait_power_state_mock, mock_scciclient):
|
||||
mock_scciclient.__version__ = '0.8.2'
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
|
@ -702,8 +702,8 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
raid._commit_raid_config(task)
|
||||
get_raid_adapter_mock.assert_called_once_with(
|
||||
irmc_common.parse_driver_info(task.node))
|
||||
irmc_info = irmc_common.parse_driver_info(task.node)
|
||||
get_raid_adapter_mock.assert_called_once_with(irmc_info)
|
||||
update_raid_info_mock.assert_called_once_with(
|
||||
task.node, task.node.raid_config)
|
||||
set_async_step_flags_mock.assert_called_once_with(
|
||||
|
@ -33,6 +33,7 @@ oslo.utils==4.5.0
|
||||
oslo.versionedobjects==1.31.2
|
||||
osprofiler==1.5.0
|
||||
os-traits==0.4.0
|
||||
packaging==16.5
|
||||
pbr==3.1.1
|
||||
pecan==1.0.0
|
||||
psutil==3.2.2
|
||||
|
@ -0,0 +1,18 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Adds ``driver_info/irmc_verify_ca`` option to specify certification file.
|
||||
Default value of driver_info/irmc_verify_ca is True.
|
||||
security:
|
||||
- |
|
||||
Modifies the ``irmc`` hardware type to include a capability to control
|
||||
enforcement of HTTPS certificate verification. By default this is enforced.
|
||||
python-scciclient version must be one of >=0.8.2,<0.9.0, >=0.9.4,<0.10.0,
|
||||
>=0.10.1,<0.11.0, >=0.11.3,<0.12.0 or >=0.12.0,<0.13.0
|
||||
Or certificate verification will not occur.
|
||||
upgrade:
|
||||
- |
|
||||
On Yoga release, to use certification file on HTTPS connection,
|
||||
iRMC driver requires python-scciclient version to be one of >=0.8.2,<0.9.0,
|
||||
>=0.9.4,<0.10.0, >=0.10.1,<0.11.0, >=0.11.3,<0.12.0 or >=0.12.0,<0.13.0
|
||||
and packaging >=16.5
|
@ -44,3 +44,4 @@ futurist>=1.2.0 # Apache-2.0
|
||||
tooz>=2.7.0 # Apache-2.0
|
||||
openstacksdk>=0.48.0 # Apache-2.0
|
||||
sushy>=3.10.0
|
||||
packaging>=16.5 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user