Merge "Follow-up of firmware update(iLO) as manual cleaning step"

This commit is contained in:
Jenkins 2016-03-17 08:49:34 +00:00 committed by Gerrit Code Review
commit cb1a83f740
5 changed files with 120 additions and 50 deletions

View File

@ -137,10 +137,10 @@ def copy_image_to_web_server(source_file_path, destination):
def remove_image_from_web_server(object_name):
"""Removes the given image from the configured http web server.
"""Removes the given image from the configured web server.
This method removes the given image from the http_root location. It deletes
the image if it exists in web server.
This method removes the given image from the http_root location,
if the image exists.
:param object_name: The name of the image file which needs to be removed
from the web server root.
@ -362,7 +362,7 @@ def _prepare_floppy_image(task, params):
:raises: ImageUploadFailed, if copying the source file to the
web server fails.
:raises: SwiftOperationError, if any operation with Swift fails.
:returns: the http image URL or the Swift temp url for the floppy image.
:returns: the HTTP image URL or the Swift temp url for the floppy image.
"""
with tempfile.NamedTemporaryFile(
dir=CONF.tempdir) as vfat_image_tmpfile_obj:

View File

@ -1,4 +1,4 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -64,14 +64,15 @@ def verify_firmware_update_args(func):
firmware_images = kwargs.get('firmware_images')
if firmware_update_mode != 'ilo':
msg = (_("Invalid firmware update mode: %(mode)s. "
"'ilo' is the only supported firmware update mode.")
% {'mode': firmware_update_mode})
msg = (_("Invalid firmware update mode '%(mode)s' provided for "
"node: %(node)s. 'ilo' is the only supported firmware "
"update mode.")
% {'mode': firmware_update_mode, 'node': task.node.uuid})
LOG.error(msg)
raise exception.InvalidParameterValue(msg)
if not firmware_images:
msg = _("Firmware images cannot be an empty list or None. ")
msg = _("Firmware images cannot be an empty list or None.")
LOG.error(msg)
raise exception.InvalidParameterValue(msg)
@ -310,12 +311,13 @@ def _extract_fw_from_file(node, target_file):
fw_image_filename)
if to_upload:
is_different_file = True
LOG.debug("For firmware update, hosting firmware file: %s ... ",
fw_image_location)
# fw_image_filename = os.path.basename(fw_image_location)
try:
if CONF.ilo.use_web_server_for_images:
# upload firmware image file to conductor webserver
LOG.debug("For firmware update on node %(node)s, hosting "
"firmware file %(firmware_image)s on web server ...",
{'firmware_image': fw_image_location,
'node': node.uuid})
fw_image_uploaded_url = ilo_common.copy_image_to_web_server(
fw_image_location, fw_image_filename)
@ -324,6 +326,10 @@ def _extract_fw_from_file(node, target_file):
_remove_webserver_based_me, fw_image_location_obj)
else:
# upload firmware image file to swift
LOG.debug("For firmware update on node %(node)s, hosting "
"firmware file %(firmware_image)s on swift ...",
{'firmware_image': fw_image_location,
'node': node.uuid})
fw_image_uploaded_url = ilo_common.copy_image_to_swift(
fw_image_location, fw_image_filename)
@ -339,10 +345,10 @@ def _extract_fw_from_file(node, target_file):
# takes care of handling the deletion of the file.
ilo_common.remove_single_or_list_of_files(fw_image_location)
LOG.debug("For firmware update, hosting firmware file: "
"%(fw_image_location)s ... done. Hosted firmware file: "
"%(fw_image_uploaded_url)s",
{'fw_image_location': fw_image_location,
LOG.debug("For firmware update on node %(node)s, hosting firmware "
"file: %(fw_image_location)s ... done. Hosted firmware "
"file: %(fw_image_uploaded_url)s",
{'fw_image_location': fw_image_location, 'node': node.uuid,
'fw_image_uploaded_url': fw_image_uploaded_url})
else:
fw_image_location_obj.remove = types.MethodType(

View File

@ -339,23 +339,25 @@ class IloManagement(base.ManagementInterface):
@base.clean_step(priority=0, abortable=False, argsinfo={
'firmware_update_mode': {
'description': (
'This argument indicates the mode (or mechanism) of '
'Out-of-Band (OOB) firmware update procedure. '
'Currently, the only supported value is `ilo`.'
"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 dictionaries of '
'firmware image url, its checksum (md5) and component type. '
'The firmware images will be applied (in the order given) '
'one by one on the baremetal server. The different types of '
'firmware url schemes supported are: '
'`file`, `http`, `https` & `swift`. '
'And the different firmware components that can be updated '
'are: '
'`ilo`, `cpld`, `power_pic`, `bios` & `chassis`.'
"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 "
"http://docs.openstack.org/developer/ironic/drivers/ilo.html#initiating-firmware-update-as-manual-clean-step" # noqa
),
'required': True
}
@ -424,11 +426,11 @@ class IloManagement(base.ManagementInterface):
except exception.NodeCleaningFailure:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Firmware update for %(firmware_file)s on "
"node: %(node)s ... failed"),
"node: %(node)s failed."),
{'firmware_file': fw_location, 'node': node.uuid})
finally:
for fw_loc_obj_n_comp_tup in fw_location_objs_n_components:
fw_loc_obj_n_comp_tup[0].remove()
LOG.info(_LI("All Firmware operations completed successfully for "
"node: %s."), node.uuid)
LOG.info(_LI("All Firmware update operations completed successfully "
"for node: %s."), node.uuid)

View File

@ -787,6 +787,47 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
swift_obj_mock.delete_object.assert_called_once_with(
container, object_name)
@mock.patch.object(ilo_common, 'LOG')
@mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True)
def test_remove_image_from_swift_suppresses_notfound_exc(
self, swift_api_mock, LOG_mock):
# | GIVEN |
self.config(swift_ilo_container='ilo_container', group='ilo')
container = CONF.ilo.swift_ilo_container
swift_obj_mock = swift_api_mock.return_value
object_name = 'object_name'
raised_exc = exception.SwiftObjectNotFoundError(
operation='delete_object', object=object_name, container=container)
swift_obj_mock.delete_object.side_effect = raised_exc
# | WHEN |
ilo_common.remove_image_from_swift(object_name)
# | THEN |
LOG_mock.warning.assert_called_once_with(
mock.ANY, {'associated_with_msg': "", 'err': raised_exc})
@mock.patch.object(ilo_common, 'LOG')
@mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True)
def test_remove_image_from_swift_suppresses_operror_exc(
self, swift_api_mock, LOG_mock):
# | GIVEN |
self.config(swift_ilo_container='ilo_container', group='ilo')
container = CONF.ilo.swift_ilo_container
swift_obj_mock = swift_api_mock.return_value
object_name = 'object_name'
raised_exc = exception.SwiftOperationError(operation='delete_object',
error='failed')
swift_obj_mock.delete_object.side_effect = raised_exc
# | WHEN |
ilo_common.remove_image_from_swift(object_name, 'alice_in_wonderland')
# | THEN |
LOG_mock.exception.assert_called_once_with(
mock.ANY, {'object_name': object_name, 'container': container,
'associated_with_msg': ("associated with "
"alice_in_wonderland"),
'err': raised_exc})
@mock.patch.object(ironic_utils, 'unlink_without_raise', spec_set=True,
autospec=True)
@mock.patch.object(ilo_common, '_get_floppy_image_name', spec_set=True,
@ -846,7 +887,7 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
@mock.patch.object(ironic_utils, 'unlink_without_raise', spec_set=True,
autospec=True)
def test_remove_single_or_list_of_files(self, unlink_mock):
def test_remove_single_or_list_of_files_with_file_list(self, unlink_mock):
# | GIVEN |
file_list = ['/any_path1/any_file1',
'/any_path2/any_file2',
@ -859,6 +900,16 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
mock.call('/any_path3/any_file3')]
unlink_mock.assert_has_calls(calls)
@mock.patch.object(ironic_utils, 'unlink_without_raise', spec_set=True,
autospec=True)
def test_remove_single_or_list_of_files_with_file_str(self, unlink_mock):
# | GIVEN |
file_path = '/any_path1/any_file'
# | WHEN |
ilo_common.remove_single_or_list_of_files(file_path)
# | THEN |
unlink_mock.assert_called_once_with('/any_path1/any_file')
@mock.patch.object(__builtin__, 'open', autospec=True)
def test_verify_image_checksum(self, open_mock):
# | GIVEN |

View File

@ -1,4 +1,4 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -51,11 +51,13 @@ class FirmwareProcessorTestCase(base.TestCase):
update_firmware_mock.__name__ = 'update_firmware_mock'
wrapped_func = (ilo_fw_processor.
verify_firmware_update_args(update_firmware_mock))
node_fake = mock.MagicMock(uuid='fake_node_uuid')
task_fake = mock.MagicMock(node=node_fake)
# | WHEN & THEN |
self.assertRaises(exception.InvalidParameterValue,
wrapped_func,
mock.ANY,
mock.ANY,
task_fake,
**firmware_update_args)
def test_verify_firmware_update_args_throws_for_no_firmware_url(self):
@ -76,7 +78,7 @@ class FirmwareProcessorTestCase(base.TestCase):
def test_get_and_validate_firmware_image_info(self):
# | GIVEN |
firmware_image_info = {
'url': 'any_valid_url',
'url': self.any_url,
'checksum': 'b64c8f7799cfbb553d384d34dc43fafe336cc889',
'component': 'BIOS'
}
@ -85,7 +87,7 @@ class FirmwareProcessorTestCase(base.TestCase):
ilo_fw_processor.get_and_validate_firmware_image_info(
firmware_image_info))
# | THEN |
self.assertEqual('any_valid_url', url)
self.assertEqual(self.any_url, url)
self.assertEqual('b64c8f7799cfbb553d384d34dc43fafe336cc889', checksum)
self.assertEqual('bios', component)
@ -93,12 +95,12 @@ class FirmwareProcessorTestCase(base.TestCase):
self):
# | GIVEN |
invalid_firmware_image_info = {
'url': 'any_valid_url',
'url': self.any_url,
'component': 'bios'
}
# | WHEN | & | THEN |
self.assertRaises(
exception.MissingParameterValue,
self.assertRaisesRegexp(
exception.MissingParameterValue, 'checksum',
ilo_fw_processor.get_and_validate_firmware_image_info,
invalid_firmware_image_info)
@ -106,13 +108,13 @@ class FirmwareProcessorTestCase(base.TestCase):
self):
# | GIVEN |
invalid_firmware_image_info = {
'url': 'any_valid_url',
'url': self.any_url,
'checksum': 'valid_checksum',
'component': ''
}
# | WHEN | & | THEN |
self.assertRaises(
exception.MissingParameterValue,
self.assertRaisesRegexp(
exception.MissingParameterValue, 'component',
ilo_fw_processor.get_and_validate_firmware_image_info,
invalid_firmware_image_info)
@ -120,7 +122,7 @@ class FirmwareProcessorTestCase(base.TestCase):
self):
# | GIVEN |
invalid_firmware_image_info = {
'url': 'any_valid_url',
'url': self.any_url,
'checksum': 'valid_checksum',
'component': 'INVALID'
}
@ -277,6 +279,8 @@ class FirmwareProcessorTestCase(base.TestCase):
fw_processor.process_fw_on,
node_mock,
checksum_fake)
shutil_mock.rmtree.assert_called_once_with(
tempfile_mock.mkdtemp(), ignore_errors=True)
@mock.patch.object(ilo_fw_processor, 'tempfile', autospec=True)
@mock.patch.object(ilo_fw_processor, 'os', autospec=True)
@ -310,6 +314,8 @@ class FirmwareProcessorTestCase(base.TestCase):
actual_return_location.fw_image_location)
self.assertEqual(expected_return_location.fw_image_filename,
actual_return_location.fw_image_filename)
shutil_mock.rmtree.assert_called_once_with(
tempfile_mock.mkdtemp(), ignore_errors=True)
@mock.patch.object(__builtin__, 'open', autospec=True)
@mock.patch.object(
@ -384,19 +390,24 @@ class FirmwareProcessorTestCase(base.TestCase):
any_target_file = 'any_target_file'
self.fw_processor_fake.parsed_url = urlparse.urlparse(
any_swift_based_firmware_file)
urlparse_mock.reset_mock()
# | WHEN |
ilo_fw_processor._download_swift_based_fw_to(self.fw_processor_fake,
any_target_file)
# | THEN |
_download_http_based_fw_to_mock.assert_called_once_with(
self.fw_processor_fake, any_target_file)
urlparse_mock.assert_called_once_with(
swift_mock.SwiftAPI().get_temp_url.return_value)
self.assertEqual(
urlparse_mock.return_value, self.fw_processor_fake.parsed_url)
@mock.patch.object(ilo_fw_processor, 'ilo_common', autospec=True)
@mock.patch.object(ilo_fw_processor, 'proliantutils_utils', autospec=True)
def test__extract_fw_from_file_calls_process_firmware_image(
self, utils_mock, ilo_common_mock):
# | GIVEN |
node_mock = mock.ANY
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
ilo_object_mock = ilo_common_mock.get_ilo_object.return_value
utils_mock.process_firmware_image.return_value = ('some_location',
@ -412,7 +423,7 @@ class FirmwareProcessorTestCase(base.TestCase):
def test__extract_fw_from_file_doesnt_upload_firmware(
self, utils_mock, ilo_common_mock):
# | GIVEN |
node_mock = mock.ANY
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
utils_mock.process_firmware_image.return_value = (
'some_location/some_fw_file', False, True)
@ -428,7 +439,7 @@ class FirmwareProcessorTestCase(base.TestCase):
def test__extract_fw_from_file_sets_loc_obj_remove_to_file_if_no_upload(
self, _remove_mock, utils_mock, ilo_common_mock):
# | GIVEN |
node_mock = mock.ANY
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
utils_mock.process_firmware_image.return_value = (
'some_location/some_fw_file', False, True)
@ -444,7 +455,7 @@ class FirmwareProcessorTestCase(base.TestCase):
def test__extract_fw_from_file_uploads_firmware_to_webserver(
self, utils_mock, ilo_common_mock):
# | GIVEN |
node_mock = mock.ANY
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
utils_mock.process_firmware_image.return_value = (
'some_location/some_fw_file', True, True)
@ -462,7 +473,7 @@ class FirmwareProcessorTestCase(base.TestCase):
def test__extract_fw_from_file_sets_loc_obj_remove_to_webserver(
self, _remove_mock, utils_mock, ilo_common_mock):
# | GIVEN |
node_mock = mock.ANY
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
utils_mock.process_firmware_image.return_value = (
'some_location/some_fw_file', True, True)
@ -479,7 +490,7 @@ class FirmwareProcessorTestCase(base.TestCase):
def test__extract_fw_from_file_uploads_firmware_to_swift(
self, utils_mock, ilo_common_mock):
# | GIVEN |
node_mock = mock.ANY
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
utils_mock.process_firmware_image.return_value = (
'some_location/some_fw_file', True, True)
@ -497,7 +508,7 @@ class FirmwareProcessorTestCase(base.TestCase):
def test__extract_fw_from_file_sets_loc_obj_remove_to_swift(
self, _remove_mock, utils_mock, ilo_common_mock):
# | GIVEN |
node_mock = mock.ANY
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
utils_mock.process_firmware_image.return_value = (
'some_location/some_fw_file', True, True)