This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 8c86f24261c enhancement: add instance info as Libvirt metadata (#11061)
8c86f24261c is described below

commit 8c86f24261c5133831d8412aec6067a794a701dd
Author: Phsm Qwerty <[email protected]>
AuthorDate: Fri Nov 7 14:31:34 2025 +0100

    enhancement: add instance info as Libvirt metadata (#11061)
---
 .../agent/api/to/VirtualMachineMetadataTO.java     | 182 ++++++++++++++++++
 .../com/cloud/agent/api/to/VirtualMachineTO.java   |   9 +
 .../kvm/resource/LibvirtComputingResource.java     |  12 ++
 .../hypervisor/kvm/resource/LibvirtVMDef.java      | 209 +++++++++++++++++----
 .../com/cloud/hypervisor/HypervisorGuruBase.java   | 170 ++++++++++++++++-
 .../main/java/com/cloud/hypervisor/KVMGuru.java    |   4 +-
 6 files changed, 547 insertions(+), 39 deletions(-)

diff --git 
a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.java 
b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.java
new file mode 100644
index 00000000000..5b22afdedd5
--- /dev/null
+++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.java
@@ -0,0 +1,182 @@
+// 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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class VirtualMachineMetadataTO {
+    // VM details
+    private final String name;
+    private final String internalName;
+    private final String displayName;
+    private final String instanceUuid;
+    private final Integer cpuCores;
+    private final Integer memory;
+    private final Long created;
+    private final Long started;
+
+    // Owner details
+    private final String ownerDomainUuid;
+    private final String ownerDomainName;
+    private final String ownerAccountUuid;
+    private final String ownerAccountName;
+    private final String ownerProjectUuid;
+    private final String ownerProjectName;
+
+    // Host and service offering
+    private final String serviceOfferingName;
+    private final List<String> serviceOfferingHostTags;
+
+    // zone, pod, and cluster details
+    private final String zoneName;
+    private final String zoneUuid;
+    private final String podName;
+    private final String podUuid;
+    private final String clusterName;
+    private final String clusterUuid;
+
+    // resource tags
+    private final Map<String, String> resourceTags;
+
+    public VirtualMachineMetadataTO(
+            String name, String internalName, String displayName, String 
instanceUuid, Integer cpuCores, Integer memory, Long created, Long started,
+            String ownerDomainUuid, String ownerDomainName, String 
ownerAccountUuid, String ownerAccountName, String ownerProjectUuid, String 
ownerProjectName,
+            String serviceOfferingName, List<String> serviceOfferingHostTags,
+            String zoneName, String zoneUuid, String podName, String podUuid, 
String clusterName, String clusterUuid, Map<String, String> resourceTags) {
+        /*
+        * Something failed in the metadata shall not be a fatal error, the VM 
can still be started
+        * Thus, the unknown fields just get an explicit "unknown" value so it 
can be fixed in case
+        * there are bugs on some execution paths.
+        * */
+
+        this.name = (name != null) ? name : "unknown";
+        this.internalName = (internalName != null) ? internalName : "unknown";
+        this.displayName = (displayName != null) ? displayName : "unknown";
+        this.instanceUuid = (instanceUuid != null) ? instanceUuid : "unknown";
+        this.cpuCores = (cpuCores != null) ? cpuCores : -1;
+        this.memory = (memory != null) ? memory : -1;
+        this.created = (created != null) ? created : 0;
+        this.started = (started != null) ? started : 0;
+        this.ownerDomainUuid = (ownerDomainUuid != null) ? ownerDomainUuid : 
"unknown";
+        this.ownerDomainName = (ownerDomainName != null) ? ownerDomainName : 
"unknown";
+        this.ownerAccountUuid = (ownerAccountUuid != null) ? ownerAccountUuid 
: "unknown";
+        this.ownerAccountName = (ownerAccountName != null) ? ownerAccountName 
: "unknown";
+        this.ownerProjectUuid = (ownerProjectUuid != null) ? ownerProjectUuid 
: "unknown";
+        this.ownerProjectName = (ownerProjectName != null) ? ownerProjectName 
: "unknown";
+        this.serviceOfferingName = (serviceOfferingName != null) ? 
serviceOfferingName : "unknown";
+        this.serviceOfferingHostTags = (serviceOfferingHostTags != null) ? 
serviceOfferingHostTags : new ArrayList<>();
+        this.zoneName = (zoneName != null) ? zoneName : "unknown";
+        this.zoneUuid = (zoneUuid != null) ? zoneUuid : "unknown";
+        this.podName = (podName != null) ? podName : "unknown";
+        this.podUuid = (podUuid != null) ? podUuid : "unknown";
+        this.clusterName = (clusterName != null) ? clusterName : "unknown";
+        this.clusterUuid = (clusterUuid != null) ? clusterUuid : "unknown";
+
+        this.resourceTags = (resourceTags != null) ? resourceTags : new 
HashMap<>();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getInternalName() {
+        return internalName;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public String getInstanceUuid() {
+        return instanceUuid;
+    }
+
+    public Integer getCpuCores() {
+        return cpuCores;
+    }
+
+    public Integer getMemory() {
+        return memory;
+    }
+
+    public Long getCreated() { return created; }
+
+    public Long getStarted() {
+        return started;
+    }
+
+    public String getOwnerDomainUuid() {
+        return ownerDomainUuid;
+    }
+
+    public String getOwnerDomainName() {
+        return ownerDomainName;
+    }
+
+    public String getOwnerAccountUuid() {
+        return ownerAccountUuid;
+    }
+
+    public String getOwnerAccountName() {
+        return ownerAccountName;
+    }
+
+    public String getOwnerProjectUuid() {
+        return ownerProjectUuid;
+    }
+
+    public String getOwnerProjectName() {
+        return ownerProjectName;
+    }
+
+    public String getserviceOfferingName() {
+        return serviceOfferingName;
+    }
+
+    public List<String> getserviceOfferingHostTags() {
+        return serviceOfferingHostTags;
+    }
+
+    public String getZoneName() {
+        return zoneName;
+    }
+
+    public String getZoneUuid() {
+        return zoneUuid;
+    }
+
+    public String getPodName() {
+        return podName;
+    }
+
+    public String getPodUuid() {
+        return podUuid;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public String getClusterUuid() {
+        return clusterUuid;
+    }
+
+    public Map<String, String> getResourceTags() { return resourceTags; }
+}
diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java 
b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
index cffb9874080..e26cc1e9f02 100644
--- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
@@ -89,6 +89,7 @@ public class VirtualMachineTO {
     private DeployAsIsInfoTO deployAsIsInfo;
     private String metadataManufacturer;
     private String metadataProductName;
+    private VirtualMachineMetadataTO metadata;
 
     public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type 
type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType 
bootloader,
             String os, boolean enableHA, boolean limitCpuUse, String 
vncPassword) {
@@ -494,6 +495,14 @@ public class VirtualMachineTO {
         this.metadataProductName = metadataProductName;
     }
 
+    public VirtualMachineMetadataTO getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(VirtualMachineMetadataTO metadata) {
+        this.metadata = metadata;
+    }
+
     @Override
     public String toString() {
         return String.format("VM {id: \"%s\", name: \"%s\", uuid: \"%s\", 
type: \"%s\"}", id, name, uuid, type);
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 0aa094e56d9..fbfe3ef20eb 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -69,6 +69,7 @@ import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFactory;
 
+import com.cloud.agent.api.to.VirtualMachineMetadataTO;
 import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
 import org.apache.cloudstack.command.CommandInfo;
 import org.apache.cloudstack.command.ReconcileCommandService;
@@ -199,6 +200,7 @@ import 
com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VideoDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
 import 
com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogAction;
 import 
com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogModel;
+import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MetadataDef;
 import 
com.cloud.hypervisor.kvm.resource.rolling.maintenance.RollingMaintenanceAgentExecutor;
 import 
com.cloud.hypervisor.kvm.resource.rolling.maintenance.RollingMaintenanceExecutor;
 import 
com.cloud.hypervisor.kvm.resource.rolling.maintenance.RollingMaintenanceServiceExecutor;
@@ -3011,9 +3013,19 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         vm.addComp(createClockDef(vmTO));
         vm.addComp(createDevicesDef(vmTO, guest, vcpus, isUefiEnabled));
 
+        MetadataDef metaDef;
+        if ((metaDef = createMetadataDef(vmTO)) != null) {
+            vm.addComp(metaDef);
+        }
+
         addExtraConfigsToVM(vmTO, vm, extraConfig);
     }
 
+    protected MetadataDef createMetadataDef(VirtualMachineTO vmTO) {
+        VirtualMachineMetadataTO metadata = vmTO.getMetadata();
+        return (metadata != null) ? new MetadataDef(metadata) : null;
+    }
+
     /**
      *  Adds extra configuration to User VM Domain XML before starting.
      */
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index bf002b37f35..211395513d5 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -17,6 +17,10 @@
 package com.cloud.hypervisor.kvm.resource;
 
 import java.io.File;
+import java.io.StringWriter;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -29,9 +33,25 @@ import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.LogManager;
+import com.cloud.agent.api.to.VirtualMachineMetadataTO;
+
 
 import com.cloud.agent.properties.AgentProperties;
 import com.cloud.agent.properties.AgentPropertiesFileHandler;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
 
 public class LibvirtVMDef {
     protected static Logger LOGGER = LogManager.getLogger(LibvirtVMDef.class);
@@ -46,6 +66,157 @@ public class LibvirtVMDef {
     private final Map<String, Object> components = new HashMap<String, 
Object>();
     private static final int NUMBER_OF_IOTHREADS = 
AgentPropertiesFileHandler.getPropertyValue(AgentProperties.IOTHREADS);
 
+    public static class MetadataDef {
+        private VirtualMachineMetadataTO metaTO;
+
+        public MetadataDef(VirtualMachineMetadataTO data) {
+            metaTO = data;
+        }
+
+        public VirtualMachineMetadataTO getMetadata() {
+            return metaTO;
+        }
+
+        @Override
+        public String toString() {
+            DocumentBuilderFactory docFactory = 
DocumentBuilderFactory.newInstance();
+            docFactory.setNamespaceAware(true);
+            Document doc = null;
+            try {
+                doc = docFactory.newDocumentBuilder().newDocument();
+            } catch (ParserConfigurationException e) {
+                LOGGER.warn("Could not create a new DOM XML document. The 
metadata will not be included in the libvirt domain XML: {}", e.getMessage());
+                return "";
+            }
+            Element metadata = doc.createElement("metadata"); // <metadata>
+            Element instance = 
doc.createElementNS("http://cloudstack.apache.org/instance";, 
"cloudstack:instance"); // <cloudstack:instance>
+
+            Element zone = doc.createElement("cloudstack:zone");
+            zone.setAttribute("uuid", metaTO.getZoneUuid());
+            zone.setTextContent(metaTO.getZoneName());
+            instance.appendChild(zone);
+
+            Element pod = doc.createElement("cloudstack:pod");
+            pod.setAttribute("uuid", metaTO.getPodUuid());
+            pod.setTextContent(metaTO.getPodName());
+            instance.appendChild(pod);
+
+            Element cluster = doc.createElement("cloudstack:cluster");
+            cluster.setAttribute("uuid", metaTO.getClusterUuid());
+            cluster.setTextContent(metaTO.getClusterName());
+            instance.appendChild(cluster);
+
+            Element instanceName = doc.createElement("cloudstack:name");
+            instanceName.setTextContent(metaTO.getName());
+            instance.appendChild(instanceName);
+
+            Element instanceInternalName = 
doc.createElement("cloudstack:internal_name");
+            instanceInternalName.setTextContent(metaTO.getInternalName());
+            instance.appendChild(instanceInternalName);
+
+            Element instanceDisplayName = 
doc.createElement("cloudstack:display_name");
+            instanceDisplayName.setTextContent(metaTO.getDisplayName());
+            instance.appendChild(instanceDisplayName);
+
+            Element instanceUuid = doc.createElement("cloudstack:uuid");
+            instanceUuid.setTextContent(metaTO.getInstanceUuid());
+            instance.appendChild(instanceUuid);
+
+            Element serviceOffering = 
doc.createElement("cloudstack:service_offering"); // <service_offering>
+
+            Element computeOfferingName = doc.createElement("cloudstack:name");
+            
computeOfferingName.setTextContent(metaTO.getserviceOfferingName());
+            serviceOffering.appendChild(computeOfferingName);
+
+            Element cpu = doc.createElement("cloudstack:cpu");
+            cpu.setTextContent(metaTO.getCpuCores().toString());
+            serviceOffering.appendChild(cpu);
+
+            Element memory = doc.createElement("cloudstack:memory");
+            memory.setTextContent(metaTO.getMemory().toString());
+            serviceOffering.appendChild(memory);
+
+            Element hostTags = doc.createElement("cloudstack:host_tags");
+            List<String> tags = metaTO.getserviceOfferingHostTags();
+            if (tags != null) {
+                for (String i : metaTO.getserviceOfferingHostTags()) {
+                    Element tag = doc.createElement("cloudstack:tag");
+                    tag.setTextContent(i);
+                    hostTags.appendChild(tag);
+                }
+            }
+            serviceOffering.appendChild(hostTags);
+
+            instance.appendChild(serviceOffering); // </service_offering>
+
+            Element creationTime = doc.createElement("cloudstack:created_at");
+            creationTime.setTextContent(
+                    
LocalDateTime.ofInstant(Instant.ofEpochSecond(metaTO.getCreated()), 
ZoneOffset.UTC).format(ISO_LOCAL_DATE_TIME)
+            );
+            instance.appendChild(creationTime);
+
+            Element startedTime = doc.createElement("cloudstack:started_at");
+            startedTime.setTextContent(
+                    
LocalDateTime.ofInstant(Instant.ofEpochSecond(metaTO.getStarted()), 
ZoneOffset.UTC).format(ISO_LOCAL_DATE_TIME)
+            );
+            instance.appendChild(startedTime);
+
+            Element owner = doc.createElement("cloudstack:owner"); // <owner>
+
+            Element domain = doc.createElement("cloudstack:domain");
+            domain.setAttribute("uuid", metaTO.getOwnerDomainUuid());
+            domain.setTextContent(metaTO.getOwnerDomainName());
+            owner.appendChild(domain);
+
+            Element account = doc.createElement("cloudstack:account");
+            account.setAttribute("uuid", metaTO.getOwnerAccountUuid());
+            account.setTextContent(metaTO.getOwnerAccountName());
+            owner.appendChild(account);
+
+            Element project = doc.createElement("cloudstack:project");
+            project.setAttribute("uuid", metaTO.getOwnerProjectUuid());
+            project.setTextContent(metaTO.getOwnerProjectName());
+            owner.appendChild(project);
+
+            instance.appendChild(owner); // </owner>
+
+            Element resourceTags = 
doc.createElement("cloudstack:resource_tags"); // <resource_tags>
+            for (Map.Entry<String, String> entry : 
metaTO.getResourceTags().entrySet()) {
+                Element tag = doc.createElement("cloudstack:resource_tag"); // 
<resource_tag>
+                tag.setAttribute("key", entry.getKey());
+                tag.setTextContent(entry.getValue());
+                resourceTags.appendChild(tag); // </resource_tag>
+            }
+            instance.appendChild(resourceTags); // </resource_tags>
+
+            metadata.appendChild(instance); // </cloudstack:instance>
+            doc.appendChild(metadata); // </metadata>
+
+            Transformer transformer = null;
+            try {
+                transformer = 
TransformerFactory.newInstance().newTransformer();
+            } catch (TransformerConfigurationException e) {
+                LOGGER.warn("Could not create an XML transformer. The metadata 
will not be included in the libvirt domain XML: {}", e.getMessage());
+                return "";
+            }
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, 
"yes");
+            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+            DOMSource domSource = new DOMSource(doc);
+            StringWriter writer = new StringWriter();
+            StreamResult result = new StreamResult(writer);
+            try {
+                transformer.transform(domSource, result);
+            } catch (TransformerException e) {
+                LOGGER.warn("Could not generate metadata XML. The metadata 
will not be included in the libvirt domain XML: {}", e.getMessage());
+                return "";
+            }
+
+            return writer.toString();
+        }
+    }
+
     public static class GuestDef {
         enum GuestType {
             KVM, XEN, EXE, LXC
@@ -2163,34 +2334,6 @@ public class LibvirtVMDef {
         }
     }
 
-    public class MetadataDef {
-        Map<String, Object> customNodes = new HashMap<>();
-
-        public <T> T getMetadataNode(Class<T> fieldClass) {
-            T field = (T) customNodes.get(fieldClass.getName());
-            if (field == null) {
-                try {
-                    field = fieldClass.newInstance();
-                    customNodes.put(field.getClass().getName(), field);
-                } catch (InstantiationException | IllegalAccessException e) {
-                    LOGGER.debug("No default constructor available in class " 
+ fieldClass.getName() + ", ignoring exception", e);
-                }
-            }
-            return field;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder fsBuilder = new StringBuilder();
-            fsBuilder.append("<metadata>\n");
-            for (Object field : customNodes.values()) {
-                fsBuilder.append(field.toString());
-            }
-            fsBuilder.append("</metadata>\n");
-            return fsBuilder.toString();
-        }
-    }
-
     public static class RngDef {
         enum RngModel {
             VIRTIO("virtio");
@@ -2493,15 +2636,6 @@ public class LibvirtVMDef {
         return null;
     }
 
-    public MetadataDef getMetaData() {
-        MetadataDef o = (MetadataDef) 
components.get(MetadataDef.class.toString());
-        if (o == null) {
-            o = new MetadataDef();
-            addComp(o);
-        }
-        return o;
-    }
-
     @Override
     public String toString() {
         StringBuilder vmBuilder = new StringBuilder();
@@ -2513,6 +2647,7 @@ public class LibvirtVMDef {
         if (_desc != null) {
             vmBuilder.append("<description>" + _desc + "</description>\n");
         }
+
         for (Object o : components.values()) {
             vmBuilder.append(o.toString());
         }
diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java 
b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
index d40b5b22698..928107be9ce 100644
--- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
+++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
@@ -16,6 +16,9 @@
 // under the License.
 package com.cloud.hypervisor;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -24,18 +27,31 @@ import java.util.UUID;
 import javax.inject.Inject;
 
 import com.cloud.agent.api.to.GPUDeviceTO;
+import com.cloud.agent.api.to.VirtualMachineMetadataTO;
 import com.cloud.cpu.CPU;
+import com.cloud.dc.ClusterVO;
 import com.cloud.dc.DataCenter;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.HostPodVO;
+import com.cloud.dc.dao.ClusterDao;
 import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.HostPodDao;
 import com.cloud.domain.Domain;
+import com.cloud.domain.DomainVO;
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.gpu.VgpuProfileVO;
 import com.cloud.gpu.dao.VgpuProfileDao;
 import com.cloud.network.vpc.VpcVO;
 import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.projects.ProjectVO;
+import com.cloud.projects.dao.ProjectDao;
+import com.cloud.server.ResourceTag;
+import com.cloud.tags.dao.ResourceTagDao;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.UserVmVO;
+import com.cloud.vm.dao.UserVmDao;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.backup.Backup;
 import 
org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -97,7 +113,7 @@ public abstract class HypervisorGuruBase extends AdapterBase 
implements Hypervis
     @Inject
     protected AccountManager accountManager;
     @Inject
-    private DomainDao domainDao;
+    protected DomainDao domainDao;
     @Inject
     private DataCenterDao dcDao;
     @Inject
@@ -125,7 +141,19 @@ public abstract class HypervisorGuruBase extends 
AdapterBase implements Hypervis
     @Inject
     private UserVmManager userVmManager;
     @Inject
+    protected UserVmDao userVmDao;
+    @Inject
+    protected ProjectDao projectDao;
+    @Inject
+    protected ClusterDao clusterDao;
+    @Inject
+    protected DataCenterDao dataCenterDao;
+    @Inject
+    protected HostPodDao hostPodDao;
+    @Inject
     private ConfigurationManager configurationManager;
+    @Inject
+    ResourceTagDao tagsDao;
 
     public static ConfigKey<Boolean> 
VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor = new 
ConfigKey<Boolean>("Advanced", Boolean.class, 
"vm.min.memory.equals.memory.divided.by.mem.overprovisioning.factor", "true",
             "If we set this to 'true', a minimum memory (memory/ 
mem.overprovisioning.factor) will be set to the VM, independent of using a 
scalable service offering or not.", true, ConfigKey.Scope.Cluster);
@@ -470,4 +498,144 @@ public abstract class HypervisorGuruBase extends 
AdapterBase implements Hypervis
         logger.error("Unsupported operation: cannot remove template file");
         return false;
     }
+
+    /**
+     * Generates VirtualMachineMetadataTO object from VirtualMachineProfile
+     * It is a helper function to be used in the inherited classes to avoid 
repetition
+     * while generating metadata for multiple Guru implementations
+     *
+     * @param  vmProfile  virtual machine profile object
+     * @return      A VirtualMachineMetadataTO ready to be appended to 
VirtualMachineTO object
+     * @see         KVMGuru
+     */
+    protected VirtualMachineMetadataTO 
makeVirtualMachineMetadata(VirtualMachineProfile vmProfile) {
+        String vmName = "unknown",
+                instanceName = "unknown",
+                displayName = "unknown",
+                instanceUuid = "unknown",
+                clusterName = "unknown",
+                clusterUuid = "unknown",
+                zoneUuid = "unknown",
+                zoneName = "unknown",
+                podUuid = "unknown",
+                podName = "unknown",
+                domainUuid = "unknown",
+                domainName = "unknown",
+                accountUuid = "unknown",
+                accountName = "unknown",
+                projectName = "", // the project can be empty
+                projectUuid = "", // the project can be empty
+                serviceOfferingName = "unknown";
+        long created = 0L;
+        Integer cpuCores = -1, memory = -1;
+        List<String> serviceOfferingTags = new ArrayList<>();
+        HashMap<String, String> resourceTags = new HashMap<>();
+
+        UserVmVO vmVO = 
userVmDao.findById(vmProfile.getVirtualMachine().getId());
+        if (vmVO != null) {
+            instanceUuid = vmVO.getUuid();
+            vmName = vmVO.getHostName(); // this returns the VM name field
+            instanceName = vmVO.getInstanceName();
+            displayName = vmVO.getDisplayName();
+            created = vmVO.getCreated().getTime() / 1000L;
+
+            HostVO host = hostDao.findById(vmVO.getHostId());
+            if (host != null) {
+                // Find zone and cluster
+                Long clusterId = host.getClusterId();
+                ClusterVO cluster = clusterDao.findById(clusterId);
+
+                if (cluster != null) {
+                    clusterName = cluster.getName();
+                    clusterUuid = cluster.getUuid();
+
+                    DataCenterVO zone = 
dataCenterDao.findById(cluster.getDataCenterId());
+                    if (zone != null) {
+                        zoneUuid = zone.getUuid();
+                        zoneName = zone.getName();
+                    }
+
+                    HostPodVO pod = hostPodDao.findById(cluster.getPodId());
+                    if (pod != null) {
+                        podUuid = pod.getUuid();
+                        podName = pod.getName();
+                    }
+                }
+            } else {
+                logger.warn("Could not find the Host object for the virtual 
machine (null value returned). Libvirt metadata for cluster, pod, zone will not 
be populated.");
+            }
+
+            DomainVO domain = domainDao.findById(vmVO.getDomainId());
+            if (domain != null) {
+                domainUuid = domain.getUuid();
+                domainName = domain.getName();
+            } else {
+                logger.warn("Could not find the Domain object for the virtual 
machine (null value returned). Libvirt metadata for domain will not be 
populated.");
+            }
+
+            Account account = accountManager.getAccount(vmVO.getAccountId());
+            if (account != null) {
+                accountUuid = account.getUuid();
+                accountName = account.getName();
+
+                ProjectVO project = 
projectDao.findByProjectAccountId(account.getId());
+                if (project != null) {
+                    projectName = project.getName();
+                    projectUuid = project.getUuid();
+                }
+            } else {
+                logger.warn("Could not find the Account object for the virtual 
machine (null value returned). Libvirt metadata for account and project will 
not be populated.");
+            }
+
+            List<? extends ResourceTag> resourceTagsList = 
tagsDao.listBy(vmVO.getId(), ResourceTag.ResourceObjectType.UserVm);
+            if (resourceTagsList != null) {
+                for (ResourceTag tag : resourceTagsList) {
+                    resourceTags.put(tag.getKey(), tag.getValue());
+                }
+            }
+        } else {
+            logger.warn("Could not find the VirtualMachine object by its 
profile (null value returned). Libvirt metadata will not be populated.");
+        }
+
+        ServiceOffering serviceOffering = vmProfile.getServiceOffering();
+        if (serviceOffering != null) {
+            serviceOfferingName = serviceOffering.getName();
+            cpuCores = serviceOffering.getCpu();
+            memory = serviceOffering.getRamSize();
+
+            String hostTagsCommaSeparated = serviceOffering.getHostTag();
+            if (hostTagsCommaSeparated != null) { // when service offering has 
no host tags, this value is null
+                serviceOfferingTags = 
Arrays.asList(hostTagsCommaSeparated.split(","));
+            }
+        } else {
+            logger.warn("Could not find the ServiceOffering object by its 
profile (null value returned). Libvirt metadata for service offering will not 
be populated.");
+        }
+
+
+        return new VirtualMachineMetadataTO(
+                vmName, // name
+                instanceName, // internalName
+                displayName, // displayName
+                instanceUuid , // instanceUUID
+                cpuCores, // cpuCores
+                memory, // memory
+                created, // created, unix epoch in seconds
+                System.currentTimeMillis() / 1000L, // started, unix epoch in 
seconds
+                domainUuid, // ownerDomainUUID
+                domainName, // ownerDomainName
+                accountUuid, // ownerAccountUUID
+                accountName, // ownerAccountName
+                projectUuid,
+                projectName,
+                serviceOfferingName,
+                serviceOfferingTags, // serviceOfferingTags
+                zoneName,
+                zoneUuid,
+                podName,
+                podUuid,
+                clusterName,
+                clusterUuid,
+                resourceTags
+        );
+    }
 }
diff --git a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java 
b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
index 64881df4c82..d907e726b20 100644
--- a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
+++ b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
@@ -155,7 +155,6 @@ public class KVMGuru extends HypervisorGuruBase implements 
HypervisorGuru {
     }
 
     @Override
-
     public VirtualMachineTO implement(VirtualMachineProfile vm) {
         VirtualMachineTO to = toVirtualMachineTO(vm);
         setVmQuotaPercentage(to, vm);
@@ -170,6 +169,9 @@ public class KVMGuru extends HypervisorGuruBase implements 
HypervisorGuru {
         configureVmOsDescription(virtualMachine, to, host);
 
         configureVmMemoryAndCpuCores(to, host, virtualMachine, vm);
+
+        to.setMetadata(makeVirtualMachineMetadata(vm));
+
         return to;
     }
 

Reply via email to