def test_create_boot_iso_for_bios_with_no_boot_mode( self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=io.BytesIO) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', 'deploy_iso-uuid', 'efiboot-uuid', 'root-uuid', 'kernel-params', None, 'http://configdrive') fetch_images_mock.assert_any_call('ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') fetch_images_mock.assert_any_call('ctx', 'ramdisk-uuid', 'tmpdir/ramdisk-uuid') fetch_images_mock.assert_any_call('ctx', 'http://configdrive', 'tmpdir/configdrive') params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel-uuid', 'tmpdir/ramdisk-uuid', configdrive='tmpdir/configdrive', kernel_params=params)
def test_create_boot_iso_for_bios( self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', 'deploy_iso-uuid', 'root-uuid', 'kernel-params', 'bios') fetch_images_mock.assert_any_call( 'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') fetch_images_mock.assert_any_call( 'ctx', 'ramdisk-uuid', 'tmpdir/ramdisk-uuid') # Note (NobodyCam): the orginal assert asserted that fetch_images # was not called with parameters, this did not # work, So I instead assert that there were only # Two calls to the mock validating the above # asserts. self.assertEqual(2, fetch_images_mock.call_count) params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with('output_file', 'tmpdir/kernel-uuid', 'tmpdir/ramdisk-uuid', params)
def test_create_boot_iso_for_uefi_deploy_iso(self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', deploy_iso_href='deploy_iso-uuid', root_uuid='root-uuid', kernel_params='kernel-params', boot_mode='uefi') fetch_images_mock.assert_any_call('ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') fetch_images_mock.assert_any_call('ctx', 'ramdisk-uuid', 'tmpdir/ramdisk-uuid') fetch_images_mock.assert_any_call('ctx', 'deploy_iso-uuid', 'tmpdir/deploy_iso-uuid') params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel-uuid', 'tmpdir/ramdisk-uuid', deploy_iso='tmpdir/deploy_iso-uuid', esp_image=None, kernel_params=params)
def test_create_boot_iso_for_uefi_for_hrefs(self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = "tmpdir" tempdir_mock.return_value = mock_file_handle images.create_boot_iso( "ctx", "output_file", "http://kernel-href", "http://ramdisk-href", "http://deploy_iso-href", "root-uuid", "kernel-params", "uefi", ) expected_calls = [ mock.call("ctx", "http://kernel-href", "tmpdir/kernel-href"), mock.call("ctx", "http://ramdisk-href", "tmpdir/ramdisk-href"), mock.call("ctx", "http://deploy_iso-href", "tmpdir/deploy_iso-href"), ] fetch_images_mock.assert_has_calls(expected_calls) params = ["root=UUID=root-uuid", "kernel-params"] create_isolinux_mock.assert_called_once_with( "output_file", "tmpdir/deploy_iso-href", "tmpdir/kernel-href", "tmpdir/ramdisk-href", params )
def test_create_boot_iso_for_uefi_esp_image_for_hrefs( self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle images.create_boot_iso('ctx', 'output_file', 'http://kernel-href', 'http://ramdisk-href', esp_image_href='http://efiboot-href', root_uuid='root-uuid', kernel_params='kernel-params', boot_mode='uefi') expected_calls = [ mock.call('ctx', 'http://kernel-href', 'tmpdir/kernel-href'), mock.call('ctx', 'http://ramdisk-href', 'tmpdir/ramdisk-href'), mock.call('ctx', 'http://efiboot-href', 'tmpdir/efiboot-href') ] fetch_images_mock.assert_has_calls(expected_calls) params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel-href', 'tmpdir/ramdisk-href', deploy_iso=None, esp_image='tmpdir/efiboot-href', kernel_params=params)
def test_create_boot_iso_for_bios(self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', 'deploy_iso-uuid', 'efiboot-uuid', 'root-uuid', 'kernel-params', 'bios') fetch_images_mock.assert_any_call('ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') fetch_images_mock.assert_any_call('ctx', 'ramdisk-uuid', 'tmpdir/ramdisk-uuid') # Note (NobodyCam): the original assert asserted that fetch_images # was not called with parameters, this did not # work, So I instead assert that there were only # Two calls to the mock validating the above # asserts. self.assertEqual(2, fetch_images_mock.call_count) params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with('output_file', 'tmpdir/kernel-uuid', 'tmpdir/ramdisk-uuid', params)
def _prepare_boot_iso(task, root_uuid): """Prepare a boot ISO to boot the node. :param task: a TaskManager instance containing the node to act on. :param root_uuid: the uuid of the root partition. :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. :raises: ImageCreationFailed, if creating boot ISO for BIOS boot_mode failed. """ deploy_info = _parse_deploy_info(task.node) driver_internal_info = task.node.driver_internal_info # fetch boot iso if deploy_info.get('irmc_boot_iso'): boot_iso_href = deploy_info['irmc_boot_iso'] if _is_image_href_ordinary_file_name(boot_iso_href): driver_internal_info['irmc_boot_iso'] = boot_iso_href else: boot_iso_filename = _get_iso_name(task.node, label='boot') boot_iso_fullpathname = os.path.join( CONF.irmc.remote_image_share_root, boot_iso_filename) images.fetch(task.context, boot_iso_href, boot_iso_fullpathname) driver_internal_info['irmc_boot_iso'] = boot_iso_filename # create boot iso else: image_href = deploy_info['image_source'] image_props = ['kernel_id', 'ramdisk_id'] image_properties = images.get_image_properties( task.context, image_href, image_props) kernel_href = (task.node.instance_info.get('kernel') or image_properties['kernel_id']) ramdisk_href = (task.node.instance_info.get('ramdisk') or image_properties['ramdisk_id']) deploy_iso_href = deploy_info['irmc_deploy_iso'] boot_mode = boot_mode_utils.get_boot_mode(task.node) kernel_params = CONF.pxe.pxe_append_params boot_iso_filename = _get_iso_name(task.node, label='boot') boot_iso_fullpathname = os.path.join( CONF.irmc.remote_image_share_root, boot_iso_filename) images.create_boot_iso(task.context, boot_iso_fullpathname, kernel_href, ramdisk_href, deploy_iso_href=deploy_iso_href, root_uuid=root_uuid, kernel_params=kernel_params, boot_mode=boot_mode) driver_internal_info['irmc_boot_iso'] = boot_iso_filename # save driver_internal_info['irmc_boot_iso'] task.node.driver_internal_info = driver_internal_info task.node.save()
def _prepare_boot_iso(task, root_uuid): """Prepare a boot ISO to boot the node. :param task: a TaskManager instance containing the node to act on. :param root_uuid: the uuid of the root partition. :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. :raises: ImageCreationFailed, if creating boot ISO for BIOS boot_mode failed. """ deploy_info = _parse_deploy_info(task.node) driver_internal_info = task.node.driver_internal_info # fetch boot iso if deploy_info.get('irmc_boot_iso'): boot_iso_href = deploy_info['irmc_boot_iso'] if service_utils.is_image_href_ordinary_file_name(boot_iso_href): driver_internal_info['irmc_boot_iso'] = boot_iso_href else: boot_iso_filename = _get_boot_iso_name(task.node) boot_iso_fullpathname = os.path.join( CONF.irmc.remote_image_share_root, boot_iso_filename) images.fetch(task.context, boot_iso_href, boot_iso_fullpathname) driver_internal_info['irmc_boot_iso'] = boot_iso_filename # create boot iso else: image_href = deploy_info['image_source'] image_props = ['kernel_id', 'ramdisk_id'] image_properties = images.get_image_properties( task.context, image_href, image_props) kernel_href = (task.node.instance_info.get('kernel') or image_properties['kernel_id']) ramdisk_href = (task.node.instance_info.get('ramdisk') or image_properties['ramdisk_id']) deploy_iso_filename = _get_deploy_iso_name(task.node) deploy_iso = ('file://' + os.path.join( CONF.irmc.remote_image_share_root, deploy_iso_filename)) boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node) kernel_params = CONF.pxe.pxe_append_params boot_iso_filename = _get_boot_iso_name(task.node) boot_iso_fullpathname = os.path.join( CONF.irmc.remote_image_share_root, boot_iso_filename) images.create_boot_iso(task.context, boot_iso_fullpathname, kernel_href, ramdisk_href, deploy_iso, root_uuid, kernel_params, boot_mode) driver_internal_info['irmc_boot_iso'] = boot_iso_filename # save driver_internal_info['irmc_boot_iso'] task.node.driver_internal_info = driver_internal_info task.node.save()
def test_create_boot_iso_for_bios_with_no_boot_mode(self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = "tmpdir" tempdir_mock.return_value = mock_file_handle images.create_boot_iso( "ctx", "output_file", "kernel-uuid", "ramdisk-uuid", "deploy_iso-uuid", "root-uuid", "kernel-params", None ) fetch_images_mock.assert_any_call("ctx", "kernel-uuid", "tmpdir/kernel-uuid") fetch_images_mock.assert_any_call("ctx", "ramdisk-uuid", "tmpdir/ramdisk-uuid") params = ["root=UUID=root-uuid", "kernel-params"] create_isolinux_mock.assert_called_once_with("output_file", "tmpdir/kernel-uuid", "tmpdir/ramdisk-uuid", params)
def test_create_boot_iso(self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', 'root-uuid', 'kernel-params') fetch_images_mock.assert_any_call('ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') fetch_images_mock.assert_any_call('ctx', 'ramdisk-uuid', 'tmpdir/ramdisk-uuid') params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with('output_file', 'tmpdir/kernel-uuid', 'tmpdir/ramdisk-uuid', params)
def test_create_boot_iso_for_existing_iso(self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=io.BytesIO) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle base_iso = 'http://fake.local:1234/fake.iso' images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', 'deploy_iso-uuid', 'efiboot-uuid', None, None, None, base_iso=base_iso) fetch_images_mock.assert_any_call( 'ctx', base_iso, 'output_file') create_isolinux_mock.assert_not_called()
def test_create_boot_iso_for_uefi_for_hrefs( self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle images.create_boot_iso('ctx', 'output_file', 'http://kernel-href', 'http://ramdisk-href', 'http://deploy_iso-href', 'root-uuid', 'kernel-params', 'uefi') expected_calls = [mock.call('ctx', 'http://kernel-href', 'tmpdir/kernel-href'), mock.call('ctx', 'http://ramdisk-href', 'tmpdir/ramdisk-href'), mock.call('ctx', 'http://deploy_iso-href', 'tmpdir/deploy_iso-href')] fetch_images_mock.assert_has_calls(expected_calls) params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/deploy_iso-href', 'tmpdir/kernel-href', 'tmpdir/ramdisk-href', params)
def _get_boot_iso(task, root_uuid): """This method returns a boot ISO to boot the node. It chooses one of the two options in the order as below: 1. Image deployed has a meta-property 'boot_iso' in Glance. This should refer to the UUID of the boot_iso which exists in Glance. 2. Generates a boot ISO on the fly using kernel and ramdisk mentioned in the image deployed. It uploads the generated boot ISO to Swift. :param task: a TaskManager instance containing the node to act on. :param root_uuid: the uuid of the root partition. :returns: the information about the boot ISO. Returns the information in the format 'glance:<glance-boot-iso-uuid>' or 'swift:<swift-boot_iso-object-name>'. In case of Swift, it is assumed that the object exists in CONF.ilo.swift_ilo_container. On error finding the boot iso, it returns None. :raises: MissingParameterValue, if any of the required parameters are missing in the node's driver_info or instance_info. :raises: InvalidParameterValue, if any of the parameters have invalid value in the node's driver_info or instance_info. :raises: SwiftOperationError, if operation with Swift fails. :raises: ImageCreationFailed, if creation of boot ISO failed. """ # Option 1 - Check if user has provided a boot_iso in Glance. LOG.debug("Trying to get a boot ISO to boot the baremetal node") deploy_info = _parse_deploy_info(task.node) image_href = deploy_info['image_source'] glance_properties = ( images.get_glance_image_properties(task.context, image_href, ['boot_iso', 'kernel_id', 'ramdisk_id'])) boot_iso_uuid = glance_properties.get('boot_iso') kernel_uuid = glance_properties.get('kernel_id') ramdisk_uuid = glance_properties.get('ramdisk_id') if boot_iso_uuid: LOG.debug("Found boot_iso %s in Glance", boot_iso_uuid) return 'glance:%s' % boot_iso_uuid # NOTE(faizan) For uefi boot_mode, operator should provide efi capable # boot-iso in glance if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi': LOG.error(_LE("Unable to find boot_iso in Glance, required to deploy " "node %(node)s in UEFI boot mode."), {'node': task.node.uuid}) return if not kernel_uuid or not ramdisk_uuid: LOG.error(_LE("Unable to find 'kernel_id' and 'ramdisk_id' in Glance " "image %(image)s for generating boot ISO for %(node)s"), {'image': image_href, 'node': task.node.uuid}) return # NOTE(rameshg87): Functionality to share the boot ISOs created for # similar instances (instances with same deployed image) is # not implemented as of now. Creation/Deletion of such a shared boot ISO # will require synchronisation across conductor nodes for the shared boot # ISO. Such a synchronisation mechanism doesn't exist in ironic as of now. # Option 2 - Create boot_iso from kernel/ramdisk, upload to Swift # and provide its name. boot_iso_object_name = _get_boot_iso_object_name(task.node) kernel_params = CONF.pxe.pxe_append_params container = CONF.ilo.swift_ilo_container with tempfile.NamedTemporaryFile() as fileobj: boot_iso_tmp_file = fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_uuid, ramdisk_uuid, root_uuid, kernel_params) swift_api = swift.SwiftAPI() swift_api.create_object(container, boot_iso_object_name, boot_iso_tmp_file) LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name) return 'swift:%s' % boot_iso_object_name
def _get_boot_iso(task, root_uuid): """This method returns a boot ISO to boot the node. It chooses one of the three options in the order as below: 1. Does nothing if 'ilo_boot_iso' is present in node's instance_info and 'boot_iso_created_in_web_server' is not set in 'driver_internal_info'. 2. Image deployed has a meta-property 'boot_iso' in Glance. This should refer to the UUID of the boot_iso which exists in Glance. 3. Generates a boot ISO on the fly using kernel and ramdisk mentioned in the image deployed. It uploads the generated boot ISO to Swift. :param task: a TaskManager instance containing the node to act on. :param root_uuid: the uuid of the root partition. :returns: boot ISO URL. Should be either of below: * A Swift object - It should be of format 'swift:<object-name>'. It is assumed that the image object is present in CONF.ilo.swift_ilo_container; * A Glance image - It should be format 'glance://<glance-image-uuid>' or just <glance-image-uuid>; * An HTTP URL. On error finding the boot iso, it returns None. :raises: MissingParameterValue, if any of the required parameters are missing in the node's driver_info or instance_info. :raises: InvalidParameterValue, if any of the parameters have invalid value in the node's driver_info or instance_info. :raises: SwiftOperationError, if operation with Swift fails. :raises: ImageCreationFailed, if creation of boot ISO failed. :raises: exception.ImageRefValidationFailed if ilo_boot_iso is not HTTP(S) URL. """ LOG.debug("Trying to get a boot ISO to boot the baremetal node") # Option 1 - Check if user has provided ilo_boot_iso in node's # instance_info driver_internal_info = task.node.driver_internal_info boot_iso_created_in_web_server = ( driver_internal_info.get('boot_iso_created_in_web_server')) if (task.node.instance_info.get('ilo_boot_iso') and not boot_iso_created_in_web_server): LOG.debug("Using ilo_boot_iso provided in node's instance_info") boot_iso = task.node.instance_info['ilo_boot_iso'] if not service_utils.is_glance_image(boot_iso): try: image_service.HttpImageService().validate_href(boot_iso) except exception.ImageRefValidationFailed: with excutils.save_and_reraise_exception(): LOG.error("Virtual media deploy accepts only Glance " "images or HTTP(S) URLs as " "instance_info['ilo_boot_iso']. Either %s " "is not a valid HTTP(S) URL or is " "not reachable.", boot_iso) return task.node.instance_info['ilo_boot_iso'] # Option 2 - Check if user has provided a boot_iso in Glance. If boot_iso # is a supported non-glance href execution will proceed to option 3. deploy_info = _parse_deploy_info(task.node) image_href = deploy_info['image_source'] image_properties = ( images.get_image_properties( task.context, image_href, ['boot_iso', 'kernel_id', 'ramdisk_id'])) boot_iso_uuid = image_properties.get('boot_iso') kernel_href = (task.node.instance_info.get('kernel') or image_properties.get('kernel_id')) ramdisk_href = (task.node.instance_info.get('ramdisk') or image_properties.get('ramdisk_id')) if boot_iso_uuid: LOG.debug("Found boot_iso %s in Glance", boot_iso_uuid) return boot_iso_uuid if not kernel_href or not ramdisk_href: LOG.error("Unable to find kernel or ramdisk for " "image %(image)s to generate boot ISO for %(node)s", {'image': image_href, 'node': task.node.uuid}) return # NOTE(rameshg87): Functionality to share the boot ISOs created for # similar instances (instances with same deployed image) is # not implemented as of now. Creation/Deletion of such a shared boot ISO # will require synchronisation across conductor nodes for the shared boot # ISO. Such a synchronisation mechanism doesn't exist in ironic as of now. # Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift # or web server and provide its name. deploy_iso_uuid = deploy_info['ilo_deploy_iso'] boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node) boot_iso_object_name = _get_boot_iso_object_name(task.node) kernel_params = "" if deploy_utils.get_boot_option(task.node) == "ramdisk": i_info = task.node.instance_info kernel_params = "root=/dev/ram0 text " kernel_params += i_info.get("ramdisk_kernel_arguments", "") else: kernel_params = CONF.pxe.pxe_append_params with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as fileobj: boot_iso_tmp_file = fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_href, ramdisk_href, deploy_iso_uuid, root_uuid, kernel_params, boot_mode) if CONF.ilo.use_web_server_for_images: boot_iso_url = ( ilo_common.copy_image_to_web_server(boot_iso_tmp_file, boot_iso_object_name)) driver_internal_info = task.node.driver_internal_info driver_internal_info['boot_iso_created_in_web_server'] = True task.node.driver_internal_info = driver_internal_info task.node.save() LOG.debug("Created boot_iso %(boot_iso)s for node %(node)s", {'boot_iso': boot_iso_url, 'node': task.node.uuid}) return boot_iso_url else: container = CONF.ilo.swift_ilo_container swift_api = swift.SwiftAPI() swift_api.create_object(container, boot_iso_object_name, boot_iso_tmp_file) LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name) return 'swift:%s' % boot_iso_object_name
def prepare_iso_image(task, kernel_href, ramdisk_href, deploy_iso_href=None, bootloader_href=None, root_uuid=None, kernel_params=None, timeout=None, use_web_server=False, container=None): """Prepare an ISO to boot the node. Build bootable ISO out of `kernel_href` and `ramdisk_href` (and `bootloader` if it's UEFI boot), then push built image up to Swift and return a temporary URL. :param task: a TaskManager instance containing the node to act on. :param kernel_href: URL or Glance UUID of the kernel to use :param ramdisk_href: URL or Glance UUID of the ramdisk to use :param bootloader_href: URL or Glance UUID of the EFI bootloader image to use when creating UEFI bootbable ISO :param root_uuid: optional uuid of the root partition. :param kernel_params: a dictionary containing 'parameter name'->'value' mapping to be passed to kernel command line. :param timeout: swift object expiry timeout :returns: bootable ISO HTTP URL. :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. :raises: ImageCreationFailed, if creating ISO image failed. """ if not kernel_href or not ramdisk_href: raise exception.InvalidParameterValue( _("Unable to find kernel or ramdisk for " "building ISO for %(node)s") % {'node': task.node.uuid}) boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node) LOG.debug( "Trying to create %(boot_mode)s ISO image for node %(node)s " "with kernel %(kernel_href)s, ramdisk %(ramdisk_href)s, " "bootloader %(bootloader_href)s and kernel params %(params)s" "", { 'node': task.node.uuid, 'boot_mode': boot_mode, 'kernel_href': kernel_href, 'ramdisk_href': ramdisk_href, 'bootloader_href': bootloader_href, 'params': kernel_params }) with tempfile.NamedTemporaryFile(dir=CONF.tempdir, suffix='.iso') as fileobj: boot_iso_tmp_file = fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_href, ramdisk_href, deploy_iso_href=deploy_iso_href, esp_image_href=bootloader_href, root_uuid=root_uuid, kernel_params=kernel_params, boot_mode=boot_mode) iso_object_name = get_iso_image_name(task.node) if use_web_server: boot_iso_url = (deploy_utils.copy_image_to_web_server( boot_iso_tmp_file, iso_object_name)) driver_internal_info = task.node.driver_internal_info driver_internal_info['boot_iso_created_in_web_server'] = True task.node.driver_internal_info = driver_internal_info task.node.save() LOG.debug("Created boot_iso %(boot_iso)s for node %(node)s", { 'boot_iso': boot_iso_url, 'node': task.node.uuid }) return boot_iso_url else: swift_api = swift.SwiftAPI() object_headers = None if task.node.driver == 'redfish': object_headers = {'X-Delete-After': str(timeout)} swift_api.create_object(container, iso_object_name, boot_iso_tmp_file, object_headers=object_headers) LOG.debug("Created ISO %(name)s in Swift for node %(node)s", { 'node': task.node.uuid, 'name': iso_object_name }) if task.node.driver == 'redfish': boot_iso_url = swift_api.get_temp_url(container, iso_object_name, timeout) return boot_iso_url else: return 'swift:%s' % iso_object_name
def _prepare_iso_image(cls, task, kernel_href, ramdisk_href, bootloader_href=None, root_uuid=None, params=None): """Prepare an ISO to boot the node. Build bootable ISO out of `kernel_href` and `ramdisk_href` (and `bootloader` if it's UEFI boot), then push built image up to Swift and return a temporary URL. :param task: a TaskManager instance containing the node to act on. :param kernel_href: URL or Glance UUID of the kernel to use :param ramdisk_href: URL or Glance UUID of the ramdisk to use :param bootloader_href: URL or Glance UUID of the EFI bootloader image to use when creating UEFI bootbable ISO :param root_uuid: optional uuid of the root partition. :param params: a dictionary containing 'parameter name'->'value' mapping to be passed to kernel command line. :returns: bootable ISO HTTP URL. :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. :raises: ImageCreationFailed, if creating ISO image failed. """ if not kernel_href or not ramdisk_href: raise exception.InvalidParameterValue( _("Unable to find kernel or ramdisk for " "building ISO for %(node)s") % {'node': task.node.uuid}) i_info = task.node.instance_info if deploy_utils.get_boot_option(task.node) == "ramdisk": kernel_params = "root=/dev/ram0 text " kernel_params += i_info.get("ramdisk_kernel_arguments", "") else: kernel_params = i_info.get('kernel_append_params', CONF.redfish.kernel_append_params) if params: kernel_params = ' '.join( (kernel_params, ' '.join('%s=%s' % kv for kv in params.items()))) boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node) LOG.debug( "Trying to create %(boot_mode)s ISO image for node %(node)s " "with kernel %(kernel_href)s, ramdisk %(ramdisk_href)s, " "bootloader %(bootloader_href)s and kernel params %(params)s" "", { 'node': task.node.uuid, 'boot_mode': boot_mode, 'kernel_href': kernel_href, 'ramdisk_href': ramdisk_href, 'bootloader_href': bootloader_href, 'params': kernel_params }) with tempfile.NamedTemporaryFile(dir=CONF.tempdir, suffix='.iso') as fileobj: boot_iso_tmp_file = fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_href, ramdisk_href, esp_image_href=bootloader_href, root_uuid=root_uuid, kernel_params=kernel_params, boot_mode=boot_mode) iso_object_name = cls._get_iso_image_name(task.node) image_url = cls._publish_image(boot_iso_tmp_file, iso_object_name) LOG.debug( "Created ISO %(name)s in Swift for node %(node)s, exposed " "as temporary URL %(url)s", { 'node': task.node.uuid, 'name': iso_object_name, 'url': image_url }) return image_url
def _get_boot_iso(task, root_uuid): """This method returns a boot ISO to boot the node. It chooses one of the three options in the order as below: 1. Does nothing if 'ilo_boot_iso' is present in node's instance_info and 'boot_iso_created_in_web_server' is not set in 'driver_internal_info'. 2. Image deployed has a meta-property 'boot_iso' in Glance. This should refer to the UUID of the boot_iso which exists in Glance. 3. Generates a boot ISO on the fly using kernel and ramdisk mentioned in the image deployed. It uploads the generated boot ISO to Swift. :param task: a TaskManager instance containing the node to act on. :param root_uuid: the uuid of the root partition. :returns: boot ISO URL. Should be either of below: * A Swift object - It should be of format 'swift:<object-name>'. It is assumed that the image object is present in CONF.ilo.swift_ilo_container; * A Glance image - It should be format 'glance://<glance-image-uuid>' or just <glance-image-uuid>; * An HTTP URL. On error finding the boot iso, it returns None. :raises: MissingParameterValue, if any of the required parameters are missing in the node's driver_info or instance_info. :raises: InvalidParameterValue, if any of the parameters have invalid value in the node's driver_info or instance_info. :raises: SwiftOperationError, if operation with Swift fails. :raises: ImageCreationFailed, if creation of boot ISO failed. :raises: exception.ImageRefValidationFailed if ilo_boot_iso is not HTTP(S) URL. """ LOG.debug("Trying to get a boot ISO to boot the baremetal node") # Option 1 - Check if user has provided ilo_boot_iso in node's # instance_info driver_internal_info = task.node.driver_internal_info boot_iso_created_in_web_server = ( driver_internal_info.get('boot_iso_created_in_web_server')) if (task.node.instance_info.get('ilo_boot_iso') and not boot_iso_created_in_web_server): LOG.debug("Using ilo_boot_iso provided in node's instance_info") boot_iso = task.node.instance_info['ilo_boot_iso'] if not service_utils.is_glance_image(boot_iso): try: image_service.HttpImageService().validate_href(boot_iso) except exception.ImageRefValidationFailed: with excutils.save_and_reraise_exception(): LOG.error("Virtual media deploy accepts only Glance " "images or HTTP(S) URLs as " "instance_info['ilo_boot_iso']. Either %s " "is not a valid HTTP(S) URL or is " "not reachable.", boot_iso) return task.node.instance_info['ilo_boot_iso'] # Option 2 - Check if user has provided a boot_iso in Glance. If boot_iso # is a supported non-glance href execution will proceed to option 3. deploy_info = _parse_deploy_info(task.node) image_href = deploy_info['image_source'] image_properties = ( images.get_image_properties( task.context, image_href, ['boot_iso', 'kernel_id', 'ramdisk_id'])) boot_iso_uuid = image_properties.get('boot_iso') kernel_href = (task.node.instance_info.get('kernel') or image_properties.get('kernel_id')) ramdisk_href = (task.node.instance_info.get('ramdisk') or image_properties.get('ramdisk_id')) if boot_iso_uuid: LOG.debug("Found boot_iso %s in Glance", boot_iso_uuid) return boot_iso_uuid if not kernel_href or not ramdisk_href: LOG.error("Unable to find kernel or ramdisk for " "image %(image)s to generate boot ISO for %(node)s", {'image': image_href, 'node': task.node.uuid}) return # NOTE(rameshg87): Functionality to share the boot ISOs created for # similar instances (instances with same deployed image) is # not implemented as of now. Creation/Deletion of such a shared boot ISO # will require synchronisation across conductor nodes for the shared boot # ISO. Such a synchronisation mechanism doesn't exist in ironic as of now. # Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift # or web server and provide its name. deploy_iso_uuid = deploy_info['ilo_deploy_iso'] boot_mode = boot_mode_utils.get_boot_mode(task.node) boot_iso_object_name = _get_boot_iso_object_name(task.node) kernel_params = "" if deploy_utils.get_boot_option(task.node) == "ramdisk": i_info = task.node.instance_info kernel_params = "root=/dev/ram0 text " kernel_params += i_info.get("ramdisk_kernel_arguments", "") else: kernel_params = CONF.pxe.pxe_append_params with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as fileobj: boot_iso_tmp_file = fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_href, ramdisk_href, deploy_iso_href=deploy_iso_uuid, root_uuid=root_uuid, kernel_params=kernel_params, boot_mode=boot_mode) if CONF.ilo.use_web_server_for_images: boot_iso_url = ( ilo_common.copy_image_to_web_server(boot_iso_tmp_file, boot_iso_object_name)) driver_internal_info = task.node.driver_internal_info driver_internal_info['boot_iso_created_in_web_server'] = True task.node.driver_internal_info = driver_internal_info task.node.save() LOG.debug("Created boot_iso %(boot_iso)s for node %(node)s", {'boot_iso': boot_iso_url, 'node': task.node.uuid}) return boot_iso_url else: container = CONF.ilo.swift_ilo_container swift_api = swift.SwiftAPI() swift_api.create_object(container, boot_iso_object_name, boot_iso_tmp_file) LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name) return 'swift:%s' % boot_iso_object_name
def _prepare_iso_image(task, kernel_href, ramdisk_href, bootloader_href=None, configdrive=None, root_uuid=None, params=None, base_iso=None): """Prepare an ISO to boot the node. Build bootable ISO out of `kernel_href` and `ramdisk_href` (and `bootloader` if it's UEFI boot), then push built image up to Swift and return a temporary URL. If `configdrive` is specified it will be eventually written onto the boot ISO image. :param task: a TaskManager instance containing the node to act on. :param kernel_href: URL or Glance UUID of the kernel to use :param ramdisk_href: URL or Glance UUID of the ramdisk to use :param bootloader_href: URL or Glance UUID of the EFI bootloader image to use when creating UEFI bootbable ISO :param configdrive: URL to or a compressed blob of a ISO9660 or FAT-formatted OpenStack config drive image. This image will be written onto the built ISO image. Optional. :param root_uuid: optional uuid of the root partition. :param params: a dictionary containing 'parameter name'->'value' mapping to be passed to kernel command line. :returns: bootable ISO HTTP URL. :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. :raises: ImageCreationFailed, if creating ISO image failed. """ if (not kernel_href or not ramdisk_href) and not base_iso: raise exception.InvalidParameterValue( _("Unable to find kernel, ramdisk for " "building ISO, or explicit ISO for %(node)s") % {'node': task.node.uuid}) img_handler = ImageHandler(task.node.driver) k_param = img_handler.kernel_params i_info = task.node.instance_info # NOTE(TheJulia): Until we support modifying a base iso, most of # this logic actually does nothing in the end. But it should! if deploy_utils.get_boot_option(task.node) == "ramdisk": if not base_iso: kernel_params = "root=/dev/ram0 text " kernel_params += i_info.get("ramdisk_kernel_arguments", "") else: kernel_params = None else: kernel_params = i_info.get('kernel_append_params', k_param) if params and not base_iso: kernel_params = ' '.join( (kernel_params, ' '.join('%s=%s' % kv for kv in params.items()))) boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node) LOG.debug( "Trying to create %(boot_mode)s ISO image for node %(node)s " "with kernel %(kernel_href)s, ramdisk %(ramdisk_href)s, " "bootloader %(bootloader_href)s and kernel params %(params)s" "", { 'node': task.node.uuid, 'boot_mode': boot_mode, 'kernel_href': kernel_href, 'ramdisk_href': ramdisk_href, 'bootloader_href': bootloader_href, 'params': kernel_params }) with tempfile.NamedTemporaryFile(dir=CONF.tempdir, suffix='.iso') as boot_fileobj: with tempfile.NamedTemporaryFile(dir=CONF.tempdir, suffix='.img') as cfgdrv_fileobj: configdrive_href = configdrive # FIXME(TheJulia): This is treated as conditional with # a base_iso as the intent, eventually, is to support # injection into the supplied image. if configdrive and not base_iso: parsed_url = urlparse.urlparse(configdrive) if not parsed_url.scheme: cfgdrv_blob = base64.decode_as_bytes(configdrive) with open(cfgdrv_fileobj.name, 'wb') as f: f.write(cfgdrv_blob) configdrive_href = urlparse.urlunparse( ('file', '', cfgdrv_fileobj.name, '', '', '')) LOG.debug( "Built configdrive out of configdrive blob " "for node %(node)s", {'node': task.node.uuid}) boot_iso_tmp_file = boot_fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_href, ramdisk_href, esp_image_href=bootloader_href, configdrive_href=configdrive_href, root_uuid=root_uuid, kernel_params=kernel_params, boot_mode=boot_mode, base_iso=base_iso) iso_object_name = _get_iso_image_name(task.node) image_url = img_handler.publish_image(boot_iso_tmp_file, iso_object_name) LOG.debug( "Created ISO %(name)s in object store for node %(node)s, " "exposed as temporary URL " "%(url)s", { 'node': task.node.uuid, 'name': iso_object_name, 'url': image_url }) return image_url
def _prepare_iso_image(task, kernel_href, ramdisk_href, bootloader_href=None, root_uuid=None, params=None, base_iso=None, inject_files=None): """Prepare an ISO to boot the node. Build bootable ISO out of `kernel_href` and `ramdisk_href` (and `bootloader` if it's UEFI boot), then push built image up to Swift and return a temporary URL. :param task: a TaskManager instance containing the node to act on. :param kernel_href: URL or Glance UUID of the kernel to use :param ramdisk_href: URL or Glance UUID of the ramdisk to use :param bootloader_href: URL or Glance UUID of the EFI bootloader image to use when creating UEFI bootbable ISO :param root_uuid: optional uuid of the root partition. :param params: a dictionary containing 'parameter name'->'value' mapping to be passed to kernel command line. :param inject_files: Mapping of local source file paths to their location on the final ISO image. :returns: bootable ISO HTTP URL. :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. :raises: ImageCreationFailed, if creating ISO image failed. """ if (not kernel_href or not ramdisk_href) and not base_iso: raise exception.InvalidParameterValue( _("Unable to find kernel, ramdisk for " "building ISO, or explicit ISO for %(node)s") % {'node': task.node.uuid}) img_handler = ImageHandler(task.node.driver) k_param = img_handler.kernel_params i_info = task.node.instance_info # NOTE(TheJulia): Until we support modifying a base iso, most of # this logic actually does nothing in the end. But it should! if deploy_utils.get_boot_option(task.node) == "ramdisk": if not base_iso: kernel_params = "root=/dev/ram0 text " kernel_params += i_info.get("ramdisk_kernel_arguments", "") else: kernel_params = None else: kernel_params = i_info.get('kernel_append_params', k_param) if params and not base_iso: kernel_params = ' '.join( (kernel_params, ' '.join('%s=%s' % kv for kv in params.items()))) boot_mode = boot_mode_utils.get_boot_mode(task.node) LOG.debug( "Trying to create %(boot_mode)s ISO image for node %(node)s " "with kernel %(kernel_href)s, ramdisk %(ramdisk_href)s, " "bootloader %(bootloader_href)s and kernel params %(params)s" "", { 'node': task.node.uuid, 'boot_mode': boot_mode, 'kernel_href': kernel_href, 'ramdisk_href': ramdisk_href, 'bootloader_href': bootloader_href, 'params': kernel_params }) with tempfile.NamedTemporaryFile(dir=CONF.tempdir, suffix='.iso') as boot_fileobj: boot_iso_tmp_file = boot_fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_href, ramdisk_href, esp_image_href=bootloader_href, root_uuid=root_uuid, kernel_params=kernel_params, boot_mode=boot_mode, base_iso=base_iso, inject_files=inject_files) iso_object_name = _get_name(task.node, prefix='boot', suffix='.iso') image_url = img_handler.publish_image(boot_iso_tmp_file, iso_object_name) LOG.debug( "Created ISO %(name)s in object store for node %(node)s, " "exposed as temporary URL " "%(url)s", { 'node': task.node.uuid, 'name': iso_object_name, 'url': image_url }) return image_url