From 58301e41e046e0b9a16828a0a712de16e77545b1 Mon Sep 17 00:00:00 2001 From: Ryan Ronnander Date: Mon, 4 May 2026 22:56:06 -0400 Subject: [PATCH] core: pass tar member name as direct join key for OVA extract - enables multi-disk OVA imports to managed block storage domains - extract_ova.py joins disks by tar member name directly, removing the pathToImageId sub-field and image_mappings argv that bridged OVF and engine-fresh UUID universes - drops the now-orphaned imageMappings field on ConvertOvaParameters (the OVA path stashes the diskId-to-tar-name map on the parent ImportVmFromOvaParameters and resolves it once into ovaTarNamesByIndex before handing off to ConvertOva / ExtractOva) - tested on LINSTOR by exporting then re-importing: VMs with 1, 2, and 3 disks on managed block storage, mixed iSCSI + managed block storage, iSCSI-only, and a template Signed-off-by: Ryan Ronnander --- .../bll/exportimport/ExtractOvaCommand.java | 79 ++++--------- .../exportimport/ImportVmFromOvaCommand.java | 75 +++++------- .../ImportVmTemplateFromOvaCommand.java | 41 ++++--- .../OvaImportManagedBlockSupport.java | 13 --- .../common/action/ConvertOvaParameters.java | 27 ++--- .../ovirt-ova-extract/files/extract_ova.py | 107 +++++------------- .../roles/ovirt-ova-extract/tasks/main.yml | 1 - 7 files changed, 111 insertions(+), 232 deletions(-) diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ExtractOvaCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ExtractOvaCommand.java index 6cc4d57c146..c29da992568 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ExtractOvaCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ExtractOvaCommand.java @@ -8,13 +8,11 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import java.util.stream.IntStream; import javax.enterprise.inject.Instance; import javax.enterprise.inject.Typed; import javax.inject.Inject; -import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.ConcurrentChildCommandsExecutionCallback; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; @@ -101,8 +99,8 @@ protected void executeVmCommand() { prepareManagedBlockDisksForAnsibleExtract(); } List diskPaths = prepareImages(); - String diskPathToFormat = prepareDiskPathToFormat(getDiskList(), diskPaths); - boolean succeeded = runAnsibleImportOvaPlaybook(diskPathToFormat); + String disksJson = prepareDisksJson(getDiskList(), diskPaths); + boolean succeeded = runAnsibleImportOvaPlaybook(disksJson); teardownImages(); if (!succeeded) { log.error("Failed to extract OVA file"); @@ -213,50 +211,13 @@ private void prepareManagedBlockDisksForAnsibleExtract() { } } - private String buildOvaImageMappingsYamlForAnsible() { - Map byDiskId = getParameters().getOvaSourceImageIdByDiskId(); - if (MapUtils.isNotEmpty(byDiskId)) { - Map currentImageIdToSource = new LinkedHashMap<>(); - for (DiskImage d : getDiskList()) { - Guid sourceImageId = byDiskId.get(d.getId()); - if (sourceImageId != null) { - currentImageIdToSource.put(d.getImageId(), sourceImageId); - } - } - if (!currentImageIdToSource.isEmpty()) { - return currentImageIdToSource.entrySet() - .stream() - .map(e -> String - .format("\\\"%s\\\": \\\"%s\\\"", e.getValue().toString(), e.getKey().toString())) - .collect(Collectors.joining(", ", "{", "}")); - } - } - Map legacy = getParameters().getImageMappings(); - if (MapUtils.isEmpty(legacy)) { - return "{}"; - } - if (legacy.size() == 1 && getDiskList().size() == 1) { - Map.Entry e = legacy.entrySet().iterator().next(); - Guid sourceImageId = e.getValue(); - Guid currentImageId = getDiskList().get(0).getImageId(); - return "{" + String.format("\\\"%s\\\": \\\"%s\\\"", - sourceImageId.toString(), - currentImageId.toString()) + "}"; - } - return legacy.entrySet() - .stream() - .map(e -> String.format("\\\"%s\\\": \\\"%s\\\"", e.getValue().toString(), e.getKey().toString())) - .collect(Collectors.joining(", ", "{", "}")); - } - - private boolean runAnsibleImportOvaPlaybook(String disksPathToFormat) { + private boolean runAnsibleImportOvaPlaybook(String disksJson) { long timeout = TimeUnit.MINUTES.toSeconds( EngineLocalConfig.getInstance().getInteger("ANSIBLE_PLAYBOOK_EXEC_DEFAULT_TIMEOUT")); AnsibleCommandConfig commandConfig = new AnsibleCommandConfig() .host(getVds()) .variable("ovirt_import_ova_path", getParameters().getOvaPath()) - .variable("ovirt_import_ova_disks", disksPathToFormat) - .variable("ovirt_import_ova_image_mappings", buildOvaImageMappingsYamlForAnsible()) + .variable("ovirt_import_ova_disks", disksJson) .variable("ansible_timeout", timeout) // /var/log/ovirt-engine/ova/ovirt-import-ova-ansible-{hostname}-{correlationid}-{timestamp}.log .logFileDirectory(IMPORT_OVA_LOG_DIRECTORY) @@ -317,27 +278,27 @@ private List prepareImages() { } /** - * @return a json with the corresponding mounted disks paths and formats + * @return JSON map keyed by OVA tar member name: {tarName: {path, format}} */ - private String prepareDiskPathToFormat(List diskList, List diskPaths) { - Map diskPathToFormat = IntStream.range(0, diskList.size()) - .boxed() - .collect(Collectors.toMap(i -> diskPaths.get(i), - i -> diskList.get(i).getVolumeFormat() == VolumeFormat.COW ? "qcow2" : "raw")); - Map diskPathToImageId = IntStream.range(0, diskList.size()) - .boxed() - .collect(Collectors.toMap(i -> diskPaths.get(i), - i -> diskList.get(i).getImageId().toString())); - Map spec = new HashMap<>(); - spec.put("pathToFormat", diskPathToFormat); - spec.put("pathToImageId", diskPathToImageId); - String json; + private String prepareDisksJson(List diskList, List diskPaths) { + List tarNames = getParameters().getOvaTarNamesByIndex(); + if (tarNames == null || tarNames.size() != diskList.size()) { + throw new EngineException( + EngineError.GeneralException, + "OVA extract: ovaTarNamesByIndex missing or size mismatch with disk list"); + } + Map> entries = new LinkedHashMap<>(); + for (int i = 0; i < diskList.size(); i++) { + Map entry = new HashMap<>(); + entry.put("path", diskPaths.get(i)); + entry.put("format", diskList.get(i).getVolumeFormat() == VolumeFormat.COW ? "qcow2" : "raw"); + entries.put(tarNames.get(i), entry); + } try { - json = new ObjectMapper().writeValueAsString(spec); + return encode(new ObjectMapper().writeValueAsString(entries)); } catch (IOException e) { throw new RuntimeException("failed to serialize disk info"); } - return encode(json); } private List getDiskList() { diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ImportVmFromOvaCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ImportVmFromOvaCommand.java index 2fea9a4522e..59b25f0cf20 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ImportVmFromOvaCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ImportVmFromOvaCommand.java @@ -1,13 +1,12 @@ package org.ovirt.engine.core.bll.exportimport; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.inject.Inject; -import org.apache.commons.collections.CollectionUtils; import org.ovirt.engine.core.bll.CommandActionState; import org.ovirt.engine.core.bll.DisableInPrepareMode; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; @@ -123,52 +122,21 @@ private void attachManagedBlockVolumesToProxyHostForOvaConversion() { getVmId()); } - private Map buildOvaImageMappingsForConversion() { - if (!getParameters().isImportAsNewEntity() || newDiskIdForDisk.isEmpty()) { - return getParameters().getImageMappings(); - } - Map imageMappings = new HashMap<>(); - for (DiskImage disk : getDisks()) { - DiskImage old = newDiskIdForDisk.get(disk.getId()); - if (old != null) { - imageMappings.put(disk.getImageId(), old.getImageId()); - } - } - return imageMappings.isEmpty() ? getParameters().getImageMappings() : imageMappings; - } - - private Map buildOvaSourceImageIdByDiskId() { - if (!getParameters().isImportAsNewEntity() || newDiskIdForDisk.isEmpty()) { - return null; - } - return OvaImportManagedBlockSupport.ovaSourceImageIdByDiskId( - getDisks(), - diskId -> { - DiskImage old = newDiskIdForDisk.get(diskId); - return old != null ? old.getImageId() : null; - }); - } - @Override protected void processImages() { - List ovfTarImageIdsInOrder = null; - if (CommandActionState.EXECUTE.equals(getActionState()) - && !getParameters().isImportAsNewEntity() - && OvaImportManagedBlockSupport.isManagedBlockDestination(getStorageDomain()) - && getVm().getOrigin() == OriginType.OVIRT) { - ovfTarImageIdsInOrder = - getVm().getImages().stream().map(DiskImage::getImageId).collect(Collectors.toList()); - } + // Capture OVF tar names (image.imageId == ) before super.processImages + // mints fresh disk ids; persist via imageMappings (command is reloaded before convert()). + List ovfTarImageIds = getVm().getImages().stream() + .map(DiskImage::getImageId) + .collect(Collectors.toList()); super.processImages(); - if (CollectionUtils.isNotEmpty(ovfTarImageIdsInOrder)) { - List live = getDisks(); - if (live.size() == ovfTarImageIdsInOrder.size()) { - Map legacy = new HashMap<>(); - for (int i = 0; i < live.size(); i++) { - legacy.put(live.get(i).getImageId(), ovfTarImageIdsInOrder.get(i)); - } - getParameters().setImageMappings(legacy); + List live = getDisks(); + if (live.size() == ovfTarImageIds.size()) { + Map tarNameByDiskId = new LinkedHashMap<>(); + for (int i = 0; i < live.size(); i++) { + tarNameByDiskId.put(live.get(i).getId(), ovfTarImageIds.get(i)); } + getParameters().setImageMappings(tarNameByDiskId); } } @@ -191,7 +159,6 @@ private ConvertOvaParameters buildConvertOvaParameters() { parameters.setOvaPath(getParameters().getOvaPath()); parameters.setVmName(getVmName()); parameters.setDisks(getDisks()); - parameters.setImageMappings(buildOvaImageMappingsForConversion()); parameters.setStoragePoolId(getStoragePoolId()); parameters.setStorageDomainId(getStorageDomainId()); parameters.setProxyHostId(getParameters().getProxyHostId()); @@ -203,7 +170,7 @@ private ConvertOvaParameters buildConvertOvaParameters() { parameters.setParentParameters(getParameters()); parameters.setEndProcedure(EndProcedure.COMMAND_MANAGED); parameters.setPreAttachedManagedBlockDevicesByDiskId(preAttachedManagedBlockDeviceMapForOvaChildCommands()); - parameters.setOvaSourceImageIdByDiskId(buildOvaSourceImageIdByDiskId()); + parameters.setOvaTarNamesByIndex(buildOvaTarNamesByIndex()); return parameters; } @@ -212,7 +179,6 @@ private ConvertOvaParameters buildExtractOvaParameters() { parameters.setOvaPath(getParameters().getOvaPath()); parameters.setVmName(getVmName()); parameters.setDisks(getDisks()); - parameters.setImageMappings(buildOvaImageMappingsForConversion()); parameters.setStoragePoolId(getStoragePoolId()); parameters.setStorageDomainId(getStorageDomainId()); parameters.setProxyHostId(getParameters().getProxyHostId()); @@ -221,10 +187,23 @@ private ConvertOvaParameters buildExtractOvaParameters() { parameters.setParentParameters(getParameters()); parameters.setEndProcedure(EndProcedure.COMMAND_MANAGED); parameters.setPreAttachedManagedBlockDevicesByDiskId(preAttachedManagedBlockDeviceMapForOvaChildCommands()); - parameters.setOvaSourceImageIdByDiskId(buildOvaSourceImageIdByDiskId()); + parameters.setOvaTarNamesByIndex(buildOvaTarNamesByIndex()); return parameters; } + private List buildOvaTarNamesByIndex() { + Map tarNameByDiskId = getParameters().getImageMappings(); + if (tarNameByDiskId == null || tarNameByDiskId.isEmpty()) { + return null; + } + return getDisks().stream() + .map(d -> { + Guid tarName = tarNameByDiskId.get(d.getId()); + return tarName != null ? tarName.toString() : null; + }) + .collect(Collectors.toList()); + } + private Map> preAttachedManagedBlockDeviceMapForOvaChildCommands() { if (!OvaImportManagedBlockSupport.isManagedBlockDestination(getStorageDomain())) { return null; diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ImportVmTemplateFromOvaCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ImportVmTemplateFromOvaCommand.java index 3aae894fef3..91000da8b9f 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ImportVmTemplateFromOvaCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/ImportVmTemplateFromOvaCommand.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -197,10 +198,24 @@ protected void addDisksToDb() { @Override protected void copyImagesToTargetDomain() { + // OVF tar names per disk via originalDiskImageIdMap (populated by initImportClonedTemplateDisks + // for both Clone and !Clone); persist via diskMappings (command is reloaded before convert()). + List ovfTarImageIds = getImages().stream() + .map(d -> { + Guid orig = getOriginalDiskImageIdMap(d.getId()); + return orig != null ? orig : d.getImageId(); + }) + .collect(Collectors.toList()); List createdDiskIds = new ArrayList<>(); getImages().stream().map(this::adjustDisk).forEach(img -> createdDiskIds.add(createDisk(img))); getParameters().setTemplateDiskIdsForOvaExtract(createdDiskIds); - getParameters().setDiskMappings(getImageMappings()); + if (createdDiskIds.size() == ovfTarImageIds.size()) { + Map tarNameByDiskId = new LinkedHashMap<>(); + for (int i = 0; i < createdDiskIds.size(); i++) { + tarNameByDiskId.put(createdDiskIds.get(i), ovfTarImageIds.get(i)); + } + getParameters().setDiskMappings(tarNameByDiskId); + } } protected DiskImage adjustDisk(DiskImage image) { @@ -256,16 +271,17 @@ private Map> preAttachedManagedBlockDeviceMapForOvaChi return OvaImportManagedBlockSupport.preAttachedManagedBlockDevicesByDiskId(getVmTemplate().getDiskList()); } - private Map buildOvaSourceImageIdByDiskId() { - return OvaImportManagedBlockSupport.ovaSourceImageIdByDiskId( - getVmTemplate().getDiskList(), - this::getOriginalDiskImageIdMap); - } - - protected Map getImageMappings() { - return getImages().stream().collect(Collectors.toMap( - DiskImage::getImageId, - d -> getOriginalDiskImageIdMap(d.getId()))); + private List buildOvaTarNamesByIndex() { + Map tarNameByDiskId = getParameters().getImageMappings(); + if (tarNameByDiskId == null || tarNameByDiskId.isEmpty()) { + return null; + } + return getVmTemplate().getDiskList().stream() + .map(d -> { + Guid tarName = tarNameByDiskId.get(d.getId()); + return tarName != null ? tarName.toString() : null; + }) + .collect(Collectors.toList()); } private void convert() { @@ -287,7 +303,6 @@ private ConvertOvaParameters buildExtractOvaParameters() { parameters.setOvaPath(getParameters().getOvaPath()); parameters.setVmName(getVmTemplateName()); parameters.setDisks(getVmTemplate().getDiskList()); - parameters.setImageMappings(getParameters().getImageMappings()); parameters.setStoragePoolId(getStoragePoolId()); Guid extractStorageDomainId = getStorageDomainId(); if (Guid.isNullOrEmpty(extractStorageDomainId)) { @@ -307,7 +322,7 @@ private ConvertOvaParameters buildExtractOvaParameters() { parameters.setEndProcedure(EndProcedure.COMMAND_MANAGED); parameters.setVmEntityType(VmEntityType.TEMPLATE); parameters.setPreAttachedManagedBlockDevicesByDiskId(preAttachedManagedBlockDeviceMapForOvaChildCommands()); - parameters.setOvaSourceImageIdByDiskId(buildOvaSourceImageIdByDiskId()); + parameters.setOvaTarNamesByIndex(buildOvaTarNamesByIndex()); parameters.setTemplateDiskIdsForExtract(getParameters().getTemplateDiskIdsForOvaExtract()); return parameters; } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/OvaImportManagedBlockSupport.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/OvaImportManagedBlockSupport.java index edcb855c945..a8a94dfc57f 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/OvaImportManagedBlockSupport.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/exportimport/OvaImportManagedBlockSupport.java @@ -124,19 +124,6 @@ public static Map> preAttachedManagedBlockDevicesByDis return out.isEmpty() ? null : out; } - public static Map ovaSourceImageIdByDiskId( - Iterable disks, - Function currentDiskIdToOvaSourceImageId) { - Map out = new HashMap<>(); - for (DiskImage disk : disks) { - Guid sourceImageId = currentDiskIdToOvaSourceImageId.apply(disk.getId()); - if (sourceImageId != null) { - out.put(disk.getId(), sourceImageId); - } - } - return out.isEmpty() ? null : out; - } - @FunctionalInterface public interface InternalActionInvoker { void invoke(ActionType actionType, ActionParametersBase parameters, CommandContext commandContext); diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/ConvertOvaParameters.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/ConvertOvaParameters.java index b907e9d803f..79958983d11 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/ConvertOvaParameters.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/ConvertOvaParameters.java @@ -10,10 +10,9 @@ public class ConvertOvaParameters extends ConvertVmParameters { private String ovaPath; private VmEntityType vmEntityType = VmEntityType.VM; - private Map imageMappings; private Map> preAttachedManagedBlockDevicesByDiskId; - private Map ovaSourceImageIdByDiskId; private List templateDiskIdsForExtract; + private List ovaTarNamesByIndex; public ConvertOvaParameters() { } @@ -38,14 +37,6 @@ public void setVmEntityType(VmEntityType vmEntityType) { this.vmEntityType = vmEntityType; } - public Map getImageMappings() { - return imageMappings; - } - - public void setImageMappings(Map diskMappings) { - this.imageMappings = diskMappings; - } - public Map> getPreAttachedManagedBlockDevicesByDiskId() { return preAttachedManagedBlockDevicesByDiskId; } @@ -55,14 +46,6 @@ public void setPreAttachedManagedBlockDevicesByDiskId( this.preAttachedManagedBlockDevicesByDiskId = preAttachedManagedBlockDevicesByDiskId; } - public Map getOvaSourceImageIdByDiskId() { - return ovaSourceImageIdByDiskId; - } - - public void setOvaSourceImageIdByDiskId(Map ovaSourceImageIdByDiskId) { - this.ovaSourceImageIdByDiskId = ovaSourceImageIdByDiskId; - } - public List getTemplateDiskIdsForExtract() { return templateDiskIdsForExtract; } @@ -70,4 +53,12 @@ public List getTemplateDiskIdsForExtract() { public void setTemplateDiskIdsForExtract(List templateDiskIdsForExtract) { this.templateDiskIdsForExtract = templateDiskIdsForExtract; } + + public List getOvaTarNamesByIndex() { + return ovaTarNamesByIndex; + } + + public void setOvaTarNamesByIndex(List ovaTarNamesByIndex) { + this.ovaTarNamesByIndex = ovaTarNamesByIndex; + } } diff --git a/packaging/ansible-runner-service-project/project/roles/ovirt-ova-extract/files/extract_ova.py b/packaging/ansible-runner-service-project/project/roles/ovirt-ova-extract/files/extract_ova.py index 12b773e5fd2..9bf33749824 100755 --- a/packaging/ansible-runner-service-project/project/roles/ovirt-ova-extract/files/extract_ova.py +++ b/packaging/ansible-runner-service-project/project/roles/ovirt-ova-extract/files/extract_ova.py @@ -6,15 +6,12 @@ import sys import time - from contextlib import closing from subprocess import CalledProcessError from subprocess import call from subprocess import check_call from subprocess import check_output -import yaml - NUL = b"\0" TAR_BLOCK_SIZE = 512 @@ -55,10 +52,7 @@ def extract_disk(ova_path, offset, image_path, image_format): def nts(s, encoding, errors): - """ - Convert a null-terminated bytes object to a string. - Taken from tarfile.py (python 3). - """ + """Convert a null-terminated bytes object to a string.""" p = s.find(NUL) if p != -1: s = s[:p] @@ -66,98 +60,51 @@ def nts(s, encoding, errors): def nti(s): - """ - Convert a number field to a python number. - Inspired by tarfile.py. - It is customized to support both python 2 and python 3 - and the prefix 0o377 is ignored because we use this - function to parse only non-negative values. - """ + """Convert a tar number field to a python number.""" if s[0] != 0o200: try: - s = nts(s, "ascii", "strict") - n = int(s.strip() or "0", 8) + return int(nts(s, "ascii", "strict").strip() or "0", 8) except ValueError: print('invalid header') raise - else: - n = 0 - r = range(len(s) - 1) - for i in r: - n <<= 8 - n += s[i + 1] + n = 0 + for i in range(len(s) - 1): + n = (n << 8) + s[i + 1] return n -def parse_disks_arg(data): - """ - Engine JSON is either: - - Legacy: {"/path/to/disk": "raw"|"qcow2", ...} - (match via image id substring in path) - - Current: {"pathToFormat": {...}, - "pathToImageId": {"/path": "image-uuid", ...}} - Managed-block paths often contain a backend volume id that differs from - the engine image id used in OVF mappings; pathToImageId ties each path to - the mapping target id. - """ - if isinstance(data, dict) and "pathToFormat" in data: - return data["pathToFormat"], data.get("pathToImageId") or {} - return data, {} - +def extract_disks(ova_path, disks): + """Walk the OVA tar and extract each disk listed in `disks`. -def extract_disks(ova_path, disks_data, image_mappings): - path_to_format, path_to_image_id = parse_disks_arg(disks_data) + `disks` is a map keyed by tar member name: + {tar_name: {"path": target_path, "format": "raw"|"qcow2"}, ...} + Tar members not in the map (vm.ovf, nvram.dat, pad alignment) are skipped. + """ try: fd = os.open(ova_path, os.O_RDONLY | os.O_DIRECT) except OSError: fd = os.open(ova_path, os.O_RDONLY) buf = mmap.mmap(-1, TAR_BLOCK_SIZE) - with io.FileIO(fd, "r", closefd=True) as ova_file, \ - closing(buf): + with io.FileIO(fd, "r", closefd=True) as ova_file, closing(buf): while True: - # read next tar info ova_file.readinto(buf) - info = buf.read(512) - # tar files end with NUL blocks - if info == NUL*512: + info = buf.read(TAR_BLOCK_SIZE) + if info == NUL * TAR_BLOCK_SIZE: break - # preparation for the next iteration buf.seek(0) - # extract the next disk to the corresponding image name = nts(info[0:100], 'utf-8', 'surrogateescape') size = nti(info[124:136]) - if name.lower().endswith('ovf') or name.lower().endswith('dat'): - jump = size - # ovf is typically not aligned to 512 bytes blocks - remainder = size % TAR_BLOCK_SIZE - if remainder: - jump += TAR_BLOCK_SIZE - remainder - ova_file.seek(jump, 1) - elif name == 'pad': - ova_file.seek(size, 1) - else: - image_guid = image_mappings[name] if image_mappings else name - matched = False - for image_path, image_format in path_to_format.items(): - path_id = path_to_image_id.get(image_path) - if path_id == image_guid or image_guid in image_path: - extract_disk(ova_path, ova_file.tell(), image_path, - image_format) - ova_file.seek(size, 1) - matched = True - break - if not matched: - print( - "No target disk path for OVA member %r (image id %s); " - "known paths: %s" - % (name, image_guid, list(path_to_format.keys())), - file=sys.stderr) - sys.exit(1) - - -if len(sys.argv) < 4: - print("Usage: extract_ova.py ova_path disks_paths image_mappings") + target = disks.get(name) + if target is not None: + extract_disk(ova_path, ova_file.tell(), + target["path"], target["format"]) + # Skip past the entry's body, rounded up to the next 512-byte boundary. + aligned = (size + TAR_BLOCK_SIZE - 1) & ~(TAR_BLOCK_SIZE - 1) + ova_file.seek(aligned, 1) + + +if len(sys.argv) < 3: + print("Usage: extract_ova.py ova_path disks_json") sys.exit(2) -extract_disks(sys.argv[1], json.loads(sys.argv[2]), - yaml.load(sys.argv[3], Loader=yaml.SafeLoader)) +extract_disks(sys.argv[1], json.loads(sys.argv[2])) diff --git a/packaging/ansible-runner-service-project/project/roles/ovirt-ova-extract/tasks/main.yml b/packaging/ansible-runner-service-project/project/roles/ovirt-ova-extract/tasks/main.yml index 433870ba702..b2ea2a82a75 100644 --- a/packaging/ansible-runner-service-project/project/roles/ovirt-ova-extract/tasks/main.yml +++ b/packaging/ansible-runner-service-project/project/roles/ovirt-ova-extract/tasks/main.yml @@ -18,7 +18,6 @@ "{{ ova_temp.path }}/extract_ova.py" "{{ ovirt_import_ova_path }}" "{{ ovirt_import_ova_disks }}" - "{{ ovirt_import_ova_image_mappings }}" async: "{{ ansible_timeout }}" poll: 15 register: extraction_result