Add Cleaning Operations for iLO drivers
This patch adds support for the Ilo drivers to be able to perform cleaning. The cleaning operations supported with this patch are: 1. reset_ilo 2. reset_ilo_credential 3. reset_bios_to_default 4. reset_secure_boot_keys 5. clear_secure_boot_keys Implements: blueprint ilo-cleaning-support Change-Id: Ic63c668538dfb185a5addd5c435541322caa00d1
This commit is contained in:
parent
b3602906b9
commit
c59be0bb00
|
@ -835,6 +835,33 @@
|
|||
#swift_object_expiry_timeout=900
|
||||
|
||||
|
||||
#
|
||||
# Options defined in ironic.drivers.modules.ilo.management
|
||||
#
|
||||
|
||||
# Priority for reset_ilo clean step. (integer value)
|
||||
#clean_priority_reset_ilo=1
|
||||
|
||||
# Priority for reset_bios_to_default clean step. (integer
|
||||
# value)
|
||||
#clean_priority_reset_bios_to_default=10
|
||||
|
||||
# Priority for reset_secure_boot_keys clean step. This step
|
||||
# will reset the secure boot keys to manufacturing defaults.
|
||||
# (integer value)
|
||||
#clean_priority_reset_secure_boot_keys_to_default=20
|
||||
|
||||
# Priority for clear_secure_boot_keys clean step. This step is
|
||||
# not enabled by default. It can be enabled to to clear all
|
||||
# secure boot keys enrolled with iLO. (integer value)
|
||||
#clean_priority_clear_secure_boot_keys=0
|
||||
|
||||
# Priority for reset_ilo_credential clean step. This step
|
||||
# requires "ilo_change_password" parameter to be updated in
|
||||
# nodes's driver_info with the new password. (integer value)
|
||||
#clean_priority_reset_ilo_credential=30
|
||||
|
||||
|
||||
#
|
||||
# Options defined in ironic.drivers.modules.ilo.power
|
||||
#
|
||||
|
|
|
@ -555,3 +555,7 @@ class VirtualBoxOperationFailed(IronicException):
|
|||
|
||||
class HardwareInspectionFailure(IronicException):
|
||||
message = _("Failed to inspect hardware. Reason: %(error)s")
|
||||
|
||||
|
||||
class NodeCleaningFailure(IronicException):
|
||||
message = _("Failed to clean node %(node)s: %(reason)s")
|
||||
|
|
|
@ -69,7 +69,7 @@ REQUIRED_PROPERTIES = {
|
|||
}
|
||||
OPTIONAL_PROPERTIES = {
|
||||
'client_port': _("port to be used for iLO operations. Optional."),
|
||||
'client_timeout': _("timeout (in seconds) for iLO operations. Optional.")
|
||||
'client_timeout': _("timeout (in seconds) for iLO operations. Optional."),
|
||||
}
|
||||
CONSOLE_PROPERTIES = {
|
||||
'console_port': _("node's UDP port to connect to. Only required for "
|
||||
|
@ -84,6 +84,10 @@ INSPECT_PROPERTIES = {
|
|||
"inspect_ports = 'none'. "
|
||||
"Required only for inspection.")
|
||||
}
|
||||
CLEAN_PROPERTIES = {
|
||||
'ilo_change_password': _("new password for iLO. Required if the clean "
|
||||
"step 'reset_ilo_credential' is enabled.")
|
||||
}
|
||||
|
||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
|
||||
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
|
||||
|
|
|
@ -15,11 +15,14 @@
|
|||
iLO Management Interface
|
||||
"""
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LI
|
||||
from ironic.common.i18n import _LW
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules.ilo import common as ilo_common
|
||||
|
@ -37,11 +40,74 @@ BOOT_DEVICE_MAPPING_TO_ILO = {boot_devices.PXE: 'NETWORK',
|
|||
BOOT_DEVICE_ILO_TO_GENERIC = {v: k
|
||||
for k, v in BOOT_DEVICE_MAPPING_TO_ILO.items()}
|
||||
|
||||
MANAGEMENT_PROPERTIES = ilo_common.REQUIRED_PROPERTIES.copy()
|
||||
MANAGEMENT_PROPERTIES.update(ilo_common.CLEAN_PROPERTIES)
|
||||
|
||||
clean_step_opts = [
|
||||
cfg.IntOpt('clean_priority_reset_ilo',
|
||||
default=1,
|
||||
help='Priority for reset_ilo clean step.'),
|
||||
cfg.IntOpt('clean_priority_reset_bios_to_default',
|
||||
default=10,
|
||||
help='Priority for reset_bios_to_default clean step.'),
|
||||
cfg.IntOpt('clean_priority_reset_secure_boot_keys_to_default',
|
||||
default=20,
|
||||
help='Priority for reset_secure_boot_keys clean step. This '
|
||||
'step will reset the secure boot keys to manufacturing '
|
||||
' defaults.'),
|
||||
cfg.IntOpt('clean_priority_clear_secure_boot_keys',
|
||||
default=0,
|
||||
help='Priority for clear_secure_boot_keys clean step. This '
|
||||
'step is not enabled by default. It can be enabled to '
|
||||
'to clear all secure boot keys enrolled with iLO.'),
|
||||
cfg.IntOpt('clean_priority_reset_ilo_credential',
|
||||
default=30,
|
||||
help='Priority for reset_ilo_credential clean step. This step '
|
||||
'requires "ilo_change_password" parameter to be updated '
|
||||
'in nodes\'s driver_info with the new password.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(clean_step_opts, group='ilo')
|
||||
|
||||
|
||||
def _execute_ilo_clean_step(node, step, *args, **kwargs):
|
||||
"""Executes a particular clean step.
|
||||
|
||||
:param node: an Ironic node object.
|
||||
:param step: a clean step to be executed.
|
||||
:param args: The args to be passed to the clean step.
|
||||
:param kwargs: The kwargs to be passed to the clean step.
|
||||
:raises: NodeCleaningFailure, on failure to execute step.
|
||||
"""
|
||||
ilo_object = ilo_common.get_ilo_object(node)
|
||||
|
||||
try:
|
||||
clean_step = getattr(ilo_object, step)
|
||||
except AttributeError:
|
||||
# The specified clean step is not present in the proliantutils
|
||||
# package. Raise exception to update the proliantutils package
|
||||
# to newer version.
|
||||
raise exception.NodeCleaningFailure(_("Clean step '%s' not "
|
||||
"found. 'proliantutils' package needs to be updated.") % step)
|
||||
try:
|
||||
clean_step(*args, **kwargs)
|
||||
except ilo_error.IloCommandNotSupportedError:
|
||||
# This clean step is not supported on Gen8 and below servers.
|
||||
# Log the failure and continue with cleaning.
|
||||
LOG.warn(_LW("'%(step)s' clean step is not supported on node "
|
||||
"%(uuid)s. Skipping the clean step."),
|
||||
{'step': step, 'uuid': node.uuid})
|
||||
except ilo_error.IloError as ilo_exception:
|
||||
raise exception.NodeCleaningFailure(_("Clean step %(step)s failed "
|
||||
"on node %(node)s with error: %(err)s") %
|
||||
{'node': node.uuid, 'step': step, 'err': ilo_exception})
|
||||
|
||||
|
||||
class IloManagement(base.ManagementInterface):
|
||||
|
||||
def get_properties(self):
|
||||
return ilo_common.REQUIRED_PROPERTIES
|
||||
return MANAGEMENT_PROPERTIES
|
||||
|
||||
def validate(self, task):
|
||||
"""Check that 'driver_info' contains required ILO credentials.
|
||||
|
@ -162,3 +228,71 @@ class IloManagement(base.ManagementInterface):
|
|||
ilo_common.update_ipmi_properties(task)
|
||||
ipmi_management = ipmitool.IPMIManagement()
|
||||
return ipmi_management.get_sensors_data(task)
|
||||
|
||||
@base.clean_step(priority=CONF.ilo.clean_priority_reset_ilo)
|
||||
def reset_ilo(self, task):
|
||||
"""Resets the iLO.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:raises: NodeCleaningFailure, on failure to execute step.
|
||||
"""
|
||||
return _execute_ilo_clean_step(task.node, 'reset_ilo')
|
||||
|
||||
@base.clean_step(priority=CONF.ilo.clean_priority_reset_ilo_credential)
|
||||
def reset_ilo_credential(self, task):
|
||||
"""Resets the iLO password.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:raises: NodeCleaningFailure, on failure to execute step.
|
||||
"""
|
||||
info = task.node.driver_info
|
||||
password = info.pop('ilo_change_password', None)
|
||||
|
||||
if not password:
|
||||
LOG.info(_LI("Missing 'ilo_change_password' parameter in "
|
||||
"driver_info. Clean step 'reset_ilo_credential' is "
|
||||
"not performed on node %s."), task.node.uuid)
|
||||
return
|
||||
|
||||
_execute_ilo_clean_step(task.node, 'reset_ilo_credential', password)
|
||||
|
||||
info['ilo_password'] = password
|
||||
task.node.driver_info = info
|
||||
task.node.save()
|
||||
|
||||
@base.clean_step(priority=CONF.ilo.clean_priority_reset_bios_to_default)
|
||||
def reset_bios_to_default(self, task):
|
||||
"""Resets the BIOS settings to default values.
|
||||
|
||||
Resets BIOS to default settings. This operation is currently supported
|
||||
only on HP Proliant Gen9 and above servers.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:raises: NodeCleaningFailure, on failure to execute step.
|
||||
"""
|
||||
return _execute_ilo_clean_step(task.node, 'reset_bios_to_default')
|
||||
|
||||
@base.clean_step(priority=CONF.ilo.
|
||||
clean_priority_reset_secure_boot_keys_to_default)
|
||||
def reset_secure_boot_keys_to_default(self, task):
|
||||
"""Reset secure boot keys to manufacturing defaults.
|
||||
|
||||
Resets the secure boot keys to manufacturing defaults. This
|
||||
operation is supported only on HP Proliant Gen9 and above servers.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:raises: NodeCleaningFailure, on failure to execute step.
|
||||
"""
|
||||
return _execute_ilo_clean_step(task.node, 'reset_secure_boot_keys')
|
||||
|
||||
@base.clean_step(priority=CONF.ilo.clean_priority_clear_secure_boot_keys)
|
||||
def clear_secure_boot_keys(self, task):
|
||||
"""Clear all secure boot keys.
|
||||
|
||||
Clears all the secure boot keys. This operation is supported only
|
||||
on HP Proliant Gen9 and above servers.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:raises: NodeCleaningFailure, on failure to execute step.
|
||||
"""
|
||||
return _execute_ilo_clean_step(task.node, 'clear_secure_boot_keys')
|
||||
|
|
|
@ -3339,19 +3339,20 @@ class ManagerTestProperties(tests_db_base.DbTestCase):
|
|||
|
||||
def test_driver_properties_fake_ilo(self):
|
||||
expected = ['ilo_address', 'ilo_username', 'ilo_password',
|
||||
'client_port', 'client_timeout', 'inspect_ports']
|
||||
'client_port', 'client_timeout', 'inspect_ports',
|
||||
'ilo_change_password']
|
||||
self._check_driver_properties("fake_ilo", expected)
|
||||
|
||||
def test_driver_properties_ilo_iscsi(self):
|
||||
expected = ['ilo_address', 'ilo_username', 'ilo_password',
|
||||
'client_port', 'client_timeout', 'ilo_deploy_iso',
|
||||
'console_port', 'inspect_ports']
|
||||
'console_port', 'inspect_ports', 'ilo_change_password']
|
||||
self._check_driver_properties("iscsi_ilo", expected)
|
||||
|
||||
def test_driver_properties_agent_ilo(self):
|
||||
expected = ['ilo_address', 'ilo_username', 'ilo_password',
|
||||
'client_port', 'client_timeout', 'ilo_deploy_iso',
|
||||
'console_port', 'inspect_ports']
|
||||
'console_port', 'inspect_ports', 'ilo_change_password']
|
||||
self._check_driver_properties("agent_ilo", expected)
|
||||
|
||||
def test_driver_properties_fail(self):
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
"""Test class for Management Interface used by iLO modules."""
|
||||
|
||||
import mock
|
||||
|
@ -23,6 +22,7 @@ from ironic.common import boot_devices
|
|||
from ironic.common import exception
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules.ilo import common as ilo_common
|
||||
from ironic.drivers.modules.ilo import management as ilo_management
|
||||
from ironic.drivers.modules import ipmitool
|
||||
from ironic.tests.conductor import utils as mgr_utils
|
||||
from ironic.tests.db import base as db_base
|
||||
|
@ -31,7 +31,6 @@ from ironic.tests.objects import utils as obj_utils
|
|||
|
||||
ilo_error = importutils.try_import('proliantutils.exception')
|
||||
|
||||
|
||||
INFO_DICT = db_utils.get_test_ilo_info()
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
@ -47,7 +46,7 @@ class IloManagementTestCase(db_base.DbTestCase):
|
|||
def test_get_properties(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
expected = ilo_common.REQUIRED_PROPERTIES
|
||||
expected = ilo_management.MANAGEMENT_PROPERTIES
|
||||
self.assertEqual(expected, task.driver.management.
|
||||
get_properties())
|
||||
|
||||
|
@ -186,3 +185,92 @@ class IloManagementTestCase(db_base.DbTestCase):
|
|||
task.driver.management.get_sensors_data(task)
|
||||
update_ipmi_mock.assert_called_once_with(task)
|
||||
get_sensors_data_mock.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(ilo_common, 'get_ilo_object')
|
||||
def test__execute_ilo_clean_step_ok(self, get_ilo_object_mock):
|
||||
ilo_mock = get_ilo_object_mock.return_value
|
||||
clean_step_mock = getattr(ilo_mock, 'fake-step')
|
||||
ilo_management._execute_ilo_clean_step(self.node,
|
||||
'fake-step', 'args', kwarg='kwarg')
|
||||
clean_step_mock.assert_called_once_with('args', kwarg='kwarg')
|
||||
|
||||
@mock.patch.object(ilo_management, 'LOG')
|
||||
@mock.patch.object(ilo_common, 'get_ilo_object')
|
||||
def test__execute_ilo_clean_step_not_supported(self, get_ilo_object_mock,
|
||||
log_mock):
|
||||
ilo_mock = get_ilo_object_mock.return_value
|
||||
exc = ilo_error.IloCommandNotSupportedError("error")
|
||||
clean_step_mock = getattr(ilo_mock, 'fake-step')
|
||||
clean_step_mock.side_effect = exc
|
||||
ilo_management._execute_ilo_clean_step(self.node,
|
||||
'fake-step', 'args', kwarg='kwarg')
|
||||
clean_step_mock.assert_called_once_with('args', kwarg='kwarg')
|
||||
self.assertTrue(log_mock.warn.called)
|
||||
|
||||
@mock.patch.object(ilo_common, 'get_ilo_object')
|
||||
def test__execute_ilo_clean_step_fail(self, get_ilo_object_mock):
|
||||
ilo_mock = get_ilo_object_mock.return_value
|
||||
exc = ilo_error.IloError("error")
|
||||
clean_step_mock = getattr(ilo_mock, 'fake-step')
|
||||
clean_step_mock.side_effect = exc
|
||||
self.assertRaises(exception.NodeCleaningFailure,
|
||||
ilo_management._execute_ilo_clean_step,
|
||||
self.node, 'fake-step', 'args', kwarg='kwarg')
|
||||
clean_step_mock.assert_called_once_with('args', kwarg='kwarg')
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_clean_step')
|
||||
def test_reset_ilo(self, clean_step_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.management.reset_ilo(task)
|
||||
clean_step_mock.assert_called_once_with(task.node, 'reset_ilo')
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_clean_step')
|
||||
def test_reset_ilo_credential_ok(self, clean_step_mock):
|
||||
info = self.node.driver_info
|
||||
info['ilo_change_password'] = "fake-password"
|
||||
self.node.driver_info = info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.management.reset_ilo_credential(task)
|
||||
clean_step_mock.assert_called_once_with(task.node,
|
||||
'reset_ilo_credential', 'fake-password')
|
||||
self.assertIsNone(task.node.driver_info.get(
|
||||
'ilo_change_password'))
|
||||
self.assertEqual(task.node.driver_info['ilo_password'],
|
||||
'fake-password')
|
||||
|
||||
@mock.patch.object(ilo_management, 'LOG')
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_clean_step')
|
||||
def test_reset_ilo_credential_no_password(self, clean_step_mock,
|
||||
log_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.management.reset_ilo_credential(task)
|
||||
self.assertFalse(clean_step_mock.called)
|
||||
self.assertTrue(log_mock.info.called)
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_clean_step')
|
||||
def test_reset_bios_to_default(self, clean_step_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.management.reset_bios_to_default(task)
|
||||
clean_step_mock.assert_called_once_with(task.node,
|
||||
'reset_bios_to_default')
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_clean_step')
|
||||
def test_reset_secure_boot_keys_to_default(self, clean_step_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.management.reset_secure_boot_keys_to_default(task)
|
||||
clean_step_mock.assert_called_once_with(task.node,
|
||||
'reset_secure_boot_keys')
|
||||
|
||||
@mock.patch.object(ilo_management, '_execute_ilo_clean_step')
|
||||
def test_clear_secure_boot_keys(self, clean_step_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.management.clear_secure_boot_keys(task)
|
||||
clean_step_mock.assert_called_once_with(task.node,
|
||||
'clear_secure_boot_keys')
|
||||
|
|
|
@ -155,11 +155,10 @@ class IloPowerTestCase(db_base.DbTestCase):
|
|||
driver_info=driver_info)
|
||||
|
||||
def test_get_properties(self):
|
||||
expected = ilo_common.COMMON_PROPERTIES.copy()
|
||||
expected.update(ilo_common.INSPECT_PROPERTIES)
|
||||
expected = ilo_common.COMMON_PROPERTIES
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
self.assertEqual(expected, task.driver.get_properties())
|
||||
self.assertEqual(expected, task.driver.power.get_properties())
|
||||
|
||||
@mock.patch.object(ilo_common, 'parse_driver_info')
|
||||
def test_validate(self, mock_drvinfo):
|
||||
|
|
Loading…
Reference in New Issue