Merge "Add deploy steps for iLO Management interface"

This commit is contained in:
Zuul 2019-09-21 09:33:52 +00:00 committed by Gerrit Code Review
commit f2086372ec
3 changed files with 571 additions and 233 deletions

View File

@ -57,40 +57,114 @@ BOOT_DEVICE_ILO_TO_GENERIC = {
MANAGEMENT_PROPERTIES = ilo_common.REQUIRED_PROPERTIES.copy()
MANAGEMENT_PROPERTIES.update(ilo_common.CLEAN_PROPERTIES)
_ACTIVATE_ILO_LICENSE_ARGSINFO = {
'ilo_license_key': {
'description': (
'The HPE iLO Advanced license key to activate enterprise '
'features.'
),
'required': True
}
}
def _execute_ilo_clean_step(node, step, *args, **kwargs):
"""Executes a particular clean step.
_RESET_ILO_CREDENTIALS_ARGSINFO = {
'ilo_password': {
'description': (
'Password string for iLO user with administrative privileges '
'being set in the driver_info property "ilo_username".'
),
'required': True
}
}
_FIRMWARE_UPDATE_ARGSINFO = {
'firmware_update_mode': {
'description': (
"This argument indicates the mode (or mechanism) of firmware "
"update procedure. Supported value is 'ilo'."
),
'required': True
},
'firmware_images': {
'description': (
"This argument represents the ordered list of JSON "
"dictionaries of firmware images. Each firmware image "
"dictionary consists of three mandatory fields, namely 'url', "
"'checksum' and 'component'. These fields represent firmware "
"image location URL, md5 checksum of image file and firmware "
"component type respectively. The supported firmware URL "
"schemes are 'file', 'http', 'https' and 'swift'. The "
"supported values for firmware component are 'ilo', 'cpld', "
"'power_pic', 'bios' and 'chassis'. The firmware images will "
"be applied (in the order given) one by one on the baremetal "
"server. For more information, see "
"https://docs.openstack.org/ironic/latest/admin/drivers/ilo.html#initiating-firmware-update-as-manual-clean-step" # noqa
),
'required': True
}
}
_FIRMWARE_UPDATE_SUM_ARGSINFO = {
'url': {
'description': (
"The image location for SPP (Service Pack for Proliant) ISO."
),
'required': True
},
'checksum': {
'description': (
"The md5 checksum of the SPP image file."
),
'required': True
},
'components': {
'description': (
"The list of firmware component filenames. If not specified, "
"SUM updates all the firmware components."
),
'required': False
}
}
def _execute_ilo_step(node, step, *args, **kwargs):
"""Executes a particular deploy or 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.
:param step: a step to be executed.
:param args: The args to be passed to the step.
:param kwargs: The kwargs to be passed to the step.
:raises: NodeCleaningFailure, on failure to execute the clean step.
:raises: InstanceDeployFailure, on failure to execute the deploy step.
"""
ilo_object = ilo_common.get_ilo_object(node)
try:
clean_step = getattr(ilo_object, step)
step_method = getattr(ilo_object, step)
except AttributeError:
# The specified clean step is not present in the proliantutils
# The specified clean/deploy 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)
msg = (_("Step '%s' not found. 'proliantutils' package needs to be "
"updated.") % step)
if node.clean_step:
raise exception.NodeCleaningFailure(msg)
raise exception.InstanceDeployFailure(msg)
try:
clean_step(*args, **kwargs)
step_method(*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.warning("'%(step)s' clean step is not supported on node "
"%(uuid)s. Skipping the clean step.",
# This step is not supported on Gen8 and below servers.
# Log the failure and continue with cleaning or deployment.
LOG.warning("'%(step)s' step is not supported on node "
"%(uuid)s. Skipping the 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})
msg = (_("Step %(step)s failed on node %(node)s with "
"error: %(err)s") %
{'node': node.uuid, 'step': step, 'err': ilo_exception})
if node.clean_step:
raise exception.NodeCleaningFailure(msg)
raise exception.InstanceDeployFailure(msg)
def _should_collect_logs(command):
@ -232,39 +306,54 @@ class IloManagement(base.ManagementInterface):
return ipmi_management.get_sensors_data(task)
@METRICS.timer('IloManagement.reset_ilo')
@base.deploy_step(priority=0)
@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.
:raises: NodeCleaningFailure, on failure to execute of clean step.
:raises: InstanceDeployFailure, on failure to execute of deploy step.
"""
return _execute_ilo_clean_step(task.node, 'reset_ilo')
node = task.node
_execute_ilo_step(node, 'reset_ilo')
# Reset iLO ejects virtual media
# Re-create the environment for agent boot, if required
task.driver.boot.clean_up_ramdisk(task)
deploy_opts = deploy_utils.build_agent_options(node)
task.driver.boot.prepare_ramdisk(task, deploy_opts)
@METRICS.timer('IloManagement.reset_ilo_credential')
@base.deploy_step(priority=0, argsinfo=_RESET_ILO_CREDENTIALS_ARGSINFO)
@base.clean_step(priority=CONF.ilo.clean_priority_reset_ilo_credential)
def reset_ilo_credential(self, task):
def reset_ilo_credential(self, task, change_password=None):
"""Resets the iLO password.
:param task: a task from TaskManager.
:raises: NodeCleaningFailure, on failure to execute step.
:param change_password: Value for password to update on iLO.
:raises: NodeCleaningFailure, on failure to execute of clean step.
:raises: InstanceDeployFailure, on failure to execute of deploy step.
"""
info = task.node.driver_info
password = info.pop('ilo_change_password', None)
password = change_password
if not password:
password = info.pop('ilo_change_password', None)
if not password:
LOG.info("Missing 'ilo_change_password' parameter in "
"driver_info. Clean step 'reset_ilo_credential' is "
"driver_info. Step 'reset_ilo_credential' is "
"not performed on node %s.", task.node.uuid)
return
_execute_ilo_clean_step(task.node, 'reset_ilo_credential', password)
_execute_ilo_step(task.node, 'reset_ilo_credential', password)
info['ilo_password'] = password
task.node.driver_info = info
task.node.save()
@METRICS.timer('IloManagement.reset_bios_to_default')
@base.deploy_step(priority=0)
@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.
@ -273,11 +362,13 @@ class IloManagement(base.ManagementInterface):
only on HP Proliant Gen9 and above servers.
:param task: a task from TaskManager.
:raises: NodeCleaningFailure, on failure to execute step.
:raises: NodeCleaningFailure, on failure to execute of clean step.
:raises: InstanceDeployFailure, on failure to execute of deploy step.
"""
return _execute_ilo_clean_step(task.node, 'reset_bios_to_default')
return _execute_ilo_step(task.node, 'reset_bios_to_default')
@METRICS.timer('IloManagement.reset_secure_boot_keys_to_default')
@base.deploy_step(priority=0)
@base.clean_step(priority=CONF.ilo.
clean_priority_reset_secure_boot_keys_to_default)
def reset_secure_boot_keys_to_default(self, task):
@ -287,11 +378,13 @@ class IloManagement(base.ManagementInterface):
operation is supported only on HP Proliant Gen9 and above servers.
:param task: a task from TaskManager.
:raises: NodeCleaningFailure, on failure to execute step.
:raises: NodeCleaningFailure, on failure to execute of clean step.
:raises: InstanceDeployFailure, on failure to execute of deploy step.
"""
return _execute_ilo_clean_step(task.node, 'reset_secure_boot_keys')
return _execute_ilo_step(task.node, 'reset_secure_boot_keys')
@METRICS.timer('IloManagement.clear_secure_boot_keys')
@base.deploy_step(priority=0)
@base.clean_step(priority=CONF.ilo.clean_priority_clear_secure_boot_keys)
def clear_secure_boot_keys(self, task):
"""Clear all secure boot keys.
@ -300,9 +393,10 @@ class IloManagement(base.ManagementInterface):
on HP Proliant Gen9 and above servers.
:param task: a task from TaskManager.
:raises: NodeCleaningFailure, on failure to execute step.
:raises: NodeCleaningFailure, on failure to execute of clean step.
:raises: InstanceDeployFailure, on failure to execute of deploy step.
"""
return _execute_ilo_clean_step(task.node, 'clear_secure_boot_keys')
return _execute_ilo_step(task.node, 'clear_secure_boot_keys')
@METRICS.timer('IloManagement.activate_license')
@base.clean_step(priority=0, abortable=False, argsinfo={
@ -319,7 +413,7 @@ class IloManagement(base.ManagementInterface):
:param task: a TaskManager object.
:raises: InvalidParameterValue, if any of the arguments are invalid.
:raises: NodeCleaningFailure, on failure to execute clean step.
:raises: NodeCleaningFailure, on failure to execute of clean step.
"""
ilo_license_key = kwargs.get('ilo_license_key')
node = task.node
@ -334,36 +428,13 @@ class IloManagement(base.ManagementInterface):
LOG.debug("Activating iLO license for node %(node)s ...",
{'node': node.uuid})
_execute_ilo_clean_step(node, 'activate_license', ilo_license_key)
_execute_ilo_step(node, 'activate_license', ilo_license_key)
LOG.info("iLO license activated for node %s.", node.uuid)
@METRICS.timer('IloManagement.update_firmware')
@base.clean_step(priority=0, abortable=False, argsinfo={
'firmware_update_mode': {
'description': (
"This argument indicates the mode (or mechanism) of firmware "
"update procedure. Supported value is 'ilo'."
),
'required': True
},
'firmware_images': {
'description': (
"This argument represents the ordered list of JSON "
"dictionaries of firmware images. Each firmware image "
"dictionary consists of three mandatory fields, namely 'url', "
"'checksum' and 'component'. These fields represent firmware "
"image location URL, md5 checksum of image file and firmware "
"component type respectively. The supported firmware URL "
"schemes are 'file', 'http', 'https' and 'swift'. The "
"supported values for firmware component are 'ilo', 'cpld', "
"'power_pic', 'bios' and 'chassis'. The firmware images will "
"be applied (in the order given) one by one on the baremetal "
"server. For more information, see "
"https://docs.openstack.org/ironic/latest/admin/drivers/ilo.html#initiating-firmware-update-as-manual-clean-step" # noqa
),
'required': True
}
})
@base.deploy_step(priority=0, argsinfo=_FIRMWARE_UPDATE_ARGSINFO)
@base.clean_step(priority=0, abortable=False,
argsinfo=_FIRMWARE_UPDATE_ARGSINFO)
@firmware_processor.verify_firmware_update_args
def update_firmware(self, task, **kwargs):
"""Updates the firmware.
@ -371,7 +442,8 @@ class IloManagement(base.ManagementInterface):
:param task: a TaskManager object.
:raises: InvalidParameterValue if update firmware mode is not 'ilo'.
Even applicable for invalid input cases.
:raises: NodeCleaningFailure, on failure to execute step.
:raises: NodeCleaningFailure, on failure to execute of clean step.
:raises: InstanceDeployFailure, on failure to execute of deploy step.
"""
node = task.node
fw_location_objs_n_components = []
@ -409,7 +481,10 @@ class IloManagement(base.ManagementInterface):
"on node: %(node)s ... failed",
{'firmware_image': firmware_image_info,
'node': node.uuid})
raise exception.NodeCleaningFailure(node=node.uuid, reason=ilo_exc)
if node.clean_step:
raise exception.NodeCleaningFailure(node=node.uuid,
reason=ilo_exc)
raise exception.InstanceDeployFailure(reason=ilo_exc)
# Updating of firmware images happen here.
try:
@ -419,13 +494,14 @@ class IloManagement(base.ManagementInterface):
"node: %(node)s ... in progress",
{'firmware_file': fw_location, 'node': node.uuid})
_execute_ilo_clean_step(
_execute_ilo_step(
node, 'update_firmware', fw_location, component)
LOG.debug("Firmware update for %(firmware_file)s on "
"node: %(node)s ... done",
{'firmware_file': fw_location, 'node': node.uuid})
except exception.NodeCleaningFailure:
except (exception.NodeCleaningFailure,
exception.InstanceDeployFailure):
with excutils.save_and_reraise_exception():
LOG.error("Firmware update for %(firmware_file)s on "
"node: %(node)s failed.",
@ -434,35 +510,23 @@ class IloManagement(base.ManagementInterface):
for fw_loc_obj_n_comp_tup in fw_location_objs_n_components:
fw_loc_obj_n_comp_tup[0].remove()
# Firmware might have ejected the virtual media, if it was used.
# Re-create the environment for agent boot, if required
task.driver.boot.clean_up_ramdisk(task)
deploy_opts = deploy_utils.build_agent_options(node)
task.driver.boot.prepare_ramdisk(task, deploy_opts)
LOG.info("All Firmware update operations completed successfully "
"for node: %s.", node.uuid)
@METRICS.timer('IloManagement.update_firmware_sum')
@base.clean_step(priority=0, abortable=False, argsinfo={
'url': {
'description': (
"The image location for SPP (Service Pack for Proliant) ISO."
),
'required': True
},
'checksum': {
'description': (
"The md5 checksum of the SPP image file."
),
'required': True
},
'components': {
'description': (
"The list of firmware component filenames. If not specified, "
"SUM updates all the firmware components."
),
'required': False}
})
@base.clean_step(priority=0, abortable=False,
argsinfo=_FIRMWARE_UPDATE_SUM_ARGSINFO)
def update_firmware_sum(self, task, **kwargs):
"""Updates the firmware using Smart Update Manager (SUM).
:param task: a TaskManager object.
:raises: NodeCleaningFailure, on failure to execute step.
:raises: NodeCleaningFailure, on failure to execute of clean step.
"""
node = task.node
# The arguments are validated and sent to the ProliantHardwareManager

View File

@ -24,6 +24,7 @@ from ironic.common import states
from ironic.conductor import task_manager
from ironic.conductor import utils as manager_utils
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import boot as ilo_boot
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
@ -198,50 +199,84 @@ class IloManagementTestCase(test_common.BaseIloTest):
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_ilo_clean_step_ok(self, get_ilo_object_mock):
def test__execute_ilo_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(
step_mock = getattr(ilo_mock, 'fake-step')
ilo_management._execute_ilo_step(
self.node, 'fake-step', 'args', kwarg='kwarg')
clean_step_mock.assert_called_once_with('args', kwarg='kwarg')
step_mock.assert_called_once_with('args', kwarg='kwarg')
@mock.patch.object(ilo_management, 'LOG', spec_set=True, autospec=True)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_ilo_clean_step_not_supported(self, get_ilo_object_mock,
log_mock):
def test__execute_ilo_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(
step_mock = getattr(ilo_mock, 'fake-step')
step_mock.side_effect = exc
ilo_management._execute_ilo_step(
self.node, 'fake-step', 'args', kwarg='kwarg')
clean_step_mock.assert_called_once_with('args', kwarg='kwarg')
step_mock.assert_called_once_with('args', kwarg='kwarg')
self.assertTrue(log_mock.warning.called)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_ilo_clean_step_fail(self, get_ilo_object_mock):
def _test__execute_ilo_step_fail(self, get_ilo_object_mock):
if self.node.clean_step:
step = self.node.clean_step
step_name = step['step']
exept = exception.NodeCleaningFailure
else:
step = self.node.deploy_step
step_name = step['step']
exept = exception.InstanceDeployFailure
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')
step_mock = getattr(ilo_mock, step_name)
step_mock.side_effect = exc
self.assertRaises(exept,
ilo_management._execute_ilo_step,
self.node, step_name, 'args', kwarg='kwarg')
step_mock.assert_called_once_with('args', kwarg='kwarg')
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
def test__execute_ilo_step_fail_clean(self):
self.node.clean_step = {'priority': 100, 'interface': 'management',
'step': 'fake-step',
'argsinfo': {}}
self.node.save()
self._test__execute_ilo_step_fail()
def test__execute_ilo_step_fail_deploy(self):
self.node.deploy_step = {'priority': 100, 'interface': 'management',
'step': 'fake-step',
'argsinfo': {}}
self.node.save()
self._test__execute_ilo_step_fail()
@mock.patch.object(deploy_utils, 'build_agent_options',
spec_set=True, autospec=True)
def test_reset_ilo(self, clean_step_mock):
@mock.patch.object(ilo_boot.IloVirtualMediaBoot, 'clean_up_ramdisk',
spec_set=True, autospec=True)
@mock.patch.object(ilo_boot.IloVirtualMediaBoot, 'prepare_ramdisk',
spec_set=True, autospec=True)
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_reset_ilo(
self, execute_step_mock, prepare_mock, cleanup_mock, build_mock):
build_mock.return_value = {'a': 'b'}
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')
execute_step_mock.assert_called_once_with(task.node, 'reset_ilo')
cleanup_mock.assert_called_once_with(mock.ANY, task)
build_mock.assert_called_once_with(task.node)
prepare_mock.assert_called_once_with(
mock.ANY, mock.ANY, {'a': 'b'})
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_reset_ilo_credential_ok(self, clean_step_mock):
def test_reset_ilo_credential_ok(self, step_mock):
info = self.node.driver_info
info['ilo_change_password'] = "fake-password"
self.node.driver_info = info
@ -249,66 +284,79 @@ class IloManagementTestCase(test_common.BaseIloTest):
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(
step_mock.assert_called_once_with(
task.node, 'reset_ilo_credential', 'fake-password')
self.assertNotIn('ilo_change_password', task.node.driver_info)
self.assertEqual('fake-password',
task.node.driver_info['ilo_password'])
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_reset_ilo_credential_pass_as_arg_ok(self, step_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.management.reset_ilo_credential(
task, change_password='fake-password')
step_mock.assert_called_once_with(
task.node, 'reset_ilo_credential', 'fake-password')
self.assertNotIn('ilo_change_password', task.node.driver_info)
self.assertEqual('fake-password',
task.node.driver_info['ilo_password'])
@mock.patch.object(ilo_management, 'LOG', spec_set=True, autospec=True)
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_reset_ilo_credential_no_password(self, clean_step_mock,
def test_reset_ilo_credential_no_password(self, 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.assertFalse(step_mock.called)
self.assertTrue(log_mock.info.called)
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_reset_bios_to_default(self, clean_step_mock):
def test_reset_bios_to_default(self, 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')
step_mock.assert_called_once_with(task.node,
'reset_bios_to_default')
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_reset_secure_boot_keys_to_default(self, clean_step_mock):
def test_reset_secure_boot_keys_to_default(self, 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')
step_mock.assert_called_once_with(task.node,
'reset_secure_boot_keys')
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_clear_secure_boot_keys(self, clean_step_mock):
def test_clear_secure_boot_keys(self, 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')
step_mock.assert_called_once_with(task.node,
'clear_secure_boot_keys')
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_activate_license(self, clean_step_mock):
def test_activate_license(self, step_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
activate_license_args = {
'ilo_license_key': 'XXXXX-YYYYY-ZZZZZ-XYZZZ-XXYYZ'}
task.driver.management.activate_license(task,
**activate_license_args)
clean_step_mock.assert_called_once_with(
step_mock.assert_called_once_with(
task.node, 'activate_license', 'XXXXX-YYYYY-ZZZZZ-XYZZZ-XXYYZ')
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
def test_activate_license_no_or_invalid_format_license_key(
self, clean_step_mock):
self, step_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
for license_key_value in (None, [], {}):
@ -317,48 +365,32 @@ class IloManagementTestCase(test_common.BaseIloTest):
task.driver.management.activate_license,
task,
**activate_license_args)
self.assertFalse(clean_step_mock.called)
self.assertFalse(step_mock.called)
@mock.patch.object(deploy_utils, 'build_agent_options',
spec_set=True, autospec=True)
@mock.patch.object(ilo_boot.IloVirtualMediaBoot, 'clean_up_ramdisk',
spec_set=True, autospec=True)
@mock.patch.object(ilo_boot.IloVirtualMediaBoot, 'prepare_ramdisk',
spec_set=True, autospec=True)
@mock.patch.object(ilo_management, 'LOG')
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
@mock.patch.object(ilo_management.firmware_processor, 'FirmwareProcessor',
spec_set=True, autospec=True)
@mock.patch.object(ilo_common, 'remove_single_or_list_of_files',
spec_set=True, autospec=True)
def test_update_firmware_calls_clean_step_foreach_url(
self, remove_file_mock, FirmwareProcessor_mock, clean_step_mock,
LOG_mock):
def _test_update_firmware_calls_step_foreach_url(
self, remove_file_mock, FirmwareProcessor_mock, execute_step_mock,
LOG_mock, prepare_mock, cleanup_mock, build_mock):
if self.node.clean_step:
step = self.node.clean_step
else:
step = self.node.deploy_step
build_mock.return_value = {'a': 'b'}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
# | GIVEN |
firmware_images = [
{
'url': 'file:///any_path',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'http://any_url',
'checksum': 'xxxx',
'component': 'cpld'
},
{
'url': 'https://any_url',
'checksum': 'xxxx',
'component': 'power_pic'
},
{
'url': 'swift://container/object',
'checksum': 'xxxx',
'component': 'bios'
},
{
'url': 'file:///any_path',
'checksum': 'xxxx',
'component': 'chassis'
}
]
firmware_update_args = step['argsinfo']
FirmwareProcessor_mock.return_value.process_fw_on.side_effect = [
ilo_management.firmware_processor.FirmwareImageLocation(
'fw_location_for_filepath', 'filepath'),
@ -371,12 +403,8 @@ class IloManagementTestCase(test_common.BaseIloTest):
ilo_management.firmware_processor.FirmwareImageLocation(
'fw_location_for_another_filepath', 'filepath2')
]
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': firmware_images}
# | WHEN |
task.driver.management.update_firmware(task,
**firmware_update_args)
# | THEN |
calls = [mock.call(task.node, 'update_firmware',
'fw_location_for_filepath', 'ilo'),
mock.call(task.node, 'update_firmware',
@ -388,139 +416,338 @@ class IloManagementTestCase(test_common.BaseIloTest):
mock.call(task.node, 'update_firmware',
'fw_location_for_another_filepath', 'chassis'),
]
clean_step_mock.assert_has_calls(calls)
self.assertEqual(5, clean_step_mock.call_count)
execute_step_mock.assert_has_calls(calls)
self.assertEqual(5, execute_step_mock.call_count)
cleanup_mock.assert_called_once_with(mock.ANY, task)
build_mock.assert_called_once_with(task.node)
prepare_mock.assert_called_once_with(
mock.ANY, mock.ANY, {'a': 'b'})
def test_update_firmware_throws_if_invalid_update_mode_provided(self):
def test_update_firmware_calls_step_foreach_url_clean(self):
firmware_images = [
{
'url': 'file:///any_path',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'http://any_url',
'checksum': 'xxxx',
'component': 'cpld'
},
{
'url': 'https://any_url',
'checksum': 'xxxx',
'component': 'power_pic'
},
{
'url': 'swift://container/object',
'checksum': 'xxxx',
'component': 'bios'
},
{
'url': 'file:///any_path',
'checksum': 'xxxx',
'component': 'chassis'
}
]
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': firmware_images}
self.node.clean_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_calls_step_foreach_url()
def test_update_firmware_calls_step_foreach_url_deploy(self):
firmware_images = [
{
'url': 'file:///any_path',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'http://any_url',
'checksum': 'xxxx',
'component': 'cpld'
},
{
'url': 'https://any_url',
'checksum': 'xxxx',
'component': 'power_pic'
},
{
'url': 'swift://container/object',
'checksum': 'xxxx',
'component': 'bios'
},
{
'url': 'file:///any_path',
'checksum': 'xxxx',
'component': 'chassis'
}
]
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': firmware_images}
self.node.deploy_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_calls_step_foreach_url()
def _test_update_firmware_invalid_update_mode_provided(self):
if self.node.clean_step:
step = self.node.clean_step
else:
step = self.node.deploy_step
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
# | GIVEN |
firmware_update_args = step['argsinfo']
firmware_update_args = {'firmware_update_mode': 'invalid_mode',
'firmware_images': None}
# | WHEN & THEN |
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.update_firmware,
task,
**firmware_update_args)
def test_update_firmware_throws_error_for_no_firmware_url(self):
def test_update_firmware_invalid_update_mode_provided_clean(self):
firmware_update_args = {'firmware_update_mode': 'invalid_mode',
'firmware_images': None}
self.node.clean_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_invalid_update_mode_provided()
def test_update_firmware_invalid_update_mode_provided_deploy(self):
firmware_update_args = {'firmware_update_mode': 'invalid_mode',
'firmware_images': None}
self.node.deploy_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_invalid_update_mode_provided()
def _test_update_firmware_error_for_no_firmware_url(self):
if self.node.clean_step:
step = self.node.clean_step
else:
step = self.node.deploy_step
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
# | GIVEN |
firmware_update_args = step['argsinfo']
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': []}
# | WHEN & THEN |
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.update_firmware,
task,
**firmware_update_args)
def test_update_firmware_throws_error_for_invalid_component_type(self):
def test_update_firmware_error_for_no_firmware_url_clean(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': []}
self.node.clean_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_error_for_no_firmware_url()
def test_update_firmware_error_for_no_firmware_url_deploy(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': []}
self.node.deploy_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_error_for_no_firmware_url()
def _test_update_firmware_throws_error_for_invalid_component_type(self):
if self.node.clean_step:
step = self.node.clean_step
exept = exception.NodeCleaningFailure
else:
step = self.node.deploy_step
exept = exception.InstanceDeployFailure
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
# | GIVEN |
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'xyz'
}
]}
# | WHEN & THEN |
self.assertRaises(exception.NodeCleaningFailure,
firmware_update_args = step['argsinfo']
self.assertRaises(exept,
task.driver.management.update_firmware,
task,
**firmware_update_args)
def test_update_firmware_error_for_invalid_component_type_clean(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'xyz'
}
]}
self.node.clean_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_throws_error_for_invalid_component_type()
def test_update_firmware_error_for_invalid_component_type_deploy(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'xyz'
}
]}
self.node.deploy_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_throws_error_for_invalid_component_type()
@mock.patch.object(ilo_management, 'LOG')
@mock.patch.object(ilo_management.firmware_processor.FirmwareProcessor,
'process_fw_on', spec_set=True, autospec=True)
def test_update_firmware_throws_error_for_checksum_validation_error(
def _test_update_firmware_throws_error_for_checksum_validation_error(
self, process_fw_on_mock, LOG_mock):
if self.node.clean_step:
step = self.node.clean_step
exept = exception.NodeCleaningFailure
else:
step = self.node.deploy_step
exept = exception.InstanceDeployFailure
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
# | GIVEN |
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'invalid_checksum',
'component': 'bios'
}
]}
firmware_update_args = step['argsinfo']
process_fw_on_mock.side_effect = exception.ImageRefValidationFailed
# | WHEN & THEN |
self.assertRaises(exception.NodeCleaningFailure,
self.assertRaises(exept,
task.driver.management.update_firmware,
task,
**firmware_update_args)
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
def test_update_firmware_error_for_checksum_validation_error_clean(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'invalid_checksum',
'component': 'bios'
}
]}
self.node.clean_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_throws_error_for_checksum_validation_error()
def test_update_firmware_error_for_checksum_validation_error_deploy(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'invalid_checksum',
'component': 'bios'
}
]}
self.node.deploy_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_throws_error_for_checksum_validation_error()
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
@mock.patch.object(ilo_management.firmware_processor, 'FirmwareProcessor',
spec_set=True, autospec=True)
def test_update_firmware_doesnt_update_any_if_processing_on_any_url_fails(
def _test_update_firmware_doesnt_update_any_if_any_url_fails(
self, FirmwareProcessor_mock, clean_step_mock):
"""update_firmware throws error for failure in processing any url
update_firmware doesn't invoke firmware update of proliantutils
for any url if processing on any firmware url fails.
"""
if self.node.clean_step:
step = self.node.clean_step
exept = exception.NodeCleaningFailure
else:
step = self.node.deploy_step
exept = exception.InstanceDeployFailure
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
# | GIVEN |
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'any_invalid_url',
'checksum': 'xxxx',
'component': 'bios'
}]
}
firmware_update_args = step['argsinfo']
FirmwareProcessor_mock.return_value.process_fw_on.side_effect = [
ilo_management.firmware_processor.FirmwareImageLocation(
'extracted_firmware_url_of_any_valid_url', 'filename'),
exception.IronicException
]
# | WHEN & THEN |
self.assertRaises(exception.NodeCleaningFailure,
self.assertRaises(exept,
task.driver.management.update_firmware,
task,
**firmware_update_args)
self.assertFalse(clean_step_mock.called)
def test_update_firmware_doesnt_update_any_if_any_url_fails_clean(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'any_invalid_url',
'checksum': 'xxxx',
'component': 'bios'
}]
}
self.node.clean_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_doesnt_update_any_if_any_url_fails()
def test_update_firmware_doesnt_update_any_if_any_url_fails_deploy(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'any_invalid_url',
'checksum': 'xxxx',
'component': 'bios'
}]
}
self.node.deploy_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_doesnt_update_any_if_any_url_fails()
@mock.patch.object(ilo_management, 'LOG')
@mock.patch.object(ilo_management, '_execute_ilo_clean_step',
@mock.patch.object(ilo_management, '_execute_ilo_step',
spec_set=True, autospec=True)
@mock.patch.object(ilo_management.firmware_processor, 'FirmwareProcessor',
spec_set=True, autospec=True)
@mock.patch.object(ilo_management.firmware_processor.FirmwareImageLocation,
'remove', spec_set=True, autospec=True)
def test_update_firmware_cleans_all_files_if_exc_thrown(
def _test_update_firmware_cleans_all_files_if_exc_thrown(
self, remove_mock, FirmwareProcessor_mock, clean_step_mock,
LOG_mock):
if self.node.clean_step:
step = self.node.clean_step
exept = exception.NodeCleaningFailure
else:
step = self.node.deploy_step
exept = exception.InstanceDeployFailure
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
# | GIVEN |
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'any_invalid_url',
'checksum': 'xxxx',
'component': 'bios'
}]
}
firmware_update_args = step['argsinfo']
fw_loc_obj_1 = (ilo_management.firmware_processor.
FirmwareImageLocation('extracted_firmware_url_1',
'filename_1'))
@ -530,10 +757,9 @@ class IloManagementTestCase(test_common.BaseIloTest):
FirmwareProcessor_mock.return_value.process_fw_on.side_effect = [
fw_loc_obj_1, fw_loc_obj_2
]
clean_step_mock.side_effect = exception.NodeCleaningFailure(
clean_step_mock.side_effect = exept(
node=self.node.uuid, reason='ilo_exc')
# | WHEN & THEN |
self.assertRaises(exception.NodeCleaningFailure,
self.assertRaises(exept,
task.driver.management.update_firmware,
task,
**firmware_update_args)
@ -544,6 +770,46 @@ class IloManagementTestCase(test_common.BaseIloTest):
remove_mock.assert_has_calls([mock.call(fw_loc_obj_1),
mock.call(fw_loc_obj_2)])
def test_update_firmware_cleans_all_files_if_exc_thrown_clean(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'any_invalid_url',
'checksum': 'xxxx',
'component': 'bios'
}]
}
self.node.clean_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_cleans_all_files_if_exc_thrown()
def test_update_firmware_cleans_all_files_if_exc_thrown_deploy(self):
firmware_update_args = {'firmware_update_mode': 'ilo',
'firmware_images': [
{
'url': 'any_valid_url',
'checksum': 'xxxx',
'component': 'ilo'
},
{
'url': 'any_invalid_url',
'checksum': 'xxxx',
'component': 'bios'
}]
}
self.node.deploy_step = {'priority': 100, 'interface': 'management',
'step': 'update_firmware',
'argsinfo': firmware_update_args}
self.node.save()
self._test_update_firmware_cleans_all_files_if_exc_thrown()
@mock.patch.object(ilo_common, 'attach_vmedia', spec_set=True,
autospec=True)
@mock.patch.object(deploy_utils, 'agent_execute_clean_step',

View File

@ -0,0 +1,8 @@
---
features:
- |
Adds support for deploy steps to ``management`` interface of ``ilo``
hardware type. The methods ``reset_ilo``, ``reset_ilo_credential``,
``reset_bios_to_default``, ``reset_secure_boot_keys_to_default``,
``clear_secure_boot_keys`` and ``update_firmware`` can be used as
deploy steps.