package org.ovirt.engine.core.utils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.ovirt.engine.core.common.businessentities.ArchitectureType; import org.ovirt.engine.core.common.businessentities.OvfEntityData; import org.ovirt.engine.core.common.businessentities.VmBase; import org.ovirt.engine.core.common.businessentities.VmEntityType; import org.ovirt.engine.core.common.businessentities.storage.UnregisteredDisk; import org.ovirt.engine.core.common.osinfo.OsRepository; import org.ovirt.engine.core.common.utils.SimpleDependencyInjector; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.utils.archivers.tar.TarInMemoryExport; import org.ovirt.engine.core.utils.ovf.xml.XmlDocument; import org.ovirt.engine.core.utils.ovf.xml.XmlNode; import org.ovirt.engine.core.utils.ovf.xml.XmlNodeList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class OvfUtils { private static final String TEMPLATE_ENTITY_TYPE = "<TemplateType>"; private static final String ENTITY_NAME = "<Name>"; private static final String END_ENTITY_NAME = "</Name>"; private static final String OVF_FILE_EXT = ".ovf"; private static final int GUID_LENGTH = Guid.Empty.toString().length(); protected static final Logger log = LoggerFactory.getLogger(TarInMemoryExport.class); private static String getEntityName(String ovfData) { int beginIndexOfEntityName = ovfData.indexOf(ENTITY_NAME) + ENTITY_NAME.length(); int endIndexOfEntityName = ovfData.indexOf(END_ENTITY_NAME, beginIndexOfEntityName); String entityName = ovfData.substring(beginIndexOfEntityName, endIndexOfEntityName); return entityName; } private static VmEntityType getVmEntityType(String ovfData) { VmEntityType vmEntityType = VmEntityType.VM; int indexOfEntityType = ovfData.indexOf(TEMPLATE_ENTITY_TYPE); if (indexOfEntityType != -1) { vmEntityType = VmEntityType.TEMPLATE; } return vmEntityType; } public static Set<Guid> fetchVmDisks(XmlDocument xmlDocument) { Set<Guid> disksIds = new HashSet<>(); XmlNodeList nodeList = xmlDocument.selectNodes("//*/Section"); XmlNode selectedSection = null; for (XmlNode section : nodeList) { String value = section.attributes.get("xsi:type").getValue(); if (value.equals("ovf:DiskSection_Type")) { selectedSection = section; break; } } if (selectedSection != null) { NodeList childNodeList = selectedSection.getChildNodes(); for (int k = 0; k < childNodeList.getLength(); k++) { if (childNodeList.item(k).getLocalName().equals("Disk")) { Node node = childNodeList.item(k).getAttributes().getNamedItem("ovf:fileRef"); if (node != null && node.getTextContent() != null) { disksIds.add(Guid.createGuidFromString(node.getTextContent().substring(0, GUID_LENGTH))); } } } } return disksIds; } private static Guid getEntityId(String fileName) { return Guid.createGuidFromString(fileName.substring(0, fileName.length() - OVF_FILE_EXT.length())); } private static OvfEntityData createOvfEntityData(Guid storageDomainId, String ovfData, VmEntityType vmEntityType, String entityName, ArchitectureType archType, Guid entityId) { OvfEntityData ovfEntityData = new OvfEntityData(); ovfEntityData.setOvfData(ovfData); ovfEntityData.setEntityType(vmEntityType); ovfEntityData.setEntityName(entityName); ovfEntityData.setStorageDomainId(storageDomainId); ovfEntityData.setArchitecture(archType); ovfEntityData.setEntityId(entityId); return ovfEntityData; } public static List<OvfEntityData> getOvfEntities(byte[] tar, List<UnregisteredDisk> unregisteredDisks, Guid storageDomainId) { List<OvfEntityData> ovfEntityDataFromTar = new ArrayList<>(); InputStream is = new ByteArrayInputStream(tar); log.info("Start fetching OVF files from tar file"); Map<String, ByteBuffer> filesFromTar; try (TarInMemoryExport memoryTar = new TarInMemoryExport(is)) { filesFromTar = memoryTar.unTar(); } catch (IOException e) { throw new RuntimeException(String.format("Exception while getting OVFs files from tar file for domain %s", storageDomainId), e); } for (Entry<String, ByteBuffer> fileEntry : filesFromTar.entrySet()) { if (fileEntry.getKey().endsWith(OVF_FILE_EXT)) { String ovfData = new String(fileEntry.getValue().array()); VmEntityType vmType = getVmEntityType(ovfData); ArchitectureType archType = null; Guid entityId = getEntityId(fileEntry.getKey()); String vmName = getEntityName(ovfData); try { XmlDocument xmlDocument = new XmlDocument(ovfData); archType = getOsSection(xmlDocument); updateUnregisteredDisksWithVMs(unregisteredDisks, entityId, vmName, xmlDocument); } catch (Exception e) { log.error("Could not parse VM's disks or architecture, file name: {}, content size: {}, error: {}", fileEntry.getKey(), ovfData.length(), e.getMessage()); log.debug("Exception", e); continue; } // Creates an OVF entity data. OvfEntityData ovfEntityData = createOvfEntityData(storageDomainId, ovfData, vmType, vmName, archType, entityId); log.info( "Retrieve OVF Entity from storage domain ID '{}' for entity ID '{}', entity name '{}' and VM Type of '{}'", storageDomainId, getEntityId(fileEntry.getKey()), getEntityName(ovfData), vmType.name()); ovfEntityDataFromTar.add(ovfEntityData); } else { log.info("File '{}' is not an OVF file, will be ignored.", fileEntry.getKey()); } } log.info("Finish to fetch OVF files from tar file. The number of OVF entities are {}", ovfEntityDataFromTar.size()); return ovfEntityDataFromTar; } public static void updateUnregisteredDisksWithVMs(List<UnregisteredDisk> unregisteredDisks, Guid entityId, String vmName, XmlDocument xmlDocument) { for (Guid diskId : fetchVmDisks(xmlDocument)) { UnregisteredDisk unregisterDisk = unregisteredDisks.stream() .filter(unregrDisk -> diskId.equals(unregrDisk.getId())) .findAny() .orElse(null); VmBase vm = new VmBase(); vm.setId(entityId); vm.setName(vmName); if (unregisterDisk != null) { unregisterDisk.getVms().add(vm); } } } private static ArchitectureType getOsSection(XmlDocument xmlDocument) { ArchitectureType archType = null; XmlNode content = xmlDocument.selectSingleNode("//*/Content"); XmlNodeList nodeList = content.selectNodes("Section"); XmlNode selectedSection = null; OsRepository osRepository = SimpleDependencyInjector.getInstance().get(OsRepository.class); if (nodeList != null) { for (XmlNode section : nodeList) { String value = section.attributes.get("xsi:type").getValue(); if (value.equals("ovf:OperatingSystemSection_Type")) { selectedSection = section; break; } } if (selectedSection != null) { int osId = osRepository.getOsIdByUniqueName(selectedSection.innerText); archType = osRepository.getArchitectureFromOS(osId); } } return archType; } }