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 Depends-On: https://review.opendev.org/c/openstack/ironic/+/852250 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
This commit is contained in:
parent
45c9c3029f
commit
64d7a7f307
@ -111,6 +111,9 @@ Here is a command example to enroll a node with ``irmc`` hardware type.
|
|||||||
Node configuration
|
Node configuration
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Configuration via ``driver_info``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* Each node is configured for ``irmc`` hardware type by setting the following
|
* Each node is configured for ``irmc`` hardware type by setting the following
|
||||||
ironic node object's properties:
|
ironic node object's properties:
|
||||||
|
|
||||||
@ -126,6 +129,44 @@ Node configuration
|
|||||||
UEFI Secure Boot is required. Please refer to `UEFI Secure Boot Support`_
|
UEFI Secure Boot is required. Please refer to `UEFI Secure Boot Support`_
|
||||||
for more information.
|
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
|
* The following properties are also required if ``irmc-virtual-media`` boot
|
||||||
interface is used:
|
interface is used:
|
||||||
|
|
||||||
@ -150,6 +191,9 @@ Node configuration
|
|||||||
- ``driver_info/irmc_snmp_priv_password`` property to be the privacy protocol
|
- ``driver_info/irmc_snmp_priv_password`` property to be the privacy protocol
|
||||||
pass phrase. The length of pass phrase should be at least 8 characters.
|
pass phrase. The length of pass phrase should be at least 8 characters.
|
||||||
|
|
||||||
|
Configuration via ``ironic.conf``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* All of the nodes are configured by setting the following configuration
|
* All of the nodes are configured by setting the following configuration
|
||||||
options in the ``[irmc]`` section of ``/etc/ironic/ironic.conf``:
|
options in the ``[irmc]`` section of ``/etc/ironic/ironic.conf``:
|
||||||
|
|
||||||
@ -198,6 +242,10 @@ Node configuration
|
|||||||
``driver_info/irmc_snmp_user`` parameter for each node if SNMPv3
|
``driver_info/irmc_snmp_user`` parameter for each node if SNMPv3
|
||||||
inspection is needed.
|
inspection is needed.
|
||||||
|
|
||||||
|
|
||||||
|
Override ``ironic.conf`` configuration via ``driver_info``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* Each node can be further configured by setting the following ironic
|
* Each node can be further configured by setting the following ironic
|
||||||
node object's properties which override the parameter values in
|
node object's properties which override the parameter values in
|
||||||
``[irmc]`` section of ``/etc/ironic/ironic.conf``:
|
``[irmc]`` section of ``/etc/ironic/ironic.conf``:
|
||||||
@ -215,6 +263,7 @@ Node configuration
|
|||||||
- ``driver_info/irmc_snmp_priv_proto`` property overrides
|
- ``driver_info/irmc_snmp_priv_proto`` property overrides
|
||||||
``snmp_priv_proto``.
|
``snmp_priv_proto``.
|
||||||
|
|
||||||
|
|
||||||
Optional functionalities for the ``irmc`` hardware type
|
Optional functionalities for the ``irmc`` hardware type
|
||||||
=======================================================
|
=======================================================
|
||||||
|
|
||||||
|
@ -15,8 +15,11 @@
|
|||||||
"""
|
"""
|
||||||
Common functionalities shared between different iRMC modules.
|
Common functionalities shared between different iRMC modules.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
from oslo_utils import strutils
|
||||||
|
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.common.i18n import _
|
from ironic.common.i18n import _
|
||||||
@ -46,6 +49,16 @@ OPTIONAL_PROPERTIES = {
|
|||||||
"'ipmitool' or 'scci'. The default value is "
|
"'ipmitool' or 'scci'. The default value is "
|
||||||
"'ipmitool'. Optional."),
|
"'ipmitool'. 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'),
|
||||||
|
}
|
||||||
|
|
||||||
SNMP_PROPERTIES = {
|
SNMP_PROPERTIES = {
|
||||||
'irmc_snmp_version': _("SNMP protocol version; either 'v1', 'v2c', or "
|
'irmc_snmp_version': _("SNMP protocol version; either 'v1', 'v2c', or "
|
||||||
@ -84,6 +97,7 @@ SNMP_V3_DEPRECATED_PROPERTIES = {
|
|||||||
|
|
||||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
|
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
|
||||||
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
|
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
|
||||||
|
COMMON_PROPERTIES.update(OPTIONAL_DRIVER_INFO_PROPERTIES)
|
||||||
COMMON_PROPERTIES.update(SNMP_PROPERTIES)
|
COMMON_PROPERTIES.update(SNMP_PROPERTIES)
|
||||||
COMMON_PROPERTIES.update(SNMP_V3_REQUIRED_PROPERTIES)
|
COMMON_PROPERTIES.update(SNMP_V3_REQUIRED_PROPERTIES)
|
||||||
COMMON_PROPERTIES.update(SNMP_V3_OPTIONAL_PROPERTIES)
|
COMMON_PROPERTIES.update(SNMP_V3_OPTIONAL_PROPERTIES)
|
||||||
@ -116,7 +130,9 @@ def parse_driver_info(node):
|
|||||||
# corresponding config names don't have 'irmc_' prefix
|
# corresponding config names don't have 'irmc_' prefix
|
||||||
opt = {param: info.get(param, CONF.irmc.get(param[len('irmc_'):]))
|
opt = {param: info.get(param, CONF.irmc.get(param[len('irmc_'):]))
|
||||||
for param in OPTIONAL_PROPERTIES}
|
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'] = utils.validate_network_port(
|
||||||
d_info['irmc_port'], 'irmc_port')
|
d_info['irmc_port'], 'irmc_port')
|
||||||
|
|
||||||
@ -137,6 +153,38 @@ def parse_driver_info(node):
|
|||||||
error_msgs.append(
|
error_msgs.append(
|
||||||
_("Value '%s' is not supported for 'irmc_sensor_method'.") %
|
_("Value '%s' is not supported for 'irmc_sensor_method'.") %
|
||||||
d_info['irmc_sensor_method'])
|
d_info['irmc_sensor_method'])
|
||||||
|
|
||||||
|
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:
|
if error_msgs:
|
||||||
msg = (_("The following errors were encountered while parsing "
|
msg = (_("The following errors were encountered while parsing "
|
||||||
"driver_info:\n%s") % "\n".join(error_msgs))
|
"driver_info:\n%s") % "\n".join(error_msgs))
|
||||||
@ -287,6 +335,7 @@ def get_irmc_client(node):
|
|||||||
:raises: InvalidParameterValue on invalid inputs.
|
:raises: InvalidParameterValue on invalid inputs.
|
||||||
:raises: MissingParameterValue if some mandatory information
|
:raises: MissingParameterValue if some mandatory information
|
||||||
is missing on the node
|
is missing on the node
|
||||||
|
:raises: IRMCOperationError if iRMC operation failed
|
||||||
"""
|
"""
|
||||||
driver_info = parse_driver_info(node)
|
driver_info = parse_driver_info(node)
|
||||||
|
|
||||||
@ -296,6 +345,7 @@ def get_irmc_client(node):
|
|||||||
driver_info['irmc_password'],
|
driver_info['irmc_password'],
|
||||||
port=driver_info['irmc_port'],
|
port=driver_info['irmc_port'],
|
||||||
auth_method=driver_info['irmc_auth_method'],
|
auth_method=driver_info['irmc_auth_method'],
|
||||||
|
verify=driver_info.get('irmc_verify_ca'),
|
||||||
client_timeout=driver_info['irmc_client_timeout'])
|
client_timeout=driver_info['irmc_client_timeout'])
|
||||||
return scci_client
|
return scci_client
|
||||||
|
|
||||||
@ -338,6 +388,7 @@ def get_irmc_report(node):
|
|||||||
driver_info['irmc_password'],
|
driver_info['irmc_password'],
|
||||||
port=driver_info['irmc_port'],
|
port=driver_info['irmc_port'],
|
||||||
auth_method=driver_info['irmc_auth_method'],
|
auth_method=driver_info['irmc_auth_method'],
|
||||||
|
verify=driver_info.get('irmc_verify_ca'),
|
||||||
client_timeout=driver_info['irmc_client_timeout'])
|
client_timeout=driver_info['irmc_client_timeout'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ PARSED_IFNO = {
|
|||||||
'irmc_snmp_port': 161,
|
'irmc_snmp_port': 161,
|
||||||
'irmc_snmp_version': snmp.SNMP_V2C,
|
'irmc_snmp_version': snmp.SNMP_V2C,
|
||||||
'irmc_sensor_method': 'ipmitool',
|
'irmc_sensor_method': 'ipmitool',
|
||||||
|
'irmc_verify_ca': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
Test class for common methods used by iRMC modules.
|
Test class for common methods used by iRMC modules.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -71,6 +72,7 @@ class IRMCValidateParametersTestCase(BaseIRMCTest):
|
|||||||
self.assertEqual(snmp.SNMP_V2C, info['irmc_snmp_version'])
|
self.assertEqual(snmp.SNMP_V2C, info['irmc_snmp_version'])
|
||||||
self.assertEqual(161, info['irmc_snmp_port'])
|
self.assertEqual(161, info['irmc_snmp_port'])
|
||||||
self.assertEqual('public', info['irmc_snmp_community'])
|
self.assertEqual('public', info['irmc_snmp_community'])
|
||||||
|
self.assertTrue(info['irmc_verify_ca'])
|
||||||
|
|
||||||
def test_parse_driver_info_snmpv3(self):
|
def test_parse_driver_info_snmpv3(self):
|
||||||
self.node.driver_info['irmc_snmp_version'] = 'v3'
|
self.node.driver_info['irmc_snmp_version'] = 'v3'
|
||||||
@ -111,6 +113,7 @@ class IRMCValidateParametersTestCase(BaseIRMCTest):
|
|||||||
self.assertEqual(443, info['irmc_port'])
|
self.assertEqual(443, info['irmc_port'])
|
||||||
self.assertEqual(60, info['irmc_client_timeout'])
|
self.assertEqual(60, info['irmc_client_timeout'])
|
||||||
self.assertEqual('ipmitool', info['irmc_sensor_method'])
|
self.assertEqual('ipmitool', info['irmc_sensor_method'])
|
||||||
|
self.assertEqual(True, info['irmc_verify_ca'])
|
||||||
|
|
||||||
def test_parse_driver_info_missing_address(self):
|
def test_parse_driver_info_missing_address(self):
|
||||||
del self.node.driver_info['irmc_address']
|
del self.node.driver_info['irmc_address']
|
||||||
@ -274,6 +277,41 @@ class IRMCValidateParametersTestCase(BaseIRMCTest):
|
|||||||
self.assertRaises(exception.InvalidParameterValue,
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
irmc_common.parse_driver_info, self.node)
|
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):
|
class IRMCCommonMethodsTestCase(BaseIRMCTest):
|
||||||
|
|
||||||
@ -283,6 +321,7 @@ class IRMCCommonMethodsTestCase(BaseIRMCTest):
|
|||||||
self.info['irmc_port'] = 80
|
self.info['irmc_port'] = 80
|
||||||
self.info['irmc_auth_method'] = 'digest'
|
self.info['irmc_auth_method'] = 'digest'
|
||||||
self.info['irmc_client_timeout'] = 60
|
self.info['irmc_client_timeout'] = 60
|
||||||
|
self.info['irmc_verify_ca'] = True
|
||||||
mock_scci.get_client.return_value = 'get_client'
|
mock_scci.get_client.return_value = 'get_client'
|
||||||
returned_mock_scci_get_client = irmc_common.get_irmc_client(self.node)
|
returned_mock_scci_get_client = irmc_common.get_irmc_client(self.node)
|
||||||
mock_scci.get_client.assert_called_with(
|
mock_scci.get_client.assert_called_with(
|
||||||
@ -291,6 +330,7 @@ class IRMCCommonMethodsTestCase(BaseIRMCTest):
|
|||||||
self.info['irmc_password'],
|
self.info['irmc_password'],
|
||||||
port=self.info['irmc_port'],
|
port=self.info['irmc_port'],
|
||||||
auth_method=self.info['irmc_auth_method'],
|
auth_method=self.info['irmc_auth_method'],
|
||||||
|
verify=self.info['irmc_verify_ca'],
|
||||||
client_timeout=self.info['irmc_client_timeout'])
|
client_timeout=self.info['irmc_client_timeout'])
|
||||||
self.assertEqual('get_client', returned_mock_scci_get_client)
|
self.assertEqual('get_client', returned_mock_scci_get_client)
|
||||||
|
|
||||||
@ -314,6 +354,7 @@ class IRMCCommonMethodsTestCase(BaseIRMCTest):
|
|||||||
self.info['irmc_port'] = 80
|
self.info['irmc_port'] = 80
|
||||||
self.info['irmc_auth_method'] = 'digest'
|
self.info['irmc_auth_method'] = 'digest'
|
||||||
self.info['irmc_client_timeout'] = 60
|
self.info['irmc_client_timeout'] = 60
|
||||||
|
self.info['irmc_verify_ca'] = True
|
||||||
mock_scci.get_report.return_value = 'get_report'
|
mock_scci.get_report.return_value = 'get_report'
|
||||||
returned_mock_scci_get_report = irmc_common.get_irmc_report(self.node)
|
returned_mock_scci_get_report = irmc_common.get_irmc_report(self.node)
|
||||||
mock_scci.get_report.assert_called_with(
|
mock_scci.get_report.assert_called_with(
|
||||||
@ -322,6 +363,7 @@ class IRMCCommonMethodsTestCase(BaseIRMCTest):
|
|||||||
self.info['irmc_password'],
|
self.info['irmc_password'],
|
||||||
port=self.info['irmc_port'],
|
port=self.info['irmc_port'],
|
||||||
auth_method=self.info['irmc_auth_method'],
|
auth_method=self.info['irmc_auth_method'],
|
||||||
|
verify=self.info['irmc_verify_ca'],
|
||||||
client_timeout=self.info['irmc_client_timeout'])
|
client_timeout=self.info['irmc_client_timeout'])
|
||||||
self.assertEqual('get_report', returned_mock_scci_get_report)
|
self.assertEqual('get_report', returned_mock_scci_get_report)
|
||||||
|
|
||||||
|
@ -702,8 +702,8 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
|
|||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=True) as task:
|
shared=True) as task:
|
||||||
raid._commit_raid_config(task)
|
raid._commit_raid_config(task)
|
||||||
get_raid_adapter_mock.assert_called_once_with(
|
irmc_info = irmc_common.parse_driver_info(task.node)
|
||||||
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(
|
update_raid_info_mock.assert_called_once_with(
|
||||||
task.node, task.node.raid_config)
|
task.node, task.node.raid_config)
|
||||||
set_async_step_flags_mock.assert_called_once_with(
|
set_async_step_flags_mock.assert_called_once_with(
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
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 >= 0.12.0 is required.
|
Loading…
Reference in New Issue
Block a user