Adds create_csr and add_https_certificate clean step
This commit adds new clean steps create_csr and add_https_certificate to allow users to create certificate signing request and adds https certificate to the iLO. Story: 2009118 Task: 43016 Change-Id: I1e2da0e0da5e397b6e519e817e0bf60a02bbf007
This commit is contained in:
parent
b796d7b833
commit
9c19dd6ef3
@ -55,6 +55,8 @@ The hardware type ``ilo`` supports following HPE server features:
|
||||
* `Updating security parameters as manual clean step`_
|
||||
* `Update Minimum Password Length security parameter as manual clean step`_
|
||||
* `Update Authentication Failure Logging security parameter as manual clean step`_
|
||||
* `Create Certificate Signing Request(CSR) as manual clean step`_
|
||||
* `Add HTTPS Certificate as manual clean step`_
|
||||
* `Activating iLO Advanced license as manual clean step`_
|
||||
* `Removing CA certificates from iLO as manual clean step`_
|
||||
* `Firmware based UEFI iSCSI boot from volume support`_
|
||||
@ -751,6 +753,12 @@ Supported **Manual** Cleaning Operations
|
||||
``update_auth_failure_logging_threshold``:
|
||||
Updates the Authentication Failure Logging security parameter. See
|
||||
`Update Authentication Failure Logging security parameter as manual clean step`_ for user guidance on usage.
|
||||
``create_csr``:
|
||||
Creates the certificate signing request. See `Create Certificate Signing Request(CSR) as manual clean step`_
|
||||
for user guidance on usage.
|
||||
``add_https_certificate``:
|
||||
Adds the signed HTTPS certificate to the iLO. See `Add HTTPS Certificate as manual clean step`_ for user
|
||||
guidance on usage.
|
||||
|
||||
* iLO with firmware version 1.5 is minimally required to support all the
|
||||
operations.
|
||||
@ -1648,6 +1656,54 @@ Both the arguments ``logging_threshold`` and ``ignore`` are optional. The accept
|
||||
value be False. If user passes the value of logging_threshold as 0, the Authentication Failure Logging security
|
||||
parameter will be disabled.
|
||||
|
||||
Create Certificate Signing Request(CSR) as manual clean step
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
iLO driver can invoke ``create_csr`` request as a manual clean step. This step is only supported for iLO5 based hardware.
|
||||
|
||||
An example of a manual clean step with ``create_csr`` as the only clean step could be::
|
||||
|
||||
"clean_steps": [{
|
||||
"interface": "management",
|
||||
"step": "create_csr",
|
||||
"args": {
|
||||
"csr_params": {
|
||||
"City": "Bengaluru",
|
||||
"CommonName": "1.1.1.1",
|
||||
"Country": "India",
|
||||
"OrgName": "HPE",
|
||||
"State": "Karnataka"
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
The ``[ilo]cert_path`` option in ``ironic.conf`` is used as the directory path for
|
||||
creating the CSR, which defaults to ``/var/lib/ironic/ilo``. The CSR is created in the directory location
|
||||
given in ``[ilo]cert_path`` in ``node_uuid`` directory as <node_uuid>.csr.
|
||||
|
||||
|
||||
Add HTTPS Certificate as manual clean step
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
iLO driver can invoke ``add_https_certificate`` request as a manual clean step. This step is only supported for
|
||||
iLO5 based hardware.
|
||||
|
||||
An example of a manual clean step with ``add_https_certificate`` as the only clean step could be::
|
||||
|
||||
"clean_steps": [{
|
||||
"interface": "management",
|
||||
"step": "add_https_certificate",
|
||||
"args": {
|
||||
"cert_file": "/test1/iLO.crt"
|
||||
}
|
||||
}]
|
||||
|
||||
Argument ``cert_file`` is mandatory. The ``cert_file`` takes the path or url of the certificate file.
|
||||
The url schemes supported are: ``file``, ``http`` and ``https``.
|
||||
The CSR generated in step ``create_csr`` needs to be signed by a valid CA and the resultant HTTPS certificate should
|
||||
be provided in ``cert_file``. It copies the ``cert_file`` to ``[ilo]cert_path`` under ``node.uuid`` as <node_uuid>.crt
|
||||
before adding it to iLO.
|
||||
|
||||
RAID Support
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
# python projects they should package as optional dependencies for Ironic.
|
||||
|
||||
# These are available on pypi
|
||||
proliantutils>=2.13.0
|
||||
proliantutils>=2.14.0
|
||||
pysnmp>=4.3.0,<5.0.0
|
||||
python-scciclient>=0.12.2
|
||||
python-dracclient>=5.1.0,<9.0.0
|
||||
|
@ -120,6 +120,11 @@ opts = [
|
||||
'/proc/cmdline. Mind severe cmdline size limit! Can be '
|
||||
'overridden by `instance_info/kernel_append_params` '
|
||||
'property.')),
|
||||
cfg.StrOpt('cert_path',
|
||||
default='/var/lib/ironic/ilo/',
|
||||
help=_('On the ironic-conductor node, directory where ilo '
|
||||
'driver stores the CSR and the cert.')),
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
@ -31,6 +31,7 @@ from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common.glance_service import service_utils
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common import image_service
|
||||
from ironic.common import images
|
||||
from ironic.common import swift
|
||||
from ironic.common import utils
|
||||
@ -1126,3 +1127,23 @@ def setup_uefi_https(task, iso, persistent=False):
|
||||
except ilo_error.IloError as ilo_exception:
|
||||
raise exception.IloOperationError(operation=operation,
|
||||
error=ilo_exception)
|
||||
|
||||
|
||||
def download(target_file, file_url):
|
||||
"""Downloads file based on the scheme.
|
||||
|
||||
It downloads the file (url) to given location.
|
||||
The supported url schemes are file, http, and https.
|
||||
:param target_file: target file for copying the downloaded file.
|
||||
:param file_url: source file url from where file needs to be downloaded.
|
||||
:raises: ImageDownloadFailed, on failure to download the file.
|
||||
"""
|
||||
parsed_url = urlparse.urlparse(file_url)
|
||||
if parsed_url.scheme == "file":
|
||||
src_file = parsed_url.path
|
||||
with open(target_file, 'wb') as fd:
|
||||
image_service.FileImageService().download(src_file, fd)
|
||||
elif parsed_url.scheme in ('http', 'https'):
|
||||
src_file = parsed_url.geturl()
|
||||
with open(target_file, 'wb') as fd:
|
||||
image_service.HttpImageService().download(src_file, fd)
|
||||
|
@ -14,7 +14,8 @@
|
||||
"""
|
||||
iLO Management Interface
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from urllib import parse as urlparse
|
||||
|
||||
from ironic_lib import metrics_utils
|
||||
@ -79,6 +80,27 @@ _RESET_ILO_CREDENTIALS_ARGSINFO = {
|
||||
}
|
||||
}
|
||||
|
||||
_CREATE_CSR_ARGSINFO = {
|
||||
'csr_params': {
|
||||
'description': (
|
||||
"This arguments represents the information needed "
|
||||
"to create the CSR certificate. The keys to be provided are "
|
||||
"City, CommonName, OrgName, State."
|
||||
),
|
||||
'required': True
|
||||
}
|
||||
}
|
||||
|
||||
_ADD_HTTPS_CERT_ARGSINFO = {
|
||||
'cert_file': {
|
||||
'description': (
|
||||
"This argument represents the path to the signed HTTPS "
|
||||
"certificate which will be added to the iLO."
|
||||
),
|
||||
'required': True
|
||||
}
|
||||
}
|
||||
|
||||
_SECURITY_PARAMETER_UPDATE_ARGSINFO = {
|
||||
'security_parameters': {
|
||||
'description': (
|
||||
@ -574,6 +596,61 @@ class IloManagement(base.ManagementInterface):
|
||||
"parameter for node %(node)s is updated",
|
||||
{'node': node.uuid})
|
||||
|
||||
@METRICS.timer('IloManagement.create_csr')
|
||||
@base.clean_step(priority=0, abortable=False,
|
||||
argsinfo=_CREATE_CSR_ARGSINFO)
|
||||
def create_csr(self, task, **kwargs):
|
||||
"""Creates the CSR.
|
||||
|
||||
:param task: a TaskManager object.
|
||||
"""
|
||||
node = task.node
|
||||
csr_params = kwargs.get('csr_params')
|
||||
csr_path = CONF.ilo.cert_path
|
||||
path = os.path.join(csr_path, task.node.uuid)
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path, 0o755)
|
||||
|
||||
LOG.debug("Creating CSR for node %(node)s ..",
|
||||
{'node': node.uuid})
|
||||
_execute_ilo_step(node, 'create_csr', path, csr_params)
|
||||
LOG.info("Creation of CSR for node %(node)s is "
|
||||
"completed.", {'node': node.uuid})
|
||||
|
||||
@METRICS.timer('IloManagement.add_https_certificate')
|
||||
@base.clean_step(priority=0, abortable=False,
|
||||
argsinfo=_ADD_HTTPS_CERT_ARGSINFO)
|
||||
def add_https_certificate(self, task, **kwargs):
|
||||
"""Adds the signed HTTPS certificate to the iLO.
|
||||
|
||||
:param task: a TaskManager object.
|
||||
"""
|
||||
node = task.node
|
||||
csr_path = CONF.ilo.cert_path
|
||||
path = os.path.join(csr_path, task.node.uuid)
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path, 0o755)
|
||||
cert_file_name = node.uuid + ".crt"
|
||||
cert_file_path = os.path.join(path, cert_file_name)
|
||||
cert_file = kwargs.get('cert_file')
|
||||
url_scheme = urlparse.urlparse(cert_file).scheme
|
||||
if url_scheme == '':
|
||||
shutil.copy(cert_file, cert_file_path)
|
||||
elif url_scheme in ('http', 'https', 'file'):
|
||||
ilo_common.download(cert_file_path, cert_file)
|
||||
else:
|
||||
msg = (_("The url scheme %(scheme)s not supported with clean step "
|
||||
"%(step)s") % {'scheme': url_scheme,
|
||||
'step': 'add_https_certificate'})
|
||||
raise exception.IloOperationNotSupported(operation='clean step',
|
||||
error=msg)
|
||||
|
||||
LOG.debug("Adding the signed HTTPS certificate to the "
|
||||
"node %(node)s ..", {'node': node.uuid})
|
||||
_execute_ilo_step(node, 'add_https_certificate', cert_file_path)
|
||||
LOG.info("Adding of HTTPS certificate to the node %(node)s "
|
||||
"is completed.", {'node': node.uuid})
|
||||
|
||||
@METRICS.timer('IloManagement.update_firmware')
|
||||
@base.deploy_step(priority=0, argsinfo=_FIRMWARE_UPDATE_ARGSINFO)
|
||||
@base.clean_step(priority=0, abortable=False,
|
||||
|
@ -30,6 +30,7 @@ from oslo_utils import uuidutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common import image_service
|
||||
from ironic.common import images
|
||||
from ironic.common import swift
|
||||
from ironic.conductor import task_manager
|
||||
@ -1504,3 +1505,37 @@ class IloCommonMethodsTestCase(BaseIloTest):
|
||||
self.assertRaises(exception.IloOperationError,
|
||||
ilo_common.setup_uefi_https,
|
||||
task, iso, True)
|
||||
|
||||
@mock.patch.object(image_service, 'FileImageService', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(image_service, 'HttpImageService', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(builtins, 'open', autospec=True)
|
||||
def test_download_file_url(self, open_mock, http_mock, file_mock):
|
||||
url = "file:///test1/iLO.crt"
|
||||
target_file = "/a/b/c"
|
||||
fd_mock = mock.MagicMock(spec=io.BytesIO)
|
||||
open_mock.return_value = fd_mock
|
||||
fd_mock.__enter__.return_value = fd_mock
|
||||
ilo_common.download(target_file, url)
|
||||
open_mock.assert_called_once_with(target_file, 'wb')
|
||||
http_mock.assert_not_called()
|
||||
file_mock.return_value.download.assert_called_once_with(
|
||||
"/test1/iLO.crt", fd_mock)
|
||||
|
||||
@mock.patch.object(image_service, 'FileImageService', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(image_service, 'HttpImageService', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(builtins, 'open', autospec=True)
|
||||
def test_download_http_url(self, open_mock, http_mock, file_mock):
|
||||
url = "http://1.1.1.1/iLO.crt"
|
||||
target_file = "/a/b/c"
|
||||
fd_mock = mock.MagicMock(spec=io.BytesIO)
|
||||
open_mock.return_value = fd_mock
|
||||
fd_mock.__enter__.return_value = fd_mock
|
||||
ilo_common.download(target_file, url)
|
||||
http_mock.return_value.download.assert_called_once_with(
|
||||
"http://1.1.1.1/iLO.crt", fd_mock)
|
||||
file_mock.assert_not_called()
|
||||
open_mock.assert_called_once_with(target_file, 'wb')
|
||||
|
@ -14,9 +14,12 @@
|
||||
|
||||
"""Test class for Management Interface used by iLO modules."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from unittest import mock
|
||||
|
||||
import ddt
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import importutils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
@ -42,6 +45,8 @@ ilo_error = importutils.try_import('proliantutils.exception')
|
||||
|
||||
INFO_DICT = db_utils.get_test_ilo_info()
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class IloManagementTestCase(test_common.BaseIloTest):
|
||||
@ -424,6 +429,116 @@ class IloManagementTestCase(test_common.BaseIloTest):
|
||||
step_mock.assert_called_once_with(
|
||||
task.node, 'update_authentication_failure_logging', '1', False)
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_step',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(os, 'makedirs', spec_set=True, autospec=True)
|
||||
def test_create_csr(self, os_mock, step_mock):
|
||||
csr_params_args = {
|
||||
"City": "Bangalore",
|
||||
"CommonName": "1.1.1.1",
|
||||
"Country": "ABC",
|
||||
"OrgName": "DEF",
|
||||
"State": "IJK"
|
||||
}
|
||||
csr_args = {
|
||||
"csr_params": csr_params_args}
|
||||
CONF.ilo.cert_path = "/var/lib/ironic/ilo"
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.management.create_csr(task, **csr_args)
|
||||
cert_path = os.path.join(CONF.ilo.cert_path, self.node.uuid)
|
||||
step_mock.assert_called_once_with(task.node, 'create_csr',
|
||||
cert_path, csr_params_args)
|
||||
os_mock.assert_called_once_with(cert_path, 0o755)
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_step',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(os, 'makedirs', spec_set=True, autospec=True)
|
||||
@mock.patch.object(shutil, 'copy', spec_set=True, autospec=True)
|
||||
def test_add_https_certificate(self, shutil_mock, os_mock,
|
||||
step_mock):
|
||||
CONF.ilo.cert_path = "/var/lib/ironic/ilo"
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
cert_file_args = {'cert_file': '/test1/cert'}
|
||||
task.driver.management.add_https_certificate(
|
||||
task, **cert_file_args)
|
||||
cert_path = os.path.join(CONF.ilo.cert_path, self.node.uuid)
|
||||
cert_path_name = os.path.join(cert_path, self.node.uuid)
|
||||
filename = cert_path_name + ".crt"
|
||||
step_mock.assert_called_once_with(
|
||||
task.node, 'add_https_certificate', filename)
|
||||
os_mock.assert_called_once_with(cert_path, 0o755)
|
||||
shutil_mock.assert_called_once_with('/test1/cert', filename)
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_step',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(os, 'makedirs', spec_set=True, autospec=True)
|
||||
@mock.patch.object(shutil, 'copy', spec_set=True, autospec=True)
|
||||
@mock.patch.object(ilo_common, 'download', spec_set=True, autospec=True)
|
||||
def test_add_https_certificate_fileurl(self, download_mock, shutil_mock,
|
||||
os_mock, step_mock):
|
||||
CONF.ilo.cert_path = "/var/lib/ironic/ilo"
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
cert_file_args = {'cert_file': 'file:///test1/cert'}
|
||||
task.driver.management.add_https_certificate(
|
||||
task, **cert_file_args)
|
||||
cert_path = os.path.join(CONF.ilo.cert_path, self.node.uuid)
|
||||
cert_path_name = os.path.join(cert_path, self.node.uuid)
|
||||
fname = cert_path_name + ".crt"
|
||||
step_mock.assert_called_once_with(
|
||||
task.node, 'add_https_certificate', fname)
|
||||
os_mock.assert_called_once_with(cert_path, 0o755)
|
||||
shutil_mock.assert_not_called()
|
||||
download_mock.assert_called_once_with(fname, 'file:///test1/cert')
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_step',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(os, 'makedirs', spec_set=True, autospec=True)
|
||||
@mock.patch.object(shutil, 'copy', spec_set=True, autospec=True)
|
||||
@mock.patch.object(ilo_common, 'download', spec_set=True, autospec=True)
|
||||
def test_add_https_certificate_httpurl(self, download_mock, shutil_mock,
|
||||
os_mock, step_mock):
|
||||
CONF.ilo.cert_path = "/var/lib/ironic/ilo"
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
cert_file_args = {'cert_file': 'http://1.1.1.1/cert'}
|
||||
task.driver.management.add_https_certificate(
|
||||
task, **cert_file_args)
|
||||
cert_path = os.path.join(CONF.ilo.cert_path, self.node.uuid)
|
||||
cert_path_name = os.path.join(cert_path, self.node.uuid)
|
||||
fname = cert_path_name + ".crt"
|
||||
step_mock.assert_called_once_with(
|
||||
task.node, 'add_https_certificate', fname)
|
||||
os_mock.assert_called_once_with(cert_path, 0o755)
|
||||
shutil_mock.assert_not_called()
|
||||
download_mock.assert_called_once_with(fname, 'http://1.1.1.1/cert')
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_step',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(os, 'makedirs', spec_set=True, autospec=True)
|
||||
@mock.patch.object(shutil, 'copy', spec_set=True, autospec=True)
|
||||
@mock.patch.object(ilo_common, 'download', spec_set=True, autospec=True)
|
||||
def test_add_https_certificate_url_exception(self, download_mock,
|
||||
shutil_mock, os_mock,
|
||||
step_mock):
|
||||
CONF.ilo.cert_path = "/var/lib/ironic/ilo"
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
cert_file_args = {'cert_file': 'swift://1.1.1.1/cert'}
|
||||
self.assertRaises(exception.IloOperationNotSupported,
|
||||
task.driver.management.add_https_certificate,
|
||||
task,
|
||||
**cert_file_args)
|
||||
|
||||
cert_path = os.path.join(CONF.ilo.cert_path, self.node.uuid)
|
||||
step_mock.assert_not_called()
|
||||
os_mock.assert_called_once_with(cert_path, 0o755)
|
||||
shutil_mock.assert_not_called()
|
||||
download_mock.assert_not_called()
|
||||
|
||||
@mock.patch.object(deploy_utils, 'build_agent_options',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(ilo_boot.IloVirtualMediaBoot, 'clean_up_ramdisk',
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds new clean steps ``create_csr`` and ``add_https_certificate``
|
||||
to ``ilo`` and ``ilo5`` hardware types which allows users to
|
||||
create Certificate Signing Request(CSR) and adds signed HTTPS
|
||||
certificate to the iLO.
|
Loading…
Reference in New Issue
Block a user