From 41354227e27c8b2bf3aaa3f05deaa7e18446620c Mon Sep 17 00:00:00 2001 From: nvazquez Date: Fri, 25 Sep 2020 01:41:47 -0300 Subject: [PATCH] Handle guest OS read from deploy-as-is OVF descriptor --- .../cloud/agent/api/storage/OVFHelper.java | 49 ++- .../cloud/agent/api/to/OVFInformationTO.java | 95 ++++ .../OVFVirtualHardwareSectionTO.java | 9 +- .../cloud/deployasis/DeployAsIsConstants.java | 1 + .../user/template/RegisterTemplateCmd.java | 4 + .../agent/api/storage/OVFHelperTest.java | 415 +++++++++--------- .../agent/api/storage/DownloadAnswer.java | 57 +-- .../cloud/storage/template/OVAProcessor.java | 41 +- .../com/cloud/storage/template/Processor.java | 13 +- .../agent/api/storage/DownloadAnswerTest.java | 7 +- .../cloud/storage/dao/GuestOSCategoryDao.java | 1 + .../storage/dao/GuestOSCategoryDaoImpl.java | 9 +- .../storage/dao/GuestOSHypervisorDao.java | 7 + .../storage/dao/GuestOSHypervisorDaoImpl.java | 37 ++ .../META-INF/db/schema-41400to41500.sql | 5 +- .../deployasis/DeployAsIsHelperImpl.java | 212 ++++++++- .../cloud/template/TemplateAdapterBase.java | 8 + .../storage/template/DownloadManagerImpl.java | 81 +--- 18 files changed, 684 insertions(+), 367 deletions(-) create mode 100644 api/src/main/java/com/cloud/agent/api/to/OVFInformationTO.java diff --git a/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java b/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java index 45eff89b0c9..8d4f780efed 100644 --- a/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java +++ b/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java @@ -46,6 +46,7 @@ import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareItemTO; import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareSectionTO; import com.cloud.configuration.Resource.ResourceType; import com.cloud.exception.InternalErrorException; +import com.cloud.utils.Pair; import com.cloud.utils.compression.CompressionUtil; import com.cloud.agent.api.to.deployasis.OVFNetworkTO; import org.apache.commons.collections.CollectionUtils; @@ -217,6 +218,12 @@ public class OVFHelper { return getConfigurableOVFPropertiesFromDocument(doc); } + protected Pair getOperatingSystemInfoFromXmlString(final String ovfString) throws ParserConfigurationException, IOException, SAXException { + InputSource is = new InputSource(new StringReader(ovfString)); + final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is); + return getOperatingSystemInfoFromDocument(doc); + } + protected List getOVFDeploymentOptionsFromXmlString(final String ovfString) throws ParserConfigurationException, IOException, SAXException { InputSource is = new InputSource(new StringReader(ovfString)); final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is); @@ -229,6 +236,12 @@ public class OVFHelper { return getVirtualHardwareItemsFromDocumentTree(doc); } + protected OVFVirtualHardwareSectionTO getVirtualHardwareSectionFromXmlString(final String ovfString) throws ParserConfigurationException, IOException, SAXException { + InputSource is = new InputSource(new StringReader(ovfString)); + final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is); + return getVirtualHardwareSectionFromDocument(doc); + } + protected List getOVFEulaSectionFromXmlString(final String ovfString) throws ParserConfigurationException, IOException, SAXException { InputSource is = new InputSource(new StringReader(ovfString)); final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is); @@ -726,7 +739,26 @@ public class OVFHelper { if (CollectionUtils.isNotEmpty(items)) { commonItems = items.stream().filter(x -> StringUtils.isBlank(x.getConfigurationIds())).collect(Collectors.toList()); } - return new OVFVirtualHardwareSectionTO(configurations, commonItems); + String minimumHardwareVersion = getMinimumHardwareVersionFromDocumentTree(doc); + return new OVFVirtualHardwareSectionTO(configurations, commonItems, minimumHardwareVersion); + } + + private String getMinimumHardwareVersionFromDocumentTree(Document doc) { + String version = null; + if (doc != null) { + NodeList systemNodeList = doc.getElementsByTagName("System"); + if (systemNodeList.getLength() != 0) { + Node systemItem = systemNodeList.item(0); + String hardwareVersions = getChildNodeValue(systemItem, "VirtualSystemType"); + if (StringUtils.isNotBlank(hardwareVersions)) { + String[] versions = hardwareVersions.split(","); + // Order the hardware versions and retrieve the minimum version + List versionsList = Arrays.stream(versions).sorted().collect(Collectors.toList()); + version = versionsList.get(0); + } + } + } + return version; } private List getDeploymentOptionsFromDocumentTree(Document doc) { @@ -869,6 +901,21 @@ public class OVFHelper { return eulas; } + public Pair getOperatingSystemInfoFromDocument(Document doc) { + if (doc == null) { + return null; + } + NodeList guesOsList = doc.getElementsByTagName("OperatingSystemSection"); + if (guesOsList.getLength() == 0) { + return null; + } + Node guestOsNode = guesOsList.item(0); + Element guestOsElement = (Element) guestOsNode; + String osType = getNodeAttribute(guestOsElement, "vmw", "osType"); + String description = getChildNodeValue(guestOsNode, "Description"); + return new Pair<>(osType, description); + } + class OVFFile { // public String _href; diff --git a/api/src/main/java/com/cloud/agent/api/to/OVFInformationTO.java b/api/src/main/java/com/cloud/agent/api/to/OVFInformationTO.java new file mode 100644 index 00000000000..412d362441e --- /dev/null +++ b/api/src/main/java/com/cloud/agent/api/to/OVFInformationTO.java @@ -0,0 +1,95 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api.to; + +import com.cloud.agent.api.LogLevel; +import com.cloud.agent.api.to.deployasis.OVFEulaSectionTO; +import com.cloud.agent.api.to.deployasis.OVFNetworkTO; +import com.cloud.agent.api.to.deployasis.OVFPropertyTO; +import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareSectionTO; +import com.cloud.utils.Pair; + +import java.util.List; + +public class OVFInformationTO { + + @LogLevel(LogLevel.Log4jLevel.Off) + private List properties; + @LogLevel(LogLevel.Log4jLevel.Off) + private List networks; + @LogLevel(LogLevel.Log4jLevel.Off) + private List disks; + @LogLevel(LogLevel.Log4jLevel.Off) + private OVFVirtualHardwareSectionTO hardwareSection; + @LogLevel(LogLevel.Log4jLevel.Off) + private List eulaSections; + @LogLevel(LogLevel.Log4jLevel.Off) + private Pair guestOsInfo; + + public OVFInformationTO() { + } + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + + public List getNetworks() { + return networks; + } + + public void setNetworks(List networks) { + this.networks = networks; + } + + public List getDisks() { + return disks; + } + + public void setDisks(List disks) { + this.disks = disks; + } + + public OVFVirtualHardwareSectionTO getHardwareSection() { + return hardwareSection; + } + + public void setHardwareSection(OVFVirtualHardwareSectionTO hardwareSection) { + this.hardwareSection = hardwareSection; + } + + public List getEulaSections() { + return eulaSections; + } + + public void setEulaSections(List eulaSections) { + this.eulaSections = eulaSections; + } + + public Pair getGuestOsInfo() { + return guestOsInfo; + } + + public void setGuestOsInfo(Pair guestOsInfo) { + this.guestOsInfo = guestOsInfo; + } +} diff --git a/api/src/main/java/com/cloud/agent/api/to/deployasis/OVFVirtualHardwareSectionTO.java b/api/src/main/java/com/cloud/agent/api/to/deployasis/OVFVirtualHardwareSectionTO.java index 2698f8c5e18..4cdbf68ed9c 100644 --- a/api/src/main/java/com/cloud/agent/api/to/deployasis/OVFVirtualHardwareSectionTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/deployasis/OVFVirtualHardwareSectionTO.java @@ -25,12 +25,15 @@ public class OVFVirtualHardwareSectionTO implements TemplateDeployAsIsInformatio public OVFVirtualHardwareSectionTO() { } + private String minimiumHardwareVersion; private List configurations; private List commonHardwareItems; - public OVFVirtualHardwareSectionTO(List configurations, List commonHardwareItems) { + public OVFVirtualHardwareSectionTO(List configurations, List commonHardwareItems, + String minimumHardwareVersion) { this.configurations = configurations; this.commonHardwareItems = commonHardwareItems; + this.minimiumHardwareVersion = minimumHardwareVersion; } public List getConfigurations() { @@ -40,4 +43,8 @@ public class OVFVirtualHardwareSectionTO implements TemplateDeployAsIsInformatio public List getCommonHardwareItems() { return commonHardwareItems; } + + public String getMinimiumHardwareVersion() { + return minimiumHardwareVersion; + } } \ No newline at end of file diff --git a/api/src/main/java/com/cloud/deployasis/DeployAsIsConstants.java b/api/src/main/java/com/cloud/deployasis/DeployAsIsConstants.java index 3dc961b9de8..23d286f5a7b 100644 --- a/api/src/main/java/com/cloud/deployasis/DeployAsIsConstants.java +++ b/api/src/main/java/com/cloud/deployasis/DeployAsIsConstants.java @@ -24,4 +24,5 @@ public interface DeployAsIsConstants { String HARDWARE_ITEM_PREFIX = "hardware-item-"; String EULA_PREFIX = "eula-"; + String DEFAULT_GUEST_OS_DEPLOY_AS_IS = "OVF Configured OS"; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java index ca08c2a5ded..6decb945496 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java @@ -347,5 +347,9 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Parameter directdownload is only allowed for KVM templates"); } + + if (isDeployAsIs() && !getHypervisor().equalsIgnoreCase(Hypervisor.HypervisorType.VMware.toString())) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Parameter deployasis is only allowed for VMware templates"); + } } } diff --git a/api/src/test/java/com/cloud/agent/api/storage/OVFHelperTest.java b/api/src/test/java/com/cloud/agent/api/storage/OVFHelperTest.java index e28680784d7..5e7b7cf9bc9 100644 --- a/api/src/test/java/com/cloud/agent/api/storage/OVFHelperTest.java +++ b/api/src/test/java/com/cloud/agent/api/storage/OVFHelperTest.java @@ -20,6 +20,8 @@ import com.cloud.agent.api.to.deployasis.OVFConfigurationTO; import com.cloud.agent.api.to.deployasis.OVFEulaSectionTO; import com.cloud.agent.api.to.deployasis.OVFPropertyTO; import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareItemTO; +import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareSectionTO; +import com.cloud.utils.Pair; import org.junit.Assert; import org.junit.Test; import org.xml.sax.SAXException; @@ -62,204 +64,210 @@ public class OVFHelperTest { " "; private String ovfFileVirtualHardwareSection = + "\n" + + "\n" + + " The kind of installed guest operating system\n" + + " Other 2.6x Linux (64-bit)\n" + + "\n" + "\n" + - " Virtual hardware requirements\n" + - " \n" + - " Virtual Hardware Family\n" + - " 0\n" + - " ASAv\n" + - " vmx-08,vmx-09\n" + - " \n" + - " \n" + - " hertz * 10^6\n" + - " Number of Virtual CPUs\n" + - " 1 virtual CPU(s)\n" + - " 1\n" + - " 5000\n" + - " 1000\n" + - " 3\n" + - " 1\n" + - " \n" + - " \n" + - " hertz * 10^6\n" + - " Number of Virtual CPUs\n" + - " 4 virtual CPU(s)\n" + - " 1\n" + - " 20000\n" + - " 1000\n" + - " 3\n" + - " 4\n" + - " \n" + - " \n" + - " byte * 2^20\n" + - " Memory Size\n" + - " 2048MB of memory\n" + - " 2\n" + - " 2048\n" + - " 2048\n" + - " 4\n" + - " 2048\n" + - " \n" + - " \n" + - " byte * 2^20\n" + - " Memory Size\n" + - " 8192MB of memory\n" + - " 2\n" + - " 8192\n" + - " 8192\n" + - " 4\n" + - " 8192\n" + - " \n" + - " \n" + - " 0\n" + - " SCSI Controller\n" + - " SCSI controller 0\n" + - " 3\n" + - " lsilogic\n" + - " 6\n" + - " \n" + - " \n" + - " 0\n" + - " IDE Controller\n" + - " IDE 0\n" + - " 4\n" + - " 5\n" + - " \n" + - " \n" + - " 0\n" + - " true\n" + - " CD/DVD Drive\n" + - " 5\n" + - " 4\n" + - " 15\n" + - " \n" + - " \n" + - " 1\n" + - " true\n" + - " CD/DVD Drive\n" + - " ovf:/file/file3\n" + - " 18\n" + - " 4\n" + - " 15\n" + - " \n" + - " \n" + - " 7\n" + - " true\n" + - " Management0-0\n" + - " E1000 Ethernet adapter on \"Management Network\"\n" + - " Network adapter 1\n" + - " 6\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 0\n" + - " Hard disk 1\n" + - " ovf:/disk/vmdisk1\n" + - " 7\n" + - " 3\n" + - " 17\n" + - " \n" + - " \n" + - " 1\n" + - " Hard disk 2\n" + - " ovf:/disk/vmdisk2\n" + - " 8\n" + - " 3\n" + - " 17\n" + - " \n" + - " \n" + - " 8\n" + - " true\n" + - " GigabitEthernet0-0\n" + - " General purpose E1000 Ethernet adapter\n" + - " Network adapter 2\n" + - " 9\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 9\n" + - " true\n" + - " GigabitEthernet0-1\n" + - " General purpose E1000 Ethernet adapter\n" + - " Network adapter 3\n" + - " 10\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 10\n" + - " true\n" + - " GigabitEthernet0-2\n" + - " General purpose E1000 Ethernet adapter\n" + - " Network adapter 4\n" + - " 11\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 11\n" + - " true\n" + - " GigabitEthernet0-3\n" + - " General purpose E1000 Ethernet adapter\n" + - " Network adapter 5\n" + - " 12\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 12\n" + - " true\n" + - " GigabitEthernet0-4\n" + - " General purpose E1000 Ethernet adapter\n" + - " Network adapter 6\n" + - " 13\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 13\n" + - " true\n" + - " GigabitEthernet0-5\n" + - " General purpose E1000 Ethernet adapter\n" + - " Network adapter 7\n" + - " 14\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 14\n" + - " true\n" + - " GigabitEthernet0-6\n" + - " General purpose E1000 Ethernet adapter\n" + - " Network adapter 8\n" + - " 15\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 15\n" + - " true\n" + - " GigabitEthernet0-7\n" + - " General purpose E1000 Ethernet adapter\n" + - " Network adapter 9\n" + - " 16\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " 16\n" + - " true\n" + - " GigabitEthernet0-8\n" + - " Default HA failover E1000 Ethernet adapter, or additional standalone general purpose adapter\n" + - " Network adapter 10\n" + - " 17\n" + - " E1000\n" + - " 10\n" + - " \n" + - " \n" + - " "; + " Virtual hardware requirements\n" + + " \n" + + " Virtual Hardware Family\n" + + " 0\n" + + " ASAv\n" + + " vmx-08,vmx-09\n" + + " \n" + + " \n" + + " hertz * 10^6\n" + + " Number of Virtual CPUs\n" + + " 1 virtual CPU(s)\n" + + " 1\n" + + " 5000\n" + + " 1000\n" + + " 3\n" + + " 1\n" + + " \n" + + " \n" + + " hertz * 10^6\n" + + " Number of Virtual CPUs\n" + + " 4 virtual CPU(s)\n" + + " 1\n" + + " 20000\n" + + " 1000\n" + + " 3\n" + + " 4\n" + + " \n" + + " \n" + + " byte * 2^20\n" + + " Memory Size\n" + + " 2048MB of memory\n" + + " 2\n" + + " 2048\n" + + " 2048\n" + + " 4\n" + + " 2048\n" + + " \n" + + " \n" + + " byte * 2^20\n" + + " Memory Size\n" + + " 8192MB of memory\n" + + " 2\n" + + " 8192\n" + + " 8192\n" + + " 4\n" + + " 8192\n" + + " \n" + + " \n" + + " 0\n" + + " SCSI Controller\n" + + " SCSI controller 0\n" + + " 3\n" + + " lsilogic\n" + + " 6\n" + + " \n" + + " \n" + + " 0\n" + + " IDE Controller\n" + + " IDE 0\n" + + " 4\n" + + " 5\n" + + " \n" + + " \n" + + " 0\n" + + " true\n" + + " CD/DVD Drive\n" + + " 5\n" + + " 4\n" + + " 15\n" + + " \n" + + " \n" + + " 1\n" + + " true\n" + + " CD/DVD Drive\n" + + " ovf:/file/file3\n" + + " 18\n" + + " 4\n" + + " 15\n" + + " \n" + + " \n" + + " 7\n" + + " true\n" + + " Management0-0\n" + + " E1000 Ethernet adapter on \"Management Network\"\n" + + " Network adapter 1\n" + + " 6\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 0\n" + + " Hard disk 1\n" + + " ovf:/disk/vmdisk1\n" + + " 7\n" + + " 3\n" + + " 17\n" + + " \n" + + " \n" + + " 1\n" + + " Hard disk 2\n" + + " ovf:/disk/vmdisk2\n" + + " 8\n" + + " 3\n" + + " 17\n" + + " \n" + + " \n" + + " 8\n" + + " true\n" + + " GigabitEthernet0-0\n" + + " General purpose E1000 Ethernet adapter\n" + + " Network adapter 2\n" + + " 9\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 9\n" + + " true\n" + + " GigabitEthernet0-1\n" + + " General purpose E1000 Ethernet adapter\n" + + " Network adapter 3\n" + + " 10\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 10\n" + + " true\n" + + " GigabitEthernet0-2\n" + + " General purpose E1000 Ethernet adapter\n" + + " Network adapter 4\n" + + " 11\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 11\n" + + " true\n" + + " GigabitEthernet0-3\n" + + " General purpose E1000 Ethernet adapter\n" + + " Network adapter 5\n" + + " 12\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 12\n" + + " true\n" + + " GigabitEthernet0-4\n" + + " General purpose E1000 Ethernet adapter\n" + + " Network adapter 6\n" + + " 13\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 13\n" + + " true\n" + + " GigabitEthernet0-5\n" + + " General purpose E1000 Ethernet adapter\n" + + " Network adapter 7\n" + + " 14\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 14\n" + + " true\n" + + " GigabitEthernet0-6\n" + + " General purpose E1000 Ethernet adapter\n" + + " Network adapter 8\n" + + " 15\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 15\n" + + " true\n" + + " GigabitEthernet0-7\n" + + " General purpose E1000 Ethernet adapter\n" + + " Network adapter 9\n" + + " 16\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " 16\n" + + " true\n" + + " GigabitEthernet0-8\n" + + " Default HA failover E1000 Ethernet adapter, or additional standalone general purpose adapter\n" + + " Network adapter 10\n" + + " 17\n" + + " E1000\n" + + " 10\n" + + " \n" + + " \n" + + " \n" + + ""; private String eulaSections = "\n" + @@ -729,4 +737,17 @@ public class OVFHelperTest { List props = ovfHelper.getOVFPropertiesFromXmlString(productSectionWithCategories); Assert.assertEquals(18, props.size()); } + + @Test + public void testGetOperatingSystemInfo() throws IOException, SAXException, ParserConfigurationException { + Pair guestOsPair = ovfHelper.getOperatingSystemInfoFromXmlString(ovfFileVirtualHardwareSection); + Assert.assertEquals("other26xLinux64Guest", guestOsPair.first()); + Assert.assertEquals("Other 2.6x Linux (64-bit)", guestOsPair.second()); + } + + @Test + public void testGetMinimumHardwareVersion() throws IOException, SAXException, ParserConfigurationException { + OVFVirtualHardwareSectionTO hardwareSection = ovfHelper.getVirtualHardwareSectionFromXmlString(ovfFileVirtualHardwareSection); + Assert.assertEquals("vmx-08", hardwareSection.getMinimiumHardwareVersion()); + } } diff --git a/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java index 9ae1d6dcca5..0c6373134b1 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java @@ -20,18 +20,13 @@ package com.cloud.agent.api.storage; import java.io.File; -import java.util.List; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.LogLevel; -import com.cloud.agent.api.to.DatadiskTO; -import com.cloud.agent.api.to.deployasis.OVFEulaSectionTO; -import com.cloud.agent.api.to.deployasis.OVFPropertyTO; -import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareSectionTO; +import com.cloud.agent.api.to.OVFInformationTO; import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; -import com.cloud.agent.api.to.deployasis.OVFNetworkTO; public class DownloadAnswer extends Answer { private String jobId; @@ -45,15 +40,7 @@ public class DownloadAnswer extends Answer { private String checkSum; @LogLevel(LogLevel.Log4jLevel.Off) - private List ovfProperties; - @LogLevel(LogLevel.Log4jLevel.Off) - private List networkRequirements; - @LogLevel(LogLevel.Log4jLevel.Off) - private List disks; - @LogLevel(LogLevel.Log4jLevel.Off) - private OVFVirtualHardwareSectionTO ovfHardwareSection; - @LogLevel(LogLevel.Log4jLevel.Off) - private List eulaSections; + private OVFInformationTO ovfInformationTO; public String getCheckSum() { return checkSum; @@ -164,43 +151,11 @@ public class DownloadAnswer extends Answer { return templatePhySicalSize; } - public List getOvfProperties() { - return ovfProperties; + public OVFInformationTO getOvfInformationTO() { + return ovfInformationTO; } - public void setOvfProperties(List ovfProperties) { - this.ovfProperties = ovfProperties; - } - - public List getNetworkRequirements() { - return networkRequirements; - } - - public void setNetworkRequirements(List networkRequirements) { - this.networkRequirements = networkRequirements; - } - - public List getDisks() { - return disks; - } - - public void setDisks(List disks) { - this.disks = disks; - } - - public OVFVirtualHardwareSectionTO getOvfHardwareSection() { - return ovfHardwareSection; - } - - public void setOvfHardwareSection(OVFVirtualHardwareSectionTO ovfHardwareSection) { - this.ovfHardwareSection = ovfHardwareSection; - } - - public List getEulaSections() { - return eulaSections; - } - - public void setEulaSections(List eulaSections) { - this.eulaSections = eulaSections; + public void setOvfInformationTO(OVFInformationTO ovfInformationTO) { + this.ovfInformationTO = ovfInformationTO; } } diff --git a/core/src/main/java/com/cloud/storage/template/OVAProcessor.java b/core/src/main/java/com/cloud/storage/template/OVAProcessor.java index e980293b750..8315ff4ff91 100644 --- a/core/src/main/java/com/cloud/storage/template/OVAProcessor.java +++ b/core/src/main/java/com/cloud/storage/template/OVAProcessor.java @@ -28,6 +28,7 @@ import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import com.cloud.agent.api.to.OVFInformationTO; import com.cloud.agent.api.to.deployasis.OVFConfigurationTO; import com.cloud.agent.api.to.deployasis.OVFEulaSectionTO; import com.cloud.agent.api.to.deployasis.OVFPropertyTO; @@ -35,6 +36,7 @@ import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareItemTO; import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareSectionTO; import com.cloud.agent.api.to.deployasis.OVFNetworkTO; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -107,30 +109,34 @@ public class OVAProcessor extends AdapterBase implements Processor { OVFHelper ovfHelper = new OVFHelper(); Document doc = ovfHelper.getDocumentFromFile(ovfFilePath); - List disks = ovfHelper.getOVFVolumeInfoFromFile(ovfFilePath, doc, null); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace(String.format("Found %d disks in template %s", CollectionUtils.isNotEmpty(disks) ? disks.size() : 0, ovfFilePath)); - } - if (CollectionUtils.isNotEmpty(disks)) { - info.disks = disks; - } + OVFInformationTO ovfInformationTO = createOvfInformationTO(ovfHelper, doc, ovfFilePath); + info.ovfInformationTO = ovfInformationTO; + } + private OVFInformationTO createOvfInformationTO(OVFHelper ovfHelper, Document doc, String ovfFilePath) throws InternalErrorException { + OVFInformationTO ovfInformationTO = new OVFInformationTO(); + + List disks = ovfHelper.getOVFVolumeInfoFromFile(ovfFilePath, doc, null); + if (CollectionUtils.isNotEmpty(disks)) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace(String.format("Found %d disks in template %s", disks.size(), ovfFilePath)); + } + ovfInformationTO.setDisks(disks); + } List nets = ovfHelper.getNetPrerequisitesFromDocument(doc); if (CollectionUtils.isNotEmpty(nets)) { LOGGER.info("Found " + nets.size() + " prerequisite networks"); - info.networks = nets; + ovfInformationTO.setNetworks(nets); } else if (LOGGER.isTraceEnabled()) { LOGGER.trace(String.format("no net prerequisites found in template %s", ovfFilePath)); } - List ovfProperties = ovfHelper.getConfigurableOVFPropertiesFromDocument(doc); if (CollectionUtils.isNotEmpty(ovfProperties)) { LOGGER.info("Found " + ovfProperties.size() + " configurable OVF properties"); - info.ovfProperties = ovfProperties; + ovfInformationTO.setProperties(ovfProperties); } else if (LOGGER.isTraceEnabled()) { LOGGER.trace(String.format("no ovf properties found in template %s", ovfFilePath)); } - OVFVirtualHardwareSectionTO hardwareSection = ovfHelper.getVirtualHardwareSectionFromDocument(doc); List configurations = hardwareSection.getConfigurations(); if (CollectionUtils.isNotEmpty(configurations)) { @@ -140,12 +146,21 @@ public class OVAProcessor extends AdapterBase implements Processor { if (CollectionUtils.isNotEmpty(hardwareItems)) { LOGGER.info("Found " + hardwareItems.size() + " virtual hardware items"); } - info.hardwareSection = hardwareSection; + if (StringUtils.isNotBlank(hardwareSection.getMinimiumHardwareVersion())) { + LOGGER.info("Found minimum hardware version " + hardwareSection.getMinimiumHardwareVersion()); + } + ovfInformationTO.setHardwareSection(hardwareSection); List eulaSections = ovfHelper.getEulaSectionsFromDocument(doc); if (CollectionUtils.isNotEmpty(eulaSections)) { LOGGER.info("Found " + eulaSections.size() + " license agreements"); - info.eulaSections = eulaSections; + ovfInformationTO.setEulaSections(eulaSections); } + Pair guestOsPair = ovfHelper.getOperatingSystemInfoFromDocument(doc); + if (guestOsPair != null) { + LOGGER.info("Found guest OS information: " + guestOsPair.first() + " - " + guestOsPair.second()); + ovfInformationTO.setGuestOsInfo(guestOsPair); + } + return ovfInformationTO; } private void setFileSystemAccessRights(String templatePath) { diff --git a/core/src/main/java/com/cloud/storage/template/Processor.java b/core/src/main/java/com/cloud/storage/template/Processor.java index 15a3dec0c6e..d8c0dfb94ec 100644 --- a/core/src/main/java/com/cloud/storage/template/Processor.java +++ b/core/src/main/java/com/cloud/storage/template/Processor.java @@ -21,16 +21,11 @@ package com.cloud.storage.template; import java.io.File; import java.io.IOException; -import java.util.List; -import com.cloud.agent.api.to.deployasis.OVFEulaSectionTO; -import com.cloud.agent.api.to.deployasis.OVFPropertyTO; -import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareSectionTO; -import com.cloud.agent.api.to.DatadiskTO; +import com.cloud.agent.api.to.OVFInformationTO; import com.cloud.exception.InternalErrorException; import com.cloud.storage.Storage.ImageFormat; import com.cloud.utils.component.Adapter; -import com.cloud.agent.api.to.deployasis.OVFNetworkTO; /** * Generic interface to process different types of image formats @@ -58,11 +53,7 @@ public interface Processor extends Adapter { public long virtualSize; public String filename; public boolean isCorrupted; - public List ovfProperties; - public List networks; - public List disks; - public OVFVirtualHardwareSectionTO hardwareSection; - public List eulaSections; + public OVFInformationTO ovfInformationTO; } long getVirtualSize(File file) throws IOException; diff --git a/core/src/test/java/com/cloud/agent/api/storage/DownloadAnswerTest.java b/core/src/test/java/com/cloud/agent/api/storage/DownloadAnswerTest.java index c7dcc22572a..ded45f819f3 100644 --- a/core/src/test/java/com/cloud/agent/api/storage/DownloadAnswerTest.java +++ b/core/src/test/java/com/cloud/agent/api/storage/DownloadAnswerTest.java @@ -17,6 +17,7 @@ package com.cloud.agent.api.storage; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.OVFInformationTO; import com.cloud.agent.api.to.deployasis.OVFPropertyTO; import com.cloud.serializer.GsonHelper; import com.cloud.storage.VMTemplateStorageResourceAssoc; @@ -49,8 +50,10 @@ public class DownloadAnswerTest { List networks = new ArrayList<>(); networks.add(new OVFNetworkTO()); - answer.setOvfProperties(properties); - answer.setNetworkRequirements(networks); + OVFInformationTO informationTO = new OVFInformationTO(); + informationTO.setProperties(properties); + informationTO.setNetworks(networks); + answer.setOvfInformationTO(informationTO); String json = gson.toJson(answer); Answer received = gson.fromJson(json, Answer.class); diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDao.java index 61fec36d526..d98ef23857b 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDao.java @@ -21,4 +21,5 @@ import com.cloud.utils.db.GenericDao; public interface GuestOSCategoryDao extends GenericDao { + GuestOSCategoryVO findByCategoryName(String categoryName); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java index 8ffb54a9873..6fad6c5c47e 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java @@ -16,7 +16,8 @@ // under the License. package com.cloud.storage.dao; - +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchCriteria; import org.springframework.stereotype.Component; import com.cloud.storage.GuestOSCategoryVO; @@ -29,4 +30,10 @@ public class GuestOSCategoryDaoImpl extends GenericDaoBase sc = QueryBuilder.create(GuestOSCategoryVO.class); + sc.and(sc.entity().getName(), SearchCriteria.Op.EQ, categoryName); + return sc.find(); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java index cd00703ce13..41b95718a0a 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java @@ -20,6 +20,8 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.GuestOSHypervisorVO; import com.cloud.utils.db.GenericDao; +import java.util.List; + public interface GuestOSHypervisorDao extends GenericDao { HypervisorType findHypervisorTypeByGuestOsId(long guestOsId); @@ -31,4 +33,9 @@ public interface GuestOSHypervisorDao extends GenericDao listByOsNameAndHypervisorMinimumVersion(String guestOsName, String hypervisorType, + String minHypervisorVersion); + + List listHypervisorSupportedVersionsFromMinimumVersion(String hypervisorType, String hypervisorVersion); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java index 699ce0bae02..add4bfc3e7d 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java @@ -16,9 +16,11 @@ // under the License. package com.cloud.storage.dao; +import java.util.ArrayList; import java.util.Date; import java.util.List; +import com.cloud.utils.db.QueryBuilder; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -36,6 +38,7 @@ public class GuestOSHypervisorDaoImpl extends GenericDaoBase mappingSearch; protected final SearchBuilder userDefinedMappingSearch; protected final SearchBuilder guestOsNameSearch; + protected final SearchBuilder availableHypervisorVersionSearch; protected GuestOSHypervisorDaoImpl() { guestOsSearch = createSearchBuilder(); @@ -60,6 +63,15 @@ public class GuestOSHypervisorDaoImpl extends GenericDaoBase listByOsNameAndHypervisorMinimumVersion(String guestOsName, String hypervisorType, + String minHypervisorVersion) { + final QueryBuilder sc = QueryBuilder.create(GuestOSHypervisorVO.class); + sc.and(sc.entity().getGuestOsName(), SearchCriteria.Op.EQ, guestOsName); + sc.and(sc.entity().getHypervisorType(), SearchCriteria.Op.EQ, hypervisorType); + sc.and(sc.entity().getHypervisorVersion(), SearchCriteria.Op.GTEQ, minHypervisorVersion); + sc.and(sc.entity().getHypervisorVersion(), SearchCriteria.Op.NEQ, "default"); + return sc.list(); + } + + @Override + public List listHypervisorSupportedVersionsFromMinimumVersion(String hypervisorType, String hypervisorVersion) { + List versions = new ArrayList<>(); + SearchCriteria sc = availableHypervisorVersionSearch.create(); + sc.setParameters("hypervisor_type", hypervisorType); + sc.setParameters("hypervisor_version", hypervisorVersion); + Filter filter = new Filter(GuestOSHypervisorVO.class, "hypervisorVersion", true, null, null); + List mappings = listBy(sc, filter); + for (GuestOSHypervisorVO mapping : mappings) { + versions.add(mapping.getHypervisorVersion()); + } + return versions; + } + } diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41400to41500.sql b/engine/schema/src/main/resources/META-INF/db/schema-41400to41500.sql index a868e2a21ea..dd7bfee314c 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41400to41500.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41400to41500.sql @@ -515,4 +515,7 @@ ALTER VIEW `cloud`.`image_store_view` AS left join `cloud`.`data_center` ON image_store.data_center_id = data_center.id left join - `cloud`.`image_store_details` ON image_store_details.store_id = image_store.id; \ No newline at end of file + `cloud`.`image_store_details` ON image_store_details.store_id = image_store.id; + +-- OVF configured OS while registering deploy-as-is templates +INSERT IGNORE INTO `cloud`.`guest_os` (uuid, category_id, display_name, created) VALUES (UUID(), 11, 'OVF Configured OS', now()); diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/deployasis/DeployAsIsHelperImpl.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/deployasis/DeployAsIsHelperImpl.java index 7e431b1a729..c71fc4da439 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/deployasis/DeployAsIsHelperImpl.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/deployasis/DeployAsIsHelperImpl.java @@ -23,6 +23,7 @@ import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.api.to.OVFInformationTO; import com.cloud.agent.api.to.deployasis.OVFConfigurationTO; import com.cloud.agent.api.to.deployasis.OVFEulaSectionTO; import com.cloud.agent.api.to.deployasis.OVFPropertyTO; @@ -34,11 +35,24 @@ import com.cloud.deployasis.TemplateDeployAsIsDetailVO; import com.cloud.deployasis.UserVmDeployAsIsDetailVO; import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao; import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.GuestOSCategoryVO; +import com.cloud.storage.GuestOSHypervisorVO; +import com.cloud.storage.GuestOSVO; import com.cloud.storage.VMTemplateStoragePoolVO; +import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; +import com.cloud.storage.dao.GuestOSCategoryDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.GuestOSHypervisorDao; +import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplatePoolDao; +import com.cloud.utils.Pair; import com.cloud.utils.compression.CompressionUtil; import com.cloud.utils.crypt.DBEncryptionUtil; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; import com.cloud.vm.VirtualMachineProfile; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -47,14 +61,18 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import javax.inject.Inject; import java.io.IOException; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; @Component public class DeployAsIsHelperImpl implements DeployAsIsHelper { @@ -70,6 +88,14 @@ public class DeployAsIsHelperImpl implements DeployAsIsHelper { private PrimaryDataStoreDao storagePoolDao; @Inject private VMTemplatePoolDao templateStoragePoolDao; + @Inject + private VMTemplateDao templateDao; + @Inject + private GuestOSDao guestOSDao; + @Inject + private GuestOSHypervisorDao guestOSHypervisorDao; + @Inject + private GuestOSCategoryDao guestOSCategoryDao; static { GsonBuilder builder = new GsonBuilder(); @@ -78,30 +104,180 @@ public class DeployAsIsHelperImpl implements DeployAsIsHelper { } public void persistTemplateDeployAsIsDetails(long templateId, DownloadAnswer answer) { - List ovfProperties = answer.getOvfProperties(); - List networkRequirements = answer.getNetworkRequirements(); - OVFVirtualHardwareSectionTO ovfHardwareSection = answer.getOvfHardwareSection(); - List eulaSections = answer.getEulaSections(); + OVFInformationTO ovfInformationTO = answer.getOvfInformationTO(); + if (ovfInformationTO != null) { + List ovfProperties = ovfInformationTO.getProperties(); + List networkRequirements = ovfInformationTO.getNetworks(); + OVFVirtualHardwareSectionTO ovfHardwareSection = ovfInformationTO.getHardwareSection(); + List eulaSections = ovfInformationTO.getEulaSections(); + Pair guestOsInfo = ovfInformationTO.getGuestOsInfo(); - if (CollectionUtils.isNotEmpty(ovfProperties)) { - persistTemplateDeployAsIsInformationTOList(templateId, ovfProperties); - } - if (CollectionUtils.isNotEmpty(networkRequirements)) { - persistTemplateDeployAsIsInformationTOList(templateId, networkRequirements); - } - if (CollectionUtils.isNotEmpty(eulaSections)) { - persistTemplateDeployAsIsInformationTOList(templateId, eulaSections); - } - if (ovfHardwareSection != null) { - if (CollectionUtils.isNotEmpty(ovfHardwareSection.getConfigurations())) { - persistTemplateDeployAsIsInformationTOList(templateId, ovfHardwareSection.getConfigurations()); + if (CollectionUtils.isNotEmpty(ovfProperties)) { + persistTemplateDeployAsIsInformationTOList(templateId, ovfProperties); } - if (CollectionUtils.isNotEmpty(ovfHardwareSection.getCommonHardwareItems())) { - persistTemplateDeployAsIsInformationTOList(templateId, ovfHardwareSection.getCommonHardwareItems()); + if (CollectionUtils.isNotEmpty(networkRequirements)) { + persistTemplateDeployAsIsInformationTOList(templateId, networkRequirements); + } + if (CollectionUtils.isNotEmpty(eulaSections)) { + persistTemplateDeployAsIsInformationTOList(templateId, eulaSections); + } + String minimumHardwareVersion = null; + if (ovfHardwareSection != null) { + if (CollectionUtils.isNotEmpty(ovfHardwareSection.getConfigurations())) { + persistTemplateDeployAsIsInformationTOList(templateId, ovfHardwareSection.getConfigurations()); + } + if (CollectionUtils.isNotEmpty(ovfHardwareSection.getCommonHardwareItems())) { + persistTemplateDeployAsIsInformationTOList(templateId, ovfHardwareSection.getCommonHardwareItems()); + } + minimumHardwareVersion = ovfHardwareSection.getMinimiumHardwareVersion(); + } + if (guestOsInfo != null) { + String osType = guestOsInfo.first(); + String osDescription = guestOsInfo.second(); + LOGGER.info("Guest OS information retrieved from the template: " + osType + " - " + osDescription); + try { + handleGuestOsFromOVFDescriptor(templateId, osType, osDescription, minimumHardwareVersion); + } catch (Exception e) { + LOGGER.error("Error handling the guest OS read from the OVF " + osType, e); + } } } } + /** + * Handle the guest OS read from the OVF and try to match it to an existing guest OS in DB. + * If the guest OS cannot be mapped to an existing guest OS in DB, then create it and create support for hypervisor versions. + * Roll back actions in case of unexpected erros + */ + private void handleGuestOsFromOVFDescriptor(long templateId, String guestOsType, String guestOsDescription, + String minimumHardwareVersion) { + VMTemplateVO template = templateDao.findById(templateId); + Hypervisor.HypervisorType hypervisor = template.getHypervisorType(); + if (hypervisor != Hypervisor.HypervisorType.VMware) { + return; + } + String minimumHypervisorVersion = getMinimumSupportedHypervisorVersionForHardwareVersion(minimumHardwareVersion); + LOGGER.info("Minimum hardware version " + minimumHardwareVersion + " matched to hypervisor version " + minimumHypervisorVersion + ". " + + "Checking guest OS supporting this version"); + + List guestOsMappings = guestOSHypervisorDao.listByOsNameAndHypervisorMinimumVersion(guestOsType, + hypervisor.toString(), minimumHypervisorVersion); + + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + if (CollectionUtils.isNotEmpty(guestOsMappings)) { + Pair> result = updateDeployAsIsTemplateToExistingGuestOs(template, guestOsMappings, guestOsDescription); + if (!result.first()) { + // If couldnt find a guest OS matching the display name, create a new guest OS + updateDeployAsIsTemplateToNewGuestOs(template, guestOsType, guestOsDescription, hypervisor, result.second()); + } + } else { + // The guest OS is not present in DB, create a new guest OS entry and mappings for supported versions + List hypervisorVersions = guestOSHypervisorDao.listHypervisorSupportedVersionsFromMinimumVersion( + hypervisor.toString(), minimumHypervisorVersion); + updateDeployAsIsTemplateToNewGuestOs(template, guestOsType, guestOsDescription, hypervisor, hypervisorVersions); + } + } + }); + } + + /** + * Return true if the template guest OS can be updated to an existing guest OS matching the display name to + * the guest OS description from the OVF. (the second element of the returned pair is not used) + * If the guest OS does not exist, return false and the hypervisor versions that the guest OS must support + */ + private Pair> updateDeployAsIsTemplateToExistingGuestOs(VMTemplateVO template, + List guestOsMappings, + String guestOsDescription) { + Set versionsToSupport = new HashSet<>(); + for (GuestOSHypervisorVO mapping : guestOsMappings) { + long guestOsId = mapping.getGuestOsId(); + GuestOSVO guestOs = guestOSDao.findById(guestOsId); + versionsToSupport.add(mapping.getHypervisorVersion()); + if (guestOs.getDisplayName().equalsIgnoreCase(guestOsDescription)) { + LOGGER.info("Found guest OS mapping for OVF read guest OS " + guestOsDescription + + " to hypervisor version " + mapping.getHypervisorVersion()); + LOGGER.info("Updating deploy-as-is template guest OS to " + guestOs.getDisplayName()); + updateTemplateGuestOsId(template, guestOsId); + return new Pair<>(true, versionsToSupport); + } + } + LOGGER.info("Could not map the OVF read guest OS " + guestOsDescription + " to an existing guest OS"); + return new Pair<>(false, versionsToSupport); + } + + /** + * Updates the deploy-as-is template guest OS doing: + * - Create a new guest OS with the guest OS description parsed from the OVF + * - Create mappings for the new guest OS and supported hypervisor versions + * - Update the template guest OS ID to the new guest OS ID + */ + private void updateDeployAsIsTemplateToNewGuestOs(VMTemplateVO template, String guestOsType, String guestOsDescription, + Hypervisor.HypervisorType hypervisor, Collection hypervisorVersions) { + GuestOSVO newGuestOs = createGuestOsEntry(guestOsDescription); + for (String hypervisorVersion : hypervisorVersions) { + LOGGER.info(String.format("Adding a new guest OS mapping for hypervisor: %s version: %s and " + + "guest OS: %s", hypervisor.toString(), hypervisorVersion, guestOsType)); + createGuestOsHypervisorMapping(newGuestOs.getId(), guestOsType, hypervisor.toString(), hypervisorVersion); + } + updateTemplateGuestOsId(template, newGuestOs.getId()); + } + + private void updateTemplateGuestOsId(VMTemplateVO template, long guestOsId) { + template.setGuestOSId(guestOsId); + templateDao.update(template.getId(), template); + } + + /** + * Create a new entry on guest_os_hypervisor + */ + private void createGuestOsHypervisorMapping(long guestOsId, String guestOsType, String hypervisorType, String hypervisorVersion) { + GuestOSHypervisorVO mappingVO = new GuestOSHypervisorVO(); + mappingVO.setGuestOsId(guestOsId); + mappingVO.setHypervisorType(hypervisorType); + mappingVO.setHypervisorVersion(hypervisorVersion); + mappingVO.setGuestOsName(guestOsType); + guestOSHypervisorDao.persist(mappingVO); + } + + /** + * Create new guest OS entry with category 'Other' + */ + private GuestOSVO createGuestOsEntry(String guestOsDescription) { + GuestOSCategoryVO categoryVO = guestOSCategoryDao.findByCategoryName("Other"); + long categoryId = categoryVO != null ? categoryVO.getId() : 7L; + GuestOSVO guestOSVO = new GuestOSVO(); + guestOSVO.setDisplayName(guestOsDescription); + guestOSVO.setCategoryId(categoryId); + return guestOSDao.persist(guestOSVO); + } + + /** + * Minimum VMware hosts supported version is 6.0 + */ + protected String getMinimumSupportedHypervisorVersionForHardwareVersion(String hardwareVersion) { + // From https://kb.vmware.com/s/article/1003746 and https://kb.vmware.com/s/article/2007240 + String hypervisorVersion = "default"; + if (StringUtils.isBlank(hardwareVersion)) { + return hypervisorVersion; + } + String hwVersion = hardwareVersion.replace("vmx-", ""); + try { + int hwVersionNumber = Integer.parseInt(hwVersion); + if (hwVersionNumber <= 11) { + hypervisorVersion = "6.0"; + } else if (hwVersionNumber == 13) { + hypervisorVersion = "6.5"; + } else if (hwVersionNumber >= 14) { + hypervisorVersion = "6.7"; + } + } catch (NumberFormatException e) { + LOGGER.error("Cannot parse hardware version " + hwVersion + " to integer. Using default hypervisor version", e); + } + return hypervisorVersion; + } + @Override public Map getVirtualMachineDeployAsIsProperties(VirtualMachineProfile vm) { Map map = new HashMap<>(); diff --git a/server/src/main/java/com/cloud/template/TemplateAdapterBase.java b/server/src/main/java/com/cloud/template/TemplateAdapterBase.java index d016fed4a2e..45a10a8c4b3 100644 --- a/server/src/main/java/com/cloud/template/TemplateAdapterBase.java +++ b/server/src/main/java/com/cloud/template/TemplateAdapterBase.java @@ -23,6 +23,7 @@ import java.util.Map; import javax.inject.Inject; +import com.cloud.deployasis.DeployAsIsConstants; import com.cloud.storage.upload.params.IsoUploadParams; import com.cloud.storage.upload.params.TemplateUploadParams; import com.cloud.storage.upload.params.UploadParams; @@ -167,6 +168,13 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat if (requiresHVM == null) { requiresHVM = true; } + if (deployAsIs) { + GuestOS deployAsIsGuestOs = ApiDBUtils.findGuestOSByDisplayName(DeployAsIsConstants.DEFAULT_GUEST_OS_DEPLOY_AS_IS); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Setting default guest OS for deploy-as-is template while the template registration is not completed"); + } + guestOSId = deployAsIsGuestOs.getId(); + } } if (isExtractable == null) { diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/DownloadManagerImpl.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/DownloadManagerImpl.java index d60d7b8fb20..7ce252a8ed7 100644 --- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/DownloadManagerImpl.java +++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/DownloadManagerImpl.java @@ -37,10 +37,7 @@ import java.util.concurrent.Executors; import javax.naming.ConfigurationException; -import com.cloud.agent.api.to.deployasis.OVFEulaSectionTO; -import com.cloud.agent.api.to.deployasis.OVFVirtualHardwareSectionTO; -import com.cloud.agent.api.to.DatadiskTO; -import com.cloud.agent.api.to.deployasis.OVFPropertyTO; +import com.cloud.agent.api.to.OVFInformationTO; import com.cloud.storage.template.Processor; import com.cloud.storage.template.S3TemplateDownloader; import com.cloud.storage.template.TemplateDownloader; @@ -58,7 +55,6 @@ import com.cloud.storage.template.RawImageProcessor; import com.cloud.storage.template.TARProcessor; import com.cloud.storage.template.VhdProcessor; import com.cloud.storage.template.TemplateConstants; -import com.cloud.agent.api.to.deployasis.OVFNetworkTO; import org.apache.cloudstack.storage.command.DownloadCommand; import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType; import org.apache.cloudstack.storage.command.DownloadProgressCommand; @@ -66,7 +62,6 @@ import org.apache.cloudstack.storage.command.DownloadProgressCommand.RequestType import org.apache.cloudstack.storage.NfsMountManagerImpl.PathParser; import org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource; import org.apache.cloudstack.storage.resource.SecondaryStorageResource; -import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import com.cloud.agent.api.storage.DownloadAnswer; @@ -131,11 +126,7 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager private long templatePhysicalSize; private final long id; private final ResourceType resourceType; - private List ovfProperties; - private List networks; - private List disks; - private OVFVirtualHardwareSectionTO hardwareSection; - private List eulaSections; + private OVFInformationTO ovfInformationTO; public DownloadJob(TemplateDownloader td, String jobId, long id, String tmpltName, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, String installPathPrefix, ResourceType resourceType) { @@ -231,44 +222,12 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager this.checksum = checksum; } - public List getOvfProperties() { - return ovfProperties; + public OVFInformationTO getOvfInformationTO() { + return ovfInformationTO; } - public void setOvfProperties(List ovfProperties) { - this.ovfProperties = ovfProperties; - } - - public List getNetworks() { - return networks; - } - - public void setNetworks(List networks) { - this.networks = networks; - } - - public List getDisks() { - return disks; - } - - public void setDisks(List disks) { - this.disks = disks; - } - - public void setVirtualHardwareSection(OVFVirtualHardwareSectionTO section) { - this.hardwareSection = section; - } - - public OVFVirtualHardwareSectionTO getVirtualHardwareSection() { - return this.hardwareSection; - } - - public List getEulaSections() { - return eulaSections; - } - - public void setEulaSections(List eulaSections) { - this.eulaSections = eulaSections; + public void setOvfInformationTO(OVFInformationTO ovfInformationTO) { + this.ovfInformationTO = ovfInformationTO; } } @@ -563,18 +522,8 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager } dnld.setTemplatesize(info.virtualSize); dnld.setTemplatePhysicalSize(info.size); - if (CollectionUtils.isNotEmpty(info.ovfProperties)) { - dnld.setOvfProperties(info.ovfProperties); - } - if (CollectionUtils.isNotEmpty(info.networks)) { - dnld.setNetworks(info.networks); - } - if (CollectionUtils.isNotEmpty(info.disks)) { - dnld.setDisks(info.disks); - } - dnld.setVirtualHardwareSection(info.hardwareSection); - if (CollectionUtils.isNotEmpty(info.eulaSections)) { - dnld.setEulaSections(info.eulaSections); + if (info.ovfInformationTO != null) { + dnld.setOvfInformationTO(info.ovfInformationTO); } break; } @@ -876,18 +825,8 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager answer = new DownloadAnswer(jobId, getDownloadPct(jobId), getDownloadError(jobId), getDownloadStatus2(jobId), getDownloadLocalPath(jobId), getInstallPath(jobId), getDownloadTemplateSize(jobId), getDownloadTemplatePhysicalSize(jobId), getDownloadCheckSum(jobId)); - if (CollectionUtils.isNotEmpty(dj.getOvfProperties())) { - answer.setOvfProperties(dj.getOvfProperties()); - } - if (CollectionUtils.isNotEmpty(dj.getNetworks())) { - answer.setNetworkRequirements(dj.getNetworks()); - } - if (CollectionUtils.isNotEmpty(dj.getDisks())) { - answer.setDisks(dj.getDisks()); - } - answer.setOvfHardwareSection(dj.getVirtualHardwareSection()); - if (CollectionUtils.isNotEmpty(dj.getEulaSections())) { - answer.setEulaSections(dj.getEulaSections()); + if (dj.getOvfInformationTO() != null) { + answer.setOvfInformationTO(dj.getOvfInformationTO()); } jobs.remove(jobId); return answer;