diff --git a/compute/src/main/java/org/zstack/compute/vm/UpdateVmInstanceMetadataGC.java b/compute/src/main/java/org/zstack/compute/vm/UpdateVmInstanceMetadataGC.java new file mode 100644 index 00000000000..122bce0dc40 --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/UpdateVmInstanceMetadataGC.java @@ -0,0 +1,71 @@ +package org.zstack.compute.vm; + +import org.springframework.beans.factory.annotation.Autowired; +import org.zstack.core.cloudbus.CloudBusCallBack; +import org.zstack.core.gc.GC; +import org.zstack.core.gc.GCCompletion; +import org.zstack.core.gc.TimeBasedGarbageCollector; +import org.zstack.core.thread.ChainTask; +import org.zstack.core.thread.SyncTaskChain; +import org.zstack.core.thread.ThreadFacade; +import org.zstack.header.core.progress.ChainInfo; +import org.zstack.header.message.MessageReply; +import org.zstack.header.vm.UpdateVmInstanceMetadataMsg; +import org.zstack.header.volume.VolumeConstant; +import org.zstack.header.volume.VolumeVO; + +public class UpdateVmInstanceMetadataGC extends TimeBasedGarbageCollector { + @GC + public String vmInstanceUuid; + + @Autowired + protected ThreadFacade thdf; + + static public String getUpdateVmInstanceMetadataSyncSignature(String vmInstanceUuid) { + return String.format("update-vm-%s-metadata", vmInstanceUuid); + } + + @Override + protected void triggerNow(GCCompletion completion) { + if (!dbf.isExist(vmInstanceUuid, VolumeVO.class)) { + completion.cancel(); + return; + } + + String queueName = getUpdateVmInstanceMetadataSyncSignature(vmInstanceUuid); + ChainInfo chainInfo = thdf.getChainTaskInfo(queueName); + if (!chainInfo.getPendingTask().isEmpty()) { + completion.cancel(); + return; + } + + thdf.chainSubmit(new ChainTask(completion) { + @Override + public String getSyncSignature() { + return queueName; + } + + @Override + public void run(final SyncTaskChain chain) { + UpdateVmInstanceMetadataMsg msg = new UpdateVmInstanceMetadataMsg(); + msg.setUuid(vmInstanceUuid); + bus.makeTargetServiceIdByResourceUuid(msg, VolumeConstant.SERVICE_ID, vmInstanceUuid); + bus.send(msg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + completion.fail(reply.getError()); + } else { + completion.success(); + } + } + }); + } + + @Override + public String getName() { + return queueName; + } + }); + } +} diff --git a/compute/src/main/java/org/zstack/compute/vm/VmGlobalConfig.java b/compute/src/main/java/org/zstack/compute/vm/VmGlobalConfig.java index bd79900c13c..8f88beb82a1 100755 --- a/compute/src/main/java/org/zstack/compute/vm/VmGlobalConfig.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmGlobalConfig.java @@ -133,4 +133,7 @@ public class VmGlobalConfig { @GlobalConfigValidation(validValues = {"None", "AuthenticAMD"}) @BindResourceConfig(value = {VmInstanceVO.class}) public static GlobalConfig VM_CPUID_VENDOR = new GlobalConfig(CATEGORY, "vm.cpuid.vendor"); + +// @GlobalConfigValidation +// public static GlobalConfig GC_INTERVAL = new GlobalConfig(CATEGORY, "deletion.gcInterval"); } diff --git a/compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java b/compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java index e31bc001218..f575fe8fd01 100755 --- a/compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java @@ -13,6 +13,8 @@ import org.zstack.core.cascade.CascadeFacade; import org.zstack.core.cloudbus.*; import org.zstack.core.componentloader.PluginRegistry; +import org.zstack.core.config.GlobalConfig; +import org.zstack.core.config.GlobalConfigDefinition; import org.zstack.core.db.*; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.defer.Defer; @@ -24,6 +26,7 @@ import org.zstack.core.thread.ThreadFacade; import org.zstack.core.workflow.FlowChainBuilder; import org.zstack.core.workflow.ShareFlow; +import org.zstack.core.workflow.ShareFlowChain; import org.zstack.core.workflow.SimpleFlowChain; import org.zstack.header.allocator.*; import org.zstack.header.apimediator.ApiMessageInterceptionException; @@ -45,6 +48,12 @@ import org.zstack.header.message.*; import org.zstack.header.network.l3.*; import org.zstack.header.storage.primary.*; +import org.zstack.header.storage.snapshot.*; +import org.zstack.header.storage.snapshot.group.*; +import org.zstack.header.tag.SystemTagVO; +import org.zstack.header.tag.SystemTagVO_; +import org.zstack.header.tag.TagDefinition; +import org.zstack.header.tag.TagType; import org.zstack.header.vm.*; import org.zstack.header.vm.ChangeVmMetaDataMsg.AtomicHostUuid; import org.zstack.header.vm.ChangeVmMetaDataMsg.AtomicVmState; @@ -66,27 +75,25 @@ import org.zstack.network.l3.L3NetworkManager; import org.zstack.network.service.DnsUtils; import org.zstack.network.service.NetworkServiceManager; -import org.zstack.resourceconfig.ResourceConfig; -import org.zstack.resourceconfig.ResourceConfigFacade; +import org.zstack.resourceconfig.*; +import org.zstack.tag.SystemTag; import org.zstack.tag.SystemTagCreator; import org.zstack.tag.SystemTagUtils; import org.zstack.tag.TagManager; -import org.zstack.utils.CollectionUtils; -import org.zstack.utils.ExceptionDSL; -import org.zstack.utils.ObjectUtils; -import org.zstack.utils.Utils; +import org.zstack.utils.*; import org.zstack.utils.function.ForEachFunction; import org.zstack.utils.function.Function; import org.zstack.utils.gson.JSONObjectUtil; import org.zstack.utils.logging.CLogger; -import org.zstack.utils.network.NicIpAddressInfo; import org.zstack.utils.network.IPv6Constants; import org.zstack.utils.network.IPv6NetworkUtils; import org.zstack.utils.network.NetworkUtils; +import org.zstack.utils.network.NicIpAddressInfo; import javax.persistence.PersistenceException; import javax.persistence.Tuple; import javax.persistence.TypedQuery; +import java.lang.reflect.Field; import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.*; @@ -140,6 +147,8 @@ public class VmInstanceBase extends AbstractVmInstance { private VmInstanceResourceMetadataManager vidm; @Autowired private NetworkServiceManager nwServiceMgr; + @Autowired + private ResourceDestinationMaker destMaker; protected VmInstanceVO self; protected VmInstanceVO originalCopy; @@ -533,6 +542,8 @@ protected void handleLocalMessage(Message msg) { handle((CancelFlattenVmInstanceMsg) msg); } else if (msg instanceof KvmReportVmShutdownEventMsg) { handle((KvmReportVmShutdownEventMsg) msg); + } else if (msg instanceof UpdateVmInstanceMetadataMsg) { + handle((UpdateVmInstanceMetadataMsg) msg); } else { VmInstanceBaseExtensionFactory ext = vmMgr.getVmInstanceBaseExtensionFactory(msg); if (ext != null) { @@ -3310,6 +3321,8 @@ protected void handleApiMessage(APIMessage msg) { handle((APIUpdateTemplatedVmInstanceMsg) msg); } else if (msg instanceof APIDeleteTemplatedVmInstanceMsg) { handle((APIDeleteTemplatedVmInstanceMsg) msg); + } else if (msg instanceof APIRegisterVmInstanceMsg) { + handle((APIRegisterVmInstanceMsg) msg); } else { VmInstanceBaseExtensionFactory ext = vmMgr.getVmInstanceBaseExtensionFactory(msg); if (ext != null) { @@ -6045,6 +6058,19 @@ public void run(MessageReply reply) { bus.publish(evt); return; } + +// SubmitGarbageCollectorMsg gcmsg = new SubmitGarbageCollectorMsg(); +// gcmsg.setGcInterval(VmGlobalConfig.GC_INTERVAL.value(Long.class)); +// gcmsg.setUnit(TimeUnit.SECONDS); +// +// UpdateVmInstanceMetadataGC gc = new UpdateVmInstanceMetadataGC(); +// gc.vmInstanceUuid = self.getUuid(); +// gc.NAME = String.format("gc-update-vm-%s-metadata", self.getUuid()); +// gcmsg.setGc(gc); +// +// bus.makeServiceIdByManagementNodeId(gcmsg, GCConstants.SERVICE_ID, destMaker.makeDestination(umsg.getVmInstanceUuid())); +// bus.send(gcmsg); + evt.setInventory(((UpdateVmInstanceReply) reply.castReply()).getInventory()); bus.publish(evt); } @@ -9369,5 +9395,529 @@ public void run(MessageReply reply) { } }); } + + private String buildVmInstanceMetadata(String vmInstanceUuid) { + VmMetadata vmMetadata = new VmMetadata(); + + // 找出vm + // 找出volume和快照 + // 找出网卡 + VmInstanceVO vm = Q.New(VmInstanceVO.class).eq(VmInstanceVO_.uuid, vmInstanceUuid).find(); + vmMetadata.vmSystemTags = getResourceSystemTagFromDb(vm.getUuid()); + vmMetadata.vmResourceConfigs = getResourceConfigFromDb(vm.getUuid()); + + // volume + // 挂载的 + List volumes1 = Q.New(VolumeVO.class).eq(VolumeVO_.vmInstanceUuid, vmInstanceUuid).list(); + // 被卸载的 + List volumes2 = Q.New(VolumeVO.class).eq(VolumeVO_.vmInstanceUuid, null).eq(VolumeVO_.lastVmInstanceUuid, vmInstanceUuid).list(); + + List volumes = new ArrayList<>(); + volumes.addAll(volumes1); + volumes.addAll(volumes2); + volumes.forEach(volume -> { + vmMetadata.volumeSystemTags.put(volume.getUuid(), getResourceSystemTagFromDb(volume.getUuid())); + vmMetadata.volumeResourceConfigs.put(volume.getUuid(), getResourceConfigFromDb(volume.getUuid())); + }); + + // snapshot + List volumeUuids = volumes.stream().map(VolumeVO::getUuid).collect(Collectors.toList()); + List snapshot = Q.New(VolumeSnapshotVO.class).in(VolumeSnapshotVO_.volumeUuid, volumeUuids).list(); + + List group = Q.New(VolumeSnapshotGroupVO.class).eq(VolumeSnapshotGroupVO_.vmInstanceUuid, vmInstanceUuid).list(); + List groupUuids = group.stream().map(VolumeSnapshotGroupVO::getUuid).collect(Collectors.toList()); + List groupRef = Q.New(VolumeSnapshotGroupRefVO.class).in(VolumeSnapshotGroupRefVO_.volumeSnapshotGroupUuid, groupUuids).list(); + + // vm nic + List nics = Q.New(VmNicVO.class).eq(VmNicVO_.vmInstanceUuid, vmInstanceUuid).list(); + nics.forEach(nic -> { + vmMetadata.vmNicSystemTags.put(nic.getUuid(), getResourceSystemTagFromDb(nic.getUuid())); + vmMetadata.vmNicResourceConfigs.put(nic.getUuid(), getResourceConfigFromDb(nic.getUuid())); + }); + + // build metadata + vmMetadata.vmInstanceVO = JSONObjectUtil.toJsonString(vm); + volumes.forEach(volumeVO -> vmMetadata.volumeVOs.add(JSONObjectUtil.toJsonString(volumeVO))); + nics.forEach(nic -> vmMetadata.vmNicVOs.add(JSONObjectUtil.toJsonString(nic))); + Map> volumeSnapshots = new HashMap<>(); + snapshot.forEach(s -> { + if (volumeSnapshots.containsKey(s.getVolumeUuid())) { + volumeSnapshots.get(s.getVolumeUuid()).add(JSONObjectUtil.toJsonString(VolumeSnapshotInventory.valueOf(s))); + } else { + volumeSnapshots.put(s.getVolumeUuid(), new ArrayList<>()); + volumeSnapshots.get(s.getVolumeUuid()).add(JSONObjectUtil.toJsonString(VolumeSnapshotInventory.valueOf(s))); + } + }); + vmMetadata.volumeSnapshots = volumeSnapshots; + vmMetadata.volumeSnapshotGroupVO = group.stream().map(JSONObjectUtil::toJsonString).collect(Collectors.toList()); + vmMetadata.volumeSnapshotGroupRefVO = groupRef.stream().map(JSONObjectUtil::toJsonString).collect(Collectors.toList()); + + // 其他组件的metadata +// CollectionUtils.safeForEach(pluginRgty.getExtensionList(UpdateVmInstanceMetadataExtensionPoint.class), +// new ForEachFunction() { +// @Override +// public void run(UpdateVmInstanceMetadataExtensionPoint ext) { +// logger.debug(String.format("execute before add acl ip entry extension point %s", ext)); +// ext.buildVmInstanceMetadata(VmInstanceInventory.valueOf(vm), vmMetadata); +// } +// }); + + return JSONObjectUtil.toJsonString(vmMetadata); +// { +// "vmInstanceVO" : "{\"vmNics\":[{\"vmInstanceUuid\":\"56583eb6f40e40d1a28f23b7d34b4f5b\",\"l3NetworkUuid\":\"81c8aafb064d4ba2944227da9555c5f0\",\"mac\":\"fa:b2:1a:b5:3a:00\",\"hypervisorType\":\"KVM\",\"deviceId\":0,\"internalName\":\"vnic3.0\",\"driverType\":\"virtio\",\"type\":\"VNIC\",\"state\":\"enable\",\"createDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:40 PM\",\"usedIps\":[],\"uuid\":\"07fdf6d90be1460f9b7ad949cc39c893\",\"resourceType\":\"VmNicVO\",\"concreteResourceType\":\"org.zstack.header.vm.VmNicVO\"}],\"allVolumes\":[{\"name\":\"DATA-for-vm3\",\"description\":\"DataVolume-56583eb6f40e40d1a28f23b7d34b4f5b\",\"primaryStorageUuid\":\"95e0442313234b7d890be67ab232af3f\",\"vmInstanceUuid\":\"56583eb6f40e40d1a28f23b7d34b4f5b\",\"diskOfferingUuid\":\"d2f0438b52a142a981ebfb62cc8dc11a\",\"installPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/dataVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-60c5546c30de4f09afaa243ae951dc63/60c5546c30de4f09afaa243ae951dc63.qcow2\",\"type\":\"Data\",\"status\":\"Ready\",\"size\":10737418240,\"actualSize\":0,\"deviceId\":1,\"format\":\"qcow2\",\"state\":\"Enabled\",\"createDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastAttachDate\":\"Jan 18, 2026 10:34:40 PM\",\"isShareable\":false,\"shadow\":{\"name\":\"DATA-for-vm3\",\"description\":\"DataVolume-56583eb6f40e40d1a28f23b7d34b4f5b\",\"primaryStorageUuid\":\"95e0442313234b7d890be67ab232af3f\",\"vmInstanceUuid\":\"56583eb6f40e40d1a28f23b7d34b4f5b\",\"diskOfferingUuid\":\"d2f0438b52a142a981ebfb62cc8dc11a\",\"installPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/dataVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-60c5546c30de4f09afaa243ae951dc63/60c5546c30de4f09afaa243ae951dc63.qcow2\",\"type\":\"Data\",\"status\":\"Ready\",\"size\":10737418240,\"actualSize\":0,\"deviceId\":1,\"format\":\"qcow2\",\"state\":\"Enabled\",\"createDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastAttachDate\":\"Jan 18, 2026 10:34:40 PM\",\"isShareable\":false},\"uuid\":\"60c5546c30de4f09afaa243ae951dc63\",\"resourceName\":\"DATA-for-vm3\",\"resourceType\":\"VolumeVO\",\"concreteResourceType\":\"org.zstack.header.volume.VolumeVO\"},{\"name\":\"ROOT-for-vm3\",\"description\":\"Root volume for VM[uuid:56583eb6f40e40d1a28f23b7d34b4f5b]\",\"primaryStorageUuid\":\"95e0442313234b7d890be67ab232af3f\",\"vmInstanceUuid\":\"56583eb6f40e40d1a28f23b7d34b4f5b\",\"rootImageUuid\":\"ed16ac328672441a93d15dfb51fa1033\",\"installPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-c28b1bd97d8247bbbf60b8c86276dceb/c28b1bd97d8247bbbf60b8c86276dceb.qcow2\",\"type\":\"Root\",\"status\":\"Ready\",\"size\":0,\"actualSize\":0,\"deviceId\":0,\"format\":\"qcow2\",\"state\":\"Enabled\",\"createDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastAttachDate\":\"Jan 18, 2026 10:34:40 PM\",\"isShareable\":false,\"shadow\":{\"name\":\"ROOT-for-vm3\",\"description\":\"Root volume for VM[uuid:56583eb6f40e40d1a28f23b7d34b4f5b]\",\"primaryStorageUuid\":\"95e0442313234b7d890be67ab232af3f\",\"vmInstanceUuid\":\"56583eb6f40e40d1a28f23b7d34b4f5b\",\"rootImageUuid\":\"ed16ac328672441a93d15dfb51fa1033\",\"installPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-c28b1bd97d8247bbbf60b8c86276dceb/c28b1bd97d8247bbbf60b8c86276dceb.qcow2\",\"type\":\"Root\",\"status\":\"Ready\",\"size\":0,\"actualSize\":0,\"deviceId\":0,\"format\":\"qcow2\",\"state\":\"Enabled\",\"createDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastAttachDate\":\"Jan 18, 2026 10:34:40 PM\",\"isShareable\":false},\"uuid\":\"c28b1bd97d8247bbbf60b8c86276dceb\",\"resourceName\":\"ROOT-for-vm3\",\"resourceType\":\"VolumeVO\",\"concreteResourceType\":\"org.zstack.header.volume.VolumeVO\"}],\"vmCdRoms\":[{\"vmInstanceUuid\":\"56583eb6f40e40d1a28f23b7d34b4f5b\",\"deviceId\":0,\"name\":\"vm-56583eb6f40e40d1a28f23b7d34b4f5b-cdRom\",\"createDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:40 PM\",\"uuid\":\"e087bbf2d5bc4eea8cf04fc5c27db3d1\",\"resourceName\":\"vm-56583eb6f40e40d1a28f23b7d34b4f5b-cdRom\",\"resourceType\":\"VmCdRomVO\",\"concreteResourceType\":\"org.zstack.header.vm.cdrom.VmCdRomVO\"}],\"name\":\"vm3\",\"zoneUuid\":\"edad4e425c6e4b9c9f62e22bc26cfe2e\",\"clusterUuid\":\"c19ac94e90004d72a85fa59269f539ec\",\"imageUuid\":\"ed16ac328672441a93d15dfb51fa1033\",\"hostUuid\":\"3495ce7c625f4b5bbbf3dd10d2387474\",\"internalId\":3,\"lastHostUuid\":\"3495ce7c625f4b5bbbf3dd10d2387474\",\"instanceOfferingUuid\":\"b219364de4d840349938c2ca238eb1ee\",\"rootVolumeUuid\":\"c28b1bd97d8247bbbf60b8c86276dceb\",\"defaultL3NetworkUuid\":\"81c8aafb064d4ba2944227da9555c5f0\",\"type\":\"UserVm\",\"hypervisorType\":\"KVM\",\"cpuNum\":4,\"cpuSpeed\":0,\"memorySize\":4294967296,\"reservedMemorySize\":0,\"platform\":\"Linux\",\"architecture\":\"x86_64\",\"allocatorStrategy\":\"LeastVmPreferredHostAllocatorStrategy\",\"guestOsType\":\"CentOS\",\"createDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:41 PM\",\"state\":\"Running\",\"uuid\":\"56583eb6f40e40d1a28f23b7d34b4f5b\",\"resourceName\":\"vm3\",\"resourceType\":\"VmInstanceVO\",\"concreteResourceType\":\"org.zstack.header.vm.VmInstanceVO\"}", +// "volumeVO" : "{\"name\":\"ROOT-for-vm2\",\"description\":\"Root volume for VM[uuid:7f9d54d410d4415ea744b9b85ccdc84d]\",\"primaryStorageUuid\":\"ad0e38a8249e4b129e5538fe5dc1c59e\",\"vmInstanceUuid\":\"7f9d54d410d4415ea744b9b85ccdc84d\",\"rootImageUuid\":\"e5875b4855f2466cbef440358318d7dd\",\"installPath\":\"/zstack_smp/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-34e8e378948948fcb273cf29a4c16831/34e8e378948948fcb273cf29a4c16831.qcow2\",\"type\":\"Root\",\"status\":\"Ready\",\"size\":0,\"actualSize\":0,\"deviceId\":0,\"format\":\"qcow2\",\"state\":\"Enabled\",\"createDate\":\"Jan 18, 2026 10:34:39 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:39 PM\",\"lastAttachDate\":\"Jan 18, 2026 10:34:39 PM\",\"isShareable\":false,\"shadow\":{\"name\":\"ROOT-for-vm2\",\"description\":\"Root volume for VM[uuid:7f9d54d410d4415ea744b9b85ccdc84d]\",\"primaryStorageUuid\":\"ad0e38a8249e4b129e5538fe5dc1c59e\",\"vmInstanceUuid\":\"7f9d54d410d4415ea744b9b85ccdc84d\",\"rootImageUuid\":\"e5875b4855f2466cbef440358318d7dd\",\"installPath\":\"/zstack_smp/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-34e8e378948948fcb273cf29a4c16831/34e8e378948948fcb273cf29a4c16831.qcow2\",\"type\":\"Root\",\"status\":\"Ready\",\"size\":0,\"actualSize\":0,\"deviceId\":0,\"format\":\"qcow2\",\"state\":\"Enabled\",\"createDate\":\"Jan 18, 2026 10:34:39 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:39 PM\",\"lastAttachDate\":\"Jan 18, 2026 10:34:39 PM\",\"isShareable\":false},\"uuid\":\"34e8e378948948fcb273cf29a4c16831\",\"resourceName\":\"ROOT-for-vm2\",\"resourceType\":\"VolumeVO\",\"concreteResourceType\":\"org.zstack.header.volume.VolumeVO\"}", +// "vmNicVO" : "{\"vmInstanceUuid\":\"56583eb6f40e40d1a28f23b7d34b4f5b\",\"l3NetworkUuid\":\"81c8aafb064d4ba2944227da9555c5f0\",\"mac\":\"fa:b2:1a:b5:3a:00\",\"hypervisorType\":\"KVM\",\"deviceId\":0,\"internalName\":\"vnic3.0\",\"driverType\":\"virtio\",\"type\":\"VNIC\",\"state\":\"enable\",\"createDate\":\"Jan 18, 2026 10:34:40 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:40 PM\",\"usedIps\":[],\"uuid\":\"07fdf6d90be1460f9b7ad949cc39c893\",\"resourceType\":\"VmNicVO\",\"concreteResourceType\":\"org.zstack.header.vm.VmNicVO\"}", +// "volumeSnapshots" : { +// "82b1c604f3fc44e1b863efc993e88223" : [ "{\"uuid\":\"3214fb7d25154a07956b65da51ef64d2\",\"name\":\"test-snap-ROOT-for-vm1\",\"type\":\"Hypervisor\",\"volumeUuid\":\"82b1c604f3fc44e1b863efc993e88223\",\"treeUuid\":\"f427df56f5dc4c7592a310b5c128520b\",\"primaryStorageUuid\":\"95e0442313234b7d890be67ab232af3f\",\"primaryStorageInstallPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/82b1c604f3fc44e1b863efc993e88223.qcow2\",\"volumeType\":\"Root\",\"format\":\"qcow2\",\"latest\":false,\"size\":0,\"distance\":1,\"state\":\"Enabled\",\"status\":\"Ready\",\"createDate\":\"Jan 18, 2026 10:34:42 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:43 PM\",\"backupStorageRefs\":[],\"groupUuid\":\"36e0512b36984d40b781efe561e8fdd6\"}", "{\"uuid\":\"bdd615cb76de4a1b9ef26fc3525799a2\",\"name\":\"test-snap-ROOT-for-vm1\",\"type\":\"Hypervisor\",\"volumeUuid\":\"82b1c604f3fc44e1b863efc993e88223\",\"treeUuid\":\"f427df56f5dc4c7592a310b5c128520b\",\"parentUuid\":\"3214fb7d25154a07956b65da51ef64d2\",\"primaryStorageUuid\":\"95e0442313234b7d890be67ab232af3f\",\"primaryStorageInstallPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/snapshots/d0abc4fad2e34b5a9d70c628a38178ec.qcow2\",\"volumeType\":\"Root\",\"format\":\"qcow2\",\"latest\":true,\"size\":0,\"distance\":2,\"state\":\"Enabled\",\"status\":\"Ready\",\"createDate\":\"Jan 18, 2026 10:34:43 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:43 PM\",\"backupStorageRefs\":[],\"groupUuid\":\"1c9321907b9d458fae5753e84e77f689\"}" ] +// }, +// "volumeSnapshotGroupVO" : [ "{\"snapshotCount\":1,\"name\":\"test-snap\",\"vmInstanceUuid\":\"e045e8df65804beab29b6744e0a8fbf1\",\"createDate\":\"Jan 18, 2026 10:34:43 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:43 PM\",\"volumeSnapshotRefs\":[{\"volumeSnapshotUuid\":\"bdd615cb76de4a1b9ef26fc3525799a2\",\"volumeSnapshotGroupUuid\":\"1c9321907b9d458fae5753e84e77f689\",\"snapshotDeleted\":false,\"deviceId\":0,\"volumeUuid\":\"82b1c604f3fc44e1b863efc993e88223\",\"volumeName\":\"ROOT-for-vm1\",\"volumeType\":\"Root\",\"volumeSnapshotInstallPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/snapshots/d0abc4fad2e34b5a9d70c628a38178ec.qcow2\",\"volumeSnapshotName\":\"test-snap-ROOT-for-vm1\",\"createDate\":\"Jan 18, 2026 10:34:43 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:43 PM\",\"volumeLastAttachDate\":\"Jan 18, 2026 10:34:38 PM\"}],\"uuid\":\"1c9321907b9d458fae5753e84e77f689\",\"resourceName\":\"test-snap\",\"resourceType\":\"VolumeSnapshotGroupVO\",\"concreteResourceType\":\"org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO\"}", "{\"snapshotCount\":1,\"name\":\"test-snap\",\"vmInstanceUuid\":\"e045e8df65804beab29b6744e0a8fbf1\",\"createDate\":\"Jan 18, 2026 10:34:42 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:42 PM\",\"volumeSnapshotRefs\":[{\"volumeSnapshotUuid\":\"3214fb7d25154a07956b65da51ef64d2\",\"volumeSnapshotGroupUuid\":\"36e0512b36984d40b781efe561e8fdd6\",\"snapshotDeleted\":false,\"deviceId\":0,\"volumeUuid\":\"82b1c604f3fc44e1b863efc993e88223\",\"volumeName\":\"ROOT-for-vm1\",\"volumeType\":\"Root\",\"volumeSnapshotInstallPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/82b1c604f3fc44e1b863efc993e88223.qcow2\",\"volumeSnapshotName\":\"test-snap-ROOT-for-vm1\",\"createDate\":\"Jan 18, 2026 10:34:42 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:42 PM\",\"volumeLastAttachDate\":\"Jan 18, 2026 10:34:38 PM\"}],\"uuid\":\"36e0512b36984d40b781efe561e8fdd6\",\"resourceName\":\"test-snap\",\"resourceType\":\"VolumeSnapshotGroupVO\",\"concreteResourceType\":\"org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO\"}" ], +// "volumeSnapshotGroupRefVO" : [ "{\"volumeSnapshotUuid\":\"3214fb7d25154a07956b65da51ef64d2\",\"volumeSnapshotGroupUuid\":\"36e0512b36984d40b781efe561e8fdd6\",\"snapshotDeleted\":false,\"deviceId\":0,\"volumeUuid\":\"82b1c604f3fc44e1b863efc993e88223\",\"volumeName\":\"ROOT-for-vm1\",\"volumeType\":\"Root\",\"volumeSnapshotInstallPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/82b1c604f3fc44e1b863efc993e88223.qcow2\",\"volumeSnapshotName\":\"test-snap-ROOT-for-vm1\",\"createDate\":\"Jan 18, 2026 10:34:42 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:42 PM\",\"volumeLastAttachDate\":\"Jan 18, 2026 10:34:38 PM\"}", "{\"volumeSnapshotUuid\":\"bdd615cb76de4a1b9ef26fc3525799a2\",\"volumeSnapshotGroupUuid\":\"1c9321907b9d458fae5753e84e77f689\",\"snapshotDeleted\":false,\"deviceId\":0,\"volumeUuid\":\"82b1c604f3fc44e1b863efc993e88223\",\"volumeName\":\"ROOT-for-vm1\",\"volumeType\":\"Root\",\"volumeSnapshotInstallPath\":\"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/snapshots/d0abc4fad2e34b5a9d70c628a38178ec.qcow2\",\"volumeSnapshotName\":\"test-snap-ROOT-for-vm1\",\"createDate\":\"Jan 18, 2026 10:34:43 PM\",\"lastOpDate\":\"Jan 18, 2026 10:34:43 PM\",\"volumeLastAttachDate\":\"Jan 18, 2026 10:34:38 PM\"}" ] +// } + + // VmMetadata vmMetadata1 = JSONObjectUtil.toObject(json, VmMetadata.class); + // 结果 = {VmInstanceBase$VmMetadata@51572} + // vmInstanceVO = "{"vmNics":[{"vmInstanceUuid":"56583eb6f40e40d1a28f23b7d34b4f5b","l3NetworkUuid":"81c8aafb064d4ba2944227da9555c5f0","mac":"fa:b2:1a:b5:3a:00","hypervisorType":"KVM","deviceId":0,"internalName":"vnic3.0","driverType":"virtio","type":"VNIC","state":"enable","createDate":"Jan 18, 2026 10:34:40 PM","lastOpDate":"Jan 18, 2026 10:34:40 PM","usedIps":[],"uuid":"07fdf6d90be1460f9b7ad949cc39c893","resourceType":"VmNicVO","concreteResourceType":"org.zstack.header.vm.VmNicVO"}],"allVolumes":[{"name":"DATA-for-vm3","description":"DataVolume-56583eb6f40e40d1a28f23b7d34b4f5b","primaryStorageUuid":"95e0442313234b7d890be67ab232af3f","vmInstanceUuid":"56583eb6f40e40d1a28f23b7d34b4f5b","diskOfferingUuid":"d2f0438b52a142a981ebfb62cc8dc11a","installPath":"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/dataVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-60c5546c30de4f09afaa243ae951dc63/60c5546c30de4f09afaa243ae951dc63.qcow2","type":"Data","status":"Ready","size":10737418240,"actualSiz" + // vmConfigs = null + // volumeVO = "{"name":"ROOT-for-vm2","description":"Root volume for VM[uuid:7f9d54d410d4415ea744b9b85ccdc84d]","primaryStorageUuid":"ad0e38a8249e4b129e5538fe5dc1c59e","vmInstanceUuid":"7f9d54d410d4415ea744b9b85ccdc84d","rootImageUuid":"e5875b4855f2466cbef440358318d7dd","installPath":"/zstack_smp/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-34e8e378948948fcb273cf29a4c16831/34e8e378948948fcb273cf29a4c16831.qcow2","type":"Root","status":"Ready","size":0,"actualSize":0,"deviceId":0,"format":"qcow2","state":"Enabled","createDate":"Jan 18, 2026 10:34:39 PM","lastOpDate":"Jan 18, 2026 10:34:39 PM","lastAttachDate":"Jan 18, 2026 10:34:39 PM","isShareable":false,"shadow":{"name":"ROOT-for-vm2","description":"Root volume for VM[uuid:7f9d54d410d4415ea744b9b85ccdc84d]","primaryStorageUuid":"ad0e38a8249e4b129e5538fe5dc1c59e","vmInstanceUuid":"7f9d54d410d4415ea744b9b85ccdc84d","rootImageUuid":"e5875b4855f2466cbef440358318d7dd","installPath":"/zstack_smp/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/v" + // volumeConfigs = null + // vmNicVO = "{"vmInstanceUuid":"56583eb6f40e40d1a28f23b7d34b4f5b","l3NetworkUuid":"81c8aafb064d4ba2944227da9555c5f0","mac":"fa:b2:1a:b5:3a:00","hypervisorType":"KVM","deviceId":0,"internalName":"vnic3.0","driverType":"virtio","type":"VNIC","state":"enable","createDate":"Jan 18, 2026 10:34:40 PM","lastOpDate":"Jan 18, 2026 10:34:40 PM","usedIps":[],"uuid":"07fdf6d90be1460f9b7ad949cc39c893","resourceType":"VmNicVO","concreteResourceType":"org.zstack.header.vm.VmNicVO"}" + // vmNicConfigs = null + // volumeSnapshots = {LinkedTreeMap@51576} size = 1 + // "82b1c604f3fc44e1b863efc993e88223" -> {ArrayList@51587} size = 2 + // volumeSnapshotGroupVO = {ArrayList@51577} size = 2 + // 0 = "{"snapshotCount":1,"name":"test-snap","vmInstanceUuid":"e045e8df65804beab29b6744e0a8fbf1","createDate":"Jan 18, 2026 10:34:43 PM","lastOpDate":"Jan 18, 2026 10:34:43 PM","volumeSnapshotRefs":[{"volumeSnapshotUuid":"bdd615cb76de4a1b9ef26fc3525799a2","volumeSnapshotGroupUuid":"1c9321907b9d458fae5753e84e77f689","snapshotDeleted":false,"deviceId":0,"volumeUuid":"82b1c604f3fc44e1b863efc993e88223","volumeName":"ROOT-for-vm1","volumeType":"Root","volumeSnapshotInstallPath":"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/snapshots/d0abc4fad2e34b5a9d70c628a38178ec.qcow2","volumeSnapshotName":"test-snap-ROOT-for-vm1","createDate":"Jan 18, 2026 10:34:43 PM","lastOpDate":"Jan 18, 2026 10:34:43 PM","volumeLastAttachDate":"Jan 18, 2026 10:34:38 PM"}],"uuid":"1c9321907b9d458fae5753e84e77f689","resourceName":"test-snap","resourceType":"VolumeSnapshotGroupVO","concreteResourceType":"org.zstack.he" + // 1 = "{"snapshotCount":1,"name":"test-snap","vmInstanceUuid":"e045e8df65804beab29b6744e0a8fbf1","createDate":"Jan 18, 2026 10:34:42 PM","lastOpDate":"Jan 18, 2026 10:34:42 PM","volumeSnapshotRefs":[{"volumeSnapshotUuid":"3214fb7d25154a07956b65da51ef64d2","volumeSnapshotGroupUuid":"36e0512b36984d40b781efe561e8fdd6","snapshotDeleted":false,"deviceId":0,"volumeUuid":"82b1c604f3fc44e1b863efc993e88223","volumeName":"ROOT-for-vm1","volumeType":"Root","volumeSnapshotInstallPath":"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/82b1c604f3fc44e1b863efc993e88223.qcow2","volumeSnapshotName":"test-snap-ROOT-for-vm1","createDate":"Jan 18, 2026 10:34:42 PM","lastOpDate":"Jan 18, 2026 10:34:42 PM","volumeLastAttachDate":"Jan 18, 2026 10:34:38 PM"}],"uuid":"36e0512b36984d40b781efe561e8fdd6","resourceName":"test-snap","resourceType":"VolumeSnapshotGroupVO","concreteResourceType":"org.zstack.header.stora" + // volumeSnapshotGroupRefVO = {ArrayList@51578} size = 2 + // 0 = "{"volumeSnapshotUuid":"3214fb7d25154a07956b65da51ef64d2","volumeSnapshotGroupUuid":"36e0512b36984d40b781efe561e8fdd6","snapshotDeleted":false,"deviceId":0,"volumeUuid":"82b1c604f3fc44e1b863efc993e88223","volumeName":"ROOT-for-vm1","volumeType":"Root","volumeSnapshotInstallPath":"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/82b1c604f3fc44e1b863efc993e88223.qcow2","volumeSnapshotName":"test-snap-ROOT-for-vm1","createDate":"Jan 18, 2026 10:34:42 PM","lastOpDate":"Jan 18, 2026 10:34:42 PM","volumeLastAttachDate":"Jan 18, 2026 10:34:38 PM"}" + // 1 = "{"volumeSnapshotUuid":"bdd615cb76de4a1b9ef26fc3525799a2","volumeSnapshotGroupUuid":"1c9321907b9d458fae5753e84e77f689","snapshotDeleted":false,"deviceId":0,"volumeUuid":"82b1c604f3fc44e1b863efc993e88223","volumeName":"ROOT-for-vm1","volumeType":"Root","volumeSnapshotInstallPath":"/opt/zstack/nfsprimarystorage/prim-95e0442313234b7d890be67ab232af3f/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-82b1c604f3fc44e1b863efc993e88223/snapshots/d0abc4fad2e34b5a9d70c628a38178ec.qcow2","volumeSnapshotName":"test-snap-ROOT-for-vm1","createDate":"Jan 18, 2026 10:34:43 PM","lastOpDate":"Jan 18, 2026 10:34:43 PM","volumeLastAttachDate":"Jan 18, 2026 10:34:38 PM"}" + // volumeSnapshotReferenceVO = null + // volumeSnapshotReferenceTreeVO = null + } + + private List getResourceSystemTagFromDb(String resourceUuid) { + List systemTags = new ArrayList<>(); + List tuples = Q.New(SystemTagVO.class).eq(SystemTagVO_.resourceUuid, resourceUuid) + .select(SystemTagVO_.tag, SystemTagVO_.inherent, SystemTagVO_.type).listTuple(); + tuples.forEach(t -> { + String tag = String.format("%s_%s_%s", t.get(0, String.class), t.get(1, Boolean.class), t.get(2, TagType.class).toString()); + systemTags.add(tag); + }); + return systemTags; + } + + private List getResourceConfigFromDb(String resourceUuid) { + List resourceConfigs = new ArrayList<>(); + List tuples = Q.New(ResourceConfigVO.class).eq(ResourceConfigVO_.resourceUuid, resourceUuid) + .select(ResourceConfigVO_.category, ResourceConfigVO_.name, ResourceConfigVO_.value).listTuple(); + + tuples.forEach(t -> { + String config = String.format("%s.%s_%s", t.get(0, String.class), t.get(1, String.class), t.get(2, String.class)); + resourceConfigs.add(config); + }); + return resourceConfigs; + } + + private List getResourceSystemTagFromSystem(String resourceType) throws IllegalAccessException, InstantiationException { + List systemTags = new ArrayList<>(); + + Set> classes = BeanUtils.reflections.getTypesAnnotatedWith(TagDefinition.class); + for (Class clazz : classes) { + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (!SystemTag.class.isAssignableFrom(field.getType())) { + continue; + } + + SystemTag systemTag = (SystemTag) field.get(clazz.newInstance()); + + if (resourceType.equals(systemTag.getResourceClass().getName())) { + systemTags.add(systemTag); + } + } + } + return systemTags; + } + + private List getResourceConfigFromSystem(String resourceType) throws IllegalAccessException, InstantiationException { + List globalConfigs = new ArrayList<>(); + + Set> classes = BeanUtils.reflections.getTypesAnnotatedWith(GlobalConfigDefinition.class); + for (Class clazz : classes) { + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (!GlobalConfig.class.isAssignableFrom(field.getType())) { + continue; + } + GlobalConfig globalConfig = (GlobalConfig) field.get(clazz.newInstance()); + + BindResourceConfig bindResourceConfig = field.getAnnotation(BindResourceConfig.class); + if (bindResourceConfig == null) { + continue; + } + + List bindResourceConfigs = Arrays.stream(bindResourceConfig.value()).map(Class::getName).collect(Collectors.toList()); + + if (bindResourceConfigs.contains(resourceType)) { + globalConfigs.add(globalConfig); + } + } + } + + return globalConfigs; + } + + private void handle(UpdateVmInstanceMetadataMsg msg) { + thdf.chainSubmit(new ChainTask(msg) { + @Override + public String getSyncSignature() { + return syncThreadName; + } + + @Override + public void run(SyncTaskChain chain) { + UpdateVmInstanceMetadataReply reply = new UpdateVmInstanceMetadataReply(); + UpdateVmInstanceMetadataOnHypervisorMsg umsg = new UpdateVmInstanceMetadataOnHypervisorMsg(); + umsg.setVmInstanceMetadata(buildVmInstanceMetadata(msg.getUuid())); + if (self.getHostUuid() != null) { + umsg.setHostUuid(self.getHostUuid()); + } else if (self.getLastHostUuid() != null) { + umsg.setHostUuid(self.getLastHostUuid()); + } + bus.makeTargetServiceIdByResourceUuid(umsg, HostConstant.SERVICE_ID, umsg.getHostUuid()); + bus.send(umsg, new CloudBusCallBack(msg) { + @Override + public void run(MessageReply innerReply) { + if (!innerReply.isSuccess()) { + reply.setError(Platform.operr("failed to update vm[uuid=%s] on hypervisor.", self.getUuid()) + .withCause(innerReply.getError())); + + gc(); + return; + } + bus.reply(msg, reply); + chain.next(); + } + + private void gc() { +// SubmitGarbageCollectorMsg gcmsg = new SubmitGarbageCollectorMsg(); +// gcmsg.setGcInterval(VmGlobalConfig.GC_INTERVAL.value(Long.class)); +// gcmsg.setUnit(TimeUnit.SECONDS); +// +// UpdateVmInstanceMetadataGC gc = new UpdateVmInstanceMetadataGC(); +// gc.vmInstanceUuid = self.getUuid(); +// gc.NAME = String.format("gc-update-vm-%s-metadata", self.getUuid()); +// gcmsg.setGc(gc); + +// bus.makeTargetServiceIdByResourceUuid(gcmsg, GCConstants.SERVICE_ID, umsg.getHostUuid()); +// bus.send(gcmsg); + } + }); + } + + @Override + public String getName() { + return "update-vm-info"; + } + }); + } + + private void handle(APIRegisterVmInstanceMsg msg) { + APIRegisterVmInstanceEvent evnet = new APIRegisterVmInstanceEvent(msg.getId()); + thdf.chainSubmit(new ChainTask(msg) { + @Override + public String getSyncSignature() { + return syncThreadName; + } + + @Override + public void run(SyncTaskChain chain) { + RegisterVmInstance(msg, new Completion(msg, chain) { + @Override + public void success() { + bus.publish(evnet); + chain.next(); + } + + @Override + public void fail(ErrorCode errorCode) { + bus.publish(evnet); + chain.next(); + } + }); + + } + + @Override + public String getName() { + return String.format("register-vm-from-%s", msg.getMetadataPath()); + } + }); + } + + private void RegisterVmInstance(APIRegisterVmInstanceMsg msg, Completion completion) { + FlowChain chain = new ShareFlowChain(); + chain.setName("register-vm-from-metadata"); + chain.then(new ShareFlow() { + VmMetadata vmMetadata; + + @Override + public void setup() { + flow(new NoRollbackFlow() { + String __name__ = "read-metadata"; + + @Override + public void run(FlowTrigger trigger, Map data) { + ReadVmInstanceMetadataOnHypervisorReply reply = new ReadVmInstanceMetadataOnHypervisorReply(); + ReadVmInstanceMetadataOnHypervisorMsg umsg = new ReadVmInstanceMetadataOnHypervisorMsg(); + bus.makeTargetServiceIdByResourceUuid(umsg, HostConstant.SERVICE_ID, "umsg.getHostUuid()"); + bus.send(umsg, new CloudBusCallBack(msg) { + @Override + public void run(MessageReply innerReply) { + if (!innerReply.isSuccess()) { + reply.setError(Platform.operr("failed to update vm[uuid=%s] on hypervisor.", + self.getUuid()).withCause(innerReply.getError())); + return; + } + vmMetadata = JSONObjectUtil.toObject(reply.getVmMetadata(), VmMetadata.class); + trigger.next(); + } + }); + } + }); + + flow(new NoRollbackFlow() { + String __name__ = "register-volume"; + + @Override + public void run(FlowTrigger trigger, Map data) { + + List volumesString = vmMetadata.volumeVOs; + + List volumes = new ArrayList<>(); + volumesString.forEach(v -> volumes.add(JSONObjectUtil.toObject(v, VolumeVO.class))); + + List newVolumes = new ArrayList<>(); + volumes.forEach(v -> { + VolumeVO vo = new VolumeVO(); + vo.setUuid(v.getUuid()); + vo.setDescription(v.getDescription()); + vo.setName(v.getName()); + vo.setSize(v.getSize()); + vo.setActualSize(v.getActualSize()); + vo.setType(v.getType()); + vo.setStatus(v.getStatus()); + vo.setAccountUuid(v.getAccountUuid()); + vo.setProtocol(v.getProtocol()); + vo.setPrimaryStorageUuid(vo.getPrimaryStorageUuid()); + newVolumes.add(vo); + }); + } + }); + + flow(new NoRollbackFlow() { + String __name__ = "register-snapshot"; + + @Override + public void run(FlowTrigger trigger, Map data) { + vmMetadata.volumeSnapshots.forEach((volumeUuid, snapshotList) -> { + // 一个 volume 有多个快照树 + // key = treeuuid + // value = snapshosts + Map> snapshotsByTreeUuid = new HashMap<>(); + snapshotList.forEach(snapshot -> { + VolumeSnapshotInventory inv = JSONObjectUtil.toObject(snapshot, VolumeSnapshotInventory.class); + if (snapshotsByTreeUuid.containsKey(inv.getTreeUuid())) { + snapshotsByTreeUuid.get(inv.getTreeUuid()).add(inv); + } else { + snapshotsByTreeUuid.put(inv.getTreeUuid(), new ArrayList<>()); + snapshotsByTreeUuid.get(inv.getTreeUuid()).add(inv); + } + }); + + // 遍历每一颗树 + snapshotsByTreeUuid.forEach((treeUuid, s) -> { + //构建快照树 + VolumeSnapshotTree tree = VolumeSnapshotTree.fromInventories(s); + // 层级遍历 快照 + List levelOrderTraversals = tree.levelOrderTraversal(); + // 判断当前树有没有 latest 节点 + boolean treeIsCurrent = levelOrderTraversals.stream().anyMatch(VolumeSnapshotInventory::isLatest); + + // 先创建快照树,VolumeSnapshotVO 外键依赖 VolumeSnapshotTreeVO + VolumeSnapshotTreeVO newTree = new VolumeSnapshotTreeVO(); + newTree.setCurrent(treeIsCurrent); + newTree.setVolumeUuid(volumeUuid); + newTree.setUuid(Platform.getUuid()); + newTree.setStatus(VolumeSnapshotTreeStatus.Completed); + dbf.persist(newTree); + + // 按照层级遍历的快照构建VolumeSnapshotTreeVO + levelOrderTraversals.forEach(sss -> { + VolumeSnapshotVO vo = new VolumeSnapshotVO(); + vo.setName(sss.getName()); + vo.setCreateDate(sss.getCreateDate()); + vo.setDescription(sss.getDescription()); + vo.setLastOpDate(sss.getLastOpDate()); + vo.setParentUuid(sss.getParentUuid()); + vo.setState(vo.getState()); + vo.setType(sss.getType()); + vo.setVolumeUuid(sss.getVolumeUuid()); + vo.setFormat(sss.getFormat()); + vo.setUuid(sss.getUuid()); + vo.setStatus(vo.getStatus()); + vo.setPrimaryStorageUuid(sss.getPrimaryStorageUuid()); + vo.setPrimaryStorageInstallPath(sss.getPrimaryStorageInstallPath()); + vo.setLatest(sss.isLatest()); + vo.setSize(sss.getSize()); + vo.setVolumeType(sss.getVolumeType()); + vo.setTreeUuid(sss.getTreeUuid()); + vo.setDistance(sss.getDistance()); + // VolumeSnapshotVO.parentUuid 外键依赖 父节点,因此,一个一个持久化 + dbf.persist(vo); + }); + }); + }); + + // 先持久化 快照组 + List newGroups = new ArrayList<>(); + vmMetadata.volumeSnapshotGroupVO.forEach(group -> { + VolumeSnapshotGroupVO vo = JSONObjectUtil.toObject(group, VolumeSnapshotGroupVO.class); + vo.setAccountUuid(msg.getSession().getAccountUuid()); + newGroups.add(vo); + }); + dbf.persistCollection(newGroups); + + List newGroupRefs = new ArrayList<>(); + vmMetadata.volumeSnapshotGroupRefVO.forEach(group -> { + VolumeSnapshotGroupRefVO vo = JSONObjectUtil.toObject(group, VolumeSnapshotGroupRefVO.class); + newGroupRefs.add(vo); + }); + dbf.persistCollection(newGroupRefs); + + trigger.next(); + } + }); + + flow(new NoRollbackFlow() { + String __name__ = "register-snapshot"; + + @Override + public void run(FlowTrigger trigger, Map data) { + VmInstanceVO vmInstanceString = JSONObjectUtil.toObject(vmMetadata.vmInstanceVO, VmInstanceVO.class); + VmInstanceVO vm = new VmInstanceVO(); + + List vmSystemTags = vmMetadata.vmSystemTags; + List vmResourceConfigs = vmMetadata.vmResourceConfigs; + + try { + List systemTags = getResourceSystemTagFromSystem(VmInstanceVO.class.getSimpleName()); + List resourceConfigs = getResourceConfigFromSystem(VmInstanceVO.class.getSimpleName()); + + List tagVOS = new ArrayList<>(); + vmSystemTags.forEach(tag -> { + List info = asList(tag.split("_")); + String t = info.get(0); + Boolean inherent = Boolean.valueOf(info.get(1)); + String type = info.get(2); + systemTags.forEach(it -> { + if (!it.isMatch(t)) { + return; + } + SystemTagVO vo = new SystemTagVO(); + vo.setTag(t); + vo.setType(TagType.valueOf(type)); + vo.setInherent(inherent); + vo.setResourceType(VmInstanceVO.class.getSimpleName()); + vo.setResourceUuid(vm.getUuid()); + tagVOS.add(vo); + }); + }); + + List configVOS = new ArrayList<>(); + vmResourceConfigs.forEach(tag -> { + List info = asList(tag.split("_")); + String identity = info.get(0); + String value = info.get(1); + resourceConfigs.forEach(it -> { + if (it.getIdentity() == identity) { + return; + } + ResourceConfigVO vo = new ResourceConfigVO(); + vo.setCategory(identity); + vo.setName(identity); + vo.setValue(value); + vo.setResourceType(VmInstanceVO.class.getSimpleName()); + vo.setResourceUuid(vm.getUuid()); + configVOS.add(vo); + }); + }); + } catch (IllegalAccessException | InstantiationException e) { + throw new RuntimeException(e); + } + + List volumesString = vmMetadata.volumeVOs; + List volumes = new ArrayList<>(); + volumesString.forEach(v -> volumes.add(JSONObjectUtil.toObject(v, VolumeVO.class))); + volumes.forEach(v -> { + VolumeVO volume = new VolumeVO(); + }); + + Map> volumeSnapshots = vmMetadata.volumeSnapshots; + Set snapshotTreeUuid = new HashSet<>(); + volumeSnapshots.forEach((volumeUuid, snapshots) -> { + snapshots.forEach(snapshot -> { + VolumeSnapshotInventory inv = JSONObjectUtil.toObject(snapshot, VolumeSnapshotInventory.class); + VolumeSnapshotVO vo = new VolumeSnapshotVO(); + snapshotTreeUuid.add(inv.toString()); + }); + }); + + List volumeSnapshotGroupVO = vmMetadata.volumeSnapshotGroupVO; + volumeSnapshotGroupVO.forEach(group -> { + VolumeSnapshotGroupVO inv = JSONObjectUtil.toObject(group, VolumeSnapshotGroupVO.class); + VolumeSnapshotGroupVO newGroup = new VolumeSnapshotGroupVO(); +// newGroup = new VolumeSnapshotGroupVO(); +// newGroup.setUuid(Platform.getUuid()); +// newGroup.setName(String.format("revert-vm-point-%s-%s", vmUuid, TimeUtils.getCurrentTimeStamp("yyyyMMddHHmmss"))); +// newGroup.setDescription(String.format("save snapshot for revert vm [uuid:%s]", vmUuid)); +// newGroup.setSnapshotCount(snapshots.size()); +// newGroup.setVmInstanceUuid(vmUuid); +// newGroup.setAccountUuid(msg.getSession().getAccountUuid()); + }); + + List volumeSnapshotGroupRefVO = vmMetadata.volumeSnapshotGroupRefVO; + volumeSnapshotGroupRefVO.forEach(group -> { + VolumeSnapshotGroupRefVO inv = JSONObjectUtil.toObject(group, VolumeSnapshotGroupRefVO.class); + +// VolumeSnapshotGroupRefVO ref = new VolumeSnapshotGroupRefVO(); +// ref.setVolumeUuid(inv.getVolumeUuid()); +// ref.setVolumeName(vols.get(inv.getVolumeUuid()).getName()); +// ref.setVolumeType(inv.getVolumeType()); +// ref.setVolumeSnapshotGroupUuid(group.getUuid()); +// ref.setVolumeSnapshotUuid(inv.getUuid()); +// ref.setVolumeSnapshotName(inv.getName()); +// ref.setVolumeSnapshotInstallPath(inv.getPrimaryStorageInstallPath()); +// ref.setDeviceId(vols.get(inv.getVolumeUuid()).getDeviceId()); +// ref.setVolumeLastAttachDate(vols.get(inv.getVolumeUuid()).getLastAttachDate()); + }); + } + }); + + done(new FlowDoneHandler(completion) { + @Override + public void handle(Map data) { + completion.success(); + } + }); + + error(new FlowErrorHandler(msg) { + @Override + public void handle(ErrorCode errCode, Map data) { + completion.fail(errCode); + } + }); + } + }).start(); + } } diff --git a/conf/serviceConfig/vmInstance.xml b/conf/serviceConfig/vmInstance.xml index ab73fb6bea9..f677e462059 100755 --- a/conf/serviceConfig/vmInstance.xml +++ b/conf/serviceConfig/vmInstance.xml @@ -264,4 +264,7 @@ org.zstack.header.vm.APIDeleteTemplatedVmInstanceMsg + + org.zstack.header.vm.APIRegisterVmInstanceMsg + diff --git a/core/src/main/java/org/zstack/core/gc/GarbageCollectorManagerImpl.java b/core/src/main/java/org/zstack/core/gc/GarbageCollectorManagerImpl.java index 9cbc35b3217..9848df1cf0f 100755 --- a/core/src/main/java/org/zstack/core/gc/GarbageCollectorManagerImpl.java +++ b/core/src/main/java/org/zstack/core/gc/GarbageCollectorManagerImpl.java @@ -255,6 +255,8 @@ public void handleMessage(Message msg) { private void handleLocalMessage(Message msg) { if (msg instanceof TriggerGcJobMsg) { handle((TriggerGcJobMsg) msg); + } else if (msg instanceof SubmitGarbageCollectorMsg) { + handle((SubmitGarbageCollectorMsg) msg); } else { bus.dealWithUnknownMessage(msg); } @@ -282,6 +284,12 @@ public String getName() { }); } + private void handle(final SubmitGarbageCollectorMsg msg) { + MessageReply reply = new MessageReply(); + msg.getGc().submit(msg.getGcInterval(), msg.getUnit()); + bus.reply(msg, reply); + } + private void handleApiMessage(APIMessage msg) { if (msg instanceof APITriggerGCJobMsg) { handle((APITriggerGCJobMsg) msg); diff --git a/core/src/main/java/org/zstack/core/gc/SubmitGarbageCollectorMsg.java b/core/src/main/java/org/zstack/core/gc/SubmitGarbageCollectorMsg.java new file mode 100644 index 00000000000..e854aaa43cb --- /dev/null +++ b/core/src/main/java/org/zstack/core/gc/SubmitGarbageCollectorMsg.java @@ -0,0 +1,35 @@ +package org.zstack.core.gc; + +import org.zstack.header.message.NeedReplyMessage; + +import java.util.concurrent.TimeUnit; + +public class SubmitGarbageCollectorMsg extends NeedReplyMessage { + private TimeBasedGarbageCollector gc; + private Long gcInterval; + private TimeUnit unit; + + public TimeBasedGarbageCollector getGc() { + return gc; + } + + public void setGc(TimeBasedGarbageCollector gc) { + this.gc = gc; + } + + public Long getGcInterval() { + return gcInterval; + } + + public void setGcInterval(Long gcInterval) { + this.gcInterval = gcInterval; + } + + public TimeUnit getUnit() { + return unit; + } + + public void setUnit(TimeUnit unit) { + this.unit = unit; + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotTree.java b/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotTree.java index 57b4fab4099..cd6a33b4cd3 100755 --- a/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotTree.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotTree.java @@ -442,4 +442,24 @@ public SnapshotLeaf findSnapshot(String snapshotUuid) { return findSnapshot(arg -> arg.getUuid().equals(snapshotUuid)); } + + public List levelOrderTraversal() { + List result = new ArrayList<>(); + if (this.root == null) { + return result; + } + + Queue queue = new LinkedList<>(); + queue.offer(this.root); + + while (!queue.isEmpty()) { + SnapshotLeaf currentLeaf = queue.poll(); + result.add(currentLeaf.getInventory()); + for (SnapshotLeaf child : currentLeaf.getChildren()) { + queue.offer(child); + } + } + + return result; + } } diff --git a/header/src/main/java/org/zstack/header/vm/APIRegVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIRegVmInstanceMsg.java new file mode 100644 index 00000000000..d7271b74a45 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/APIRegVmInstanceMsg.java @@ -0,0 +1,42 @@ +package org.zstack.header.vm; + +import org.springframework.http.HttpMethod; +import org.zstack.header.cluster.ClusterVO; +import org.zstack.header.host.HostVO; +import org.zstack.header.message.APIMessage; +import org.zstack.header.message.APIParam; +import org.zstack.header.network.l3.L3NetworkVO; +import org.zstack.header.rest.RestRequest; + +import java.util.List; + +/** + * Created by frank on 11/12/2015. + */ +@RestRequest( + path = "/vm-instances/{uuid}/actions", + isAction = true, + method = HttpMethod.PUT, + responseClass = APIRecoverVmInstanceEvent.class +) +public class APIRegVmInstanceMsg extends APIMessage { + @APIParam(resourceType = ClusterVO.class) + private String clusterUuid; + @APIParam(resourceType = HostVO.class) + private String hostUuid; + + + + private String defaultL3NetworkUuid; + + @APIParam(resourceType = L3NetworkVO.class, required = false) + private List l3NetworkUuids; + + @APIParam(required = false) + private String vmNicParams; + + public static APIRegVmInstanceMsg __example__() { + APIRegVmInstanceMsg msg = new APIRegVmInstanceMsg(); + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceEvent.java b/header/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceEvent.java new file mode 100644 index 00000000000..6fd289d9da1 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceEvent.java @@ -0,0 +1,99 @@ +package org.zstack.header.vm; + +import org.zstack.header.allocator.HostAllocatorConstant; +import org.zstack.header.message.APIEvent; +import org.zstack.header.rest.RestResponse; +import org.zstack.header.volume.VolumeInventory; +import org.zstack.header.volume.VolumeState; +import org.zstack.header.volume.VolumeStatus; +import org.zstack.header.volume.VolumeType; +import org.zstack.utils.data.SizeUnit; + +import java.sql.Timestamp; + +import static java.util.Arrays.asList; + +@RestResponse(allTo = "inventory") +public class APIRegisterVmInstanceEvent extends APIEvent { + private VmInstanceInventory inventory; + + public APIRegisterVmInstanceEvent() { + } + + public APIRegisterVmInstanceEvent(String apiId) { + super(apiId); + } + + public VmInstanceInventory getInventory() { + return inventory; + } + + public void setInventory(VmInstanceInventory inventory) { + this.inventory = inventory; + } + + public static APIRegisterVmInstanceEvent __example__() { + APIRegisterVmInstanceEvent event = new APIRegisterVmInstanceEvent(); + + + String defaultL3Uuid = uuid(); + String rootVolumeUuid = uuid(); + + VmInstanceInventory vm = new VmInstanceInventory(); + vm.setName("Test-VM"); + vm.setUuid(uuid()); + vm.setAllocatorStrategy(HostAllocatorConstant.LAST_HOST_PREFERRED_ALLOCATOR_STRATEGY_TYPE); + vm.setClusterUuid(uuid()); + vm.setCpuNum(1); + vm.setCreateDate(new Timestamp(org.zstack.header.message.DocUtils.date)); + vm.setDefaultL3NetworkUuid(defaultL3Uuid); + vm.setDescription("web server VM"); + vm.setHostUuid(uuid()); + vm.setHypervisorType("KVM"); + vm.setImageUuid(uuid()); + vm.setInstanceOfferingUuid(uuid()); + vm.setLastHostUuid(uuid()); + vm.setMemorySize(SizeUnit.GIGABYTE.toByte(8)); + vm.setPlatform("Linux"); + vm.setRootVolumeUuid(rootVolumeUuid); + vm.setState(VmInstanceState.Stopped.toString()); + vm.setType(VmInstanceConstant.USER_VM_TYPE); + vm.setLastOpDate(new Timestamp(org.zstack.header.message.DocUtils.date)); + vm.setZoneUuid(uuid()); + + VolumeInventory vol = new VolumeInventory(); + vol.setName(String.format("Root-Volume-For-VM-%s", vm.getUuid())); + vol.setCreateDate(new Timestamp(org.zstack.header.message.DocUtils.date)); + vol.setLastOpDate(new Timestamp(org.zstack.header.message.DocUtils.date)); + vol.setType(VolumeType.Root.toString()); + vol.setUuid(rootVolumeUuid); + vol.setSize(SizeUnit.GIGABYTE.toByte(100)); + vol.setActualSize(SizeUnit.GIGABYTE.toByte(20)); + vol.setDeviceId(0); + vol.setState(VolumeState.Enabled.toString()); + vol.setFormat("qcow2"); + vol.setDiskOfferingUuid(uuid()); + vol.setInstallPath(String.format("/zstack_ps/rootVolumes/acct-36c27e8ff05c4780bf6d2fa65700f22e/vol-%s/%s.qcow2", rootVolumeUuid, rootVolumeUuid)); + vol.setStatus(VolumeStatus.Ready.toString()); + vol.setPrimaryStorageUuid(uuid()); + vol.setVmInstanceUuid(vm.getUuid()); + vol.setRootImageUuid(vm.getImageUuid()); + vm.setAllVolumes(asList(vol)); + + VmNicInventory nic = new VmNicInventory(); + nic.setVmInstanceUuid(vm.getUuid()); + nic.setCreateDate(vm.getCreateDate()); + nic.setLastOpDate(vm.getLastOpDate()); + nic.setDeviceId(0); + nic.setL3NetworkUuid(defaultL3Uuid); + nic.setMac("00:0c:29:bd:99:fc"); + nic.setHypervisorType("KVM"); + nic.setUuid(uuid()); + vm.setVmNics(asList(nic)); + + event.setInventory(vm); + + return event; + } + +} diff --git a/header/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceMsg.java new file mode 100644 index 00000000000..e188c2507fa --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceMsg.java @@ -0,0 +1,54 @@ +package org.zstack.header.vm; + +import org.springframework.http.HttpMethod; +import org.zstack.header.message.APIMessage; +import org.zstack.header.rest.RestRequest; + +@RestRequest( + path = "/vm-instances/{uuid}/register", + method = HttpMethod.POST, + responseClass = APIRegisterVmInstanceEvent.class +) +public class APIRegisterVmInstanceMsg extends APIMessage { + private String primaryStorageUuid; + private String clusterUuid; + private String hostUuid; + private String metadataPath; + + public String getPrimaryStorageUuid() { + return primaryStorageUuid; + } + + public void setPrimaryStorageUuid(String primaryStorageUuid) { + this.primaryStorageUuid = primaryStorageUuid; + } + + public String getClusterUuid() { + return clusterUuid; + } + + public void setClusterUuid(String clusterUuid) { + this.clusterUuid = clusterUuid; + } + + public String getHostUuid() { + return hostUuid; + } + + public void setHostUuid(String hostUuid) { + this.hostUuid = hostUuid; + } + + public String getMetadataPath() { + return metadataPath; + } + + public void setMetadataPath(String metadataPath) { + this.metadataPath = metadataPath; + } + + public static APIRegisterVmInstanceMsg __example__() { + APIRegisterVmInstanceMsg msg = new APIRegisterVmInstanceMsg(); + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataMsg.java b/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataMsg.java new file mode 100644 index 00000000000..63b98b15139 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataMsg.java @@ -0,0 +1,20 @@ +package org.zstack.header.vm; + +import org.zstack.header.message.NeedReplyMessage; + +public class ReadVmInstanceMetadataMsg extends NeedReplyMessage implements VmInstanceMessage { + private String uuid; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getVmInstanceUuid() { + return uuid; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataOnHypervisorMsg.java b/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataOnHypervisorMsg.java new file mode 100644 index 00000000000..1af5d68bf0d --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataOnHypervisorMsg.java @@ -0,0 +1,20 @@ +package org.zstack.header.vm; + +import org.zstack.header.message.NeedReplyMessage; + +public class ReadVmInstanceMetadataOnHypervisorMsg extends NeedReplyMessage implements VmInstanceMessage { + private String uuid; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getVmInstanceUuid() { + return uuid; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataOnHypervisorReply.java b/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataOnHypervisorReply.java new file mode 100644 index 00000000000..9abad5b5edd --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataOnHypervisorReply.java @@ -0,0 +1,15 @@ +package org.zstack.header.vm; + +import org.zstack.header.message.MessageReply; + +public class ReadVmInstanceMetadataOnHypervisorReply extends MessageReply { + private String vmMetadata; + + public String getVmMetadata() { + return vmMetadata; + } + + public void setVmMetadata(String vmMetadata) { + this.vmMetadata = vmMetadata; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataReply.java b/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataReply.java new file mode 100644 index 00000000000..7c2f28bf5ba --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/ReadVmInstanceMetadataReply.java @@ -0,0 +1,15 @@ +package org.zstack.header.vm; + +import org.zstack.header.message.MessageReply; + +public class ReadVmInstanceMetadataReply extends MessageReply { + private String vmMetadata; + + public String getVmMetadata() { + return vmMetadata; + } + + public void setVmMetadata(String vmMetadata) { + this.vmMetadata = vmMetadata; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataExtensionPoint.java new file mode 100644 index 00000000000..cb320a9d750 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataExtensionPoint.java @@ -0,0 +1,8 @@ +package org.zstack.header.vm; + +/** + * Created by LiangHanYu on 2022/4/13 13:24 + */ +public interface UpdateVmInstanceMetadataExtensionPoint { + void buildVmInstanceMetadata(VmInstanceInventory vmInstanceInventory, VmMetadata vmMetadata); +} diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataMsg.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataMsg.java new file mode 100644 index 00000000000..bba392b322a --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataMsg.java @@ -0,0 +1,20 @@ +package org.zstack.header.vm; + +import org.zstack.header.message.NeedReplyMessage; + +public class UpdateVmInstanceMetadataMsg extends NeedReplyMessage implements VmInstanceMessage { + private String uuid; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getVmInstanceUuid() { + return uuid; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorMsg.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorMsg.java new file mode 100644 index 00000000000..a2888f6e6a6 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorMsg.java @@ -0,0 +1,26 @@ +package org.zstack.header.vm; + +import org.zstack.header.host.HostMessage; +import org.zstack.header.message.NeedReplyMessage; + +public class UpdateVmInstanceMetadataOnHypervisorMsg extends NeedReplyMessage implements HostMessage { + private String vmInstanceMetadata; + private String hostUuid; + + public String getVmInstanceMetadata() { + return vmInstanceMetadata; + } + + public void setVmInstanceMetadata(String vmInstanceMetadata) { + this.vmInstanceMetadata = vmInstanceMetadata; + } + + @Override + public String getHostUuid() { + return hostUuid; + } + + public void setHostUuid(String hostUuid) { + this.hostUuid = hostUuid; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorReply.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorReply.java new file mode 100644 index 00000000000..c1cebeaa27b --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataOnHypervisorReply.java @@ -0,0 +1,27 @@ +package org.zstack.header.vm; + +import org.zstack.header.message.MessageReply; + +/** + * Created by xing5 on 2016/4/27. + */ +public class UpdateVmInstanceMetadataOnHypervisorReply extends MessageReply { + private long actualSize; + private long size; + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public long getActualSize() { + return actualSize; + } + + public void setActualSize(long actualSize) { + this.actualSize = actualSize; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataReply.java b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataReply.java new file mode 100644 index 00000000000..3da2e0f6cbb --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/UpdateVmInstanceMetadataReply.java @@ -0,0 +1,27 @@ +package org.zstack.header.vm; + +import org.zstack.header.message.MessageReply; + +/** + * Created by xing5 on 2016/4/27. + */ +public class UpdateVmInstanceMetadataReply extends MessageReply { + private long actualSize; + private long size; + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public long getActualSize() { + return actualSize; + } + + public void setActualSize(long actualSize) { + this.actualSize = actualSize; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/VmMetadata.java b/header/src/main/java/org/zstack/header/vm/VmMetadata.java new file mode 100644 index 00000000000..7c6ffd6c130 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/VmMetadata.java @@ -0,0 +1,52 @@ +package org.zstack.header.vm; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class VmMetadata { + public String vmInstanceVO; + // key = vmUuid + // value = SystemTag + public List vmSystemTags = new ArrayList<>(); + // key = vmUuid + // value = ResourceConfig + public List vmResourceConfigs = new ArrayList<>(); + + public List volumeVOs = new ArrayList<>(); + // key = volumeUuid + // value = SystemTag + public Map> volumeSystemTags = new HashMap<>(); + // key = volumeUuid + // value = ResourceConfig + public Map> volumeResourceConfigs = new HashMap<>(); + + public List vmNicVOs = new ArrayList<>(); + // key = nicUuid + // value = SystemTag + public Map> vmNicSystemTags = new HashMap<>(); + // key = nicUuid + // value = ResourceConfig + public Map> vmNicResourceConfigs = new HashMap<>(); + + // key = volumeUuid + // value = List + public Map> volumeSnapshots = new HashMap<>(); + + // VolumeSnapshotGroupVO.toString + public List volumeSnapshotGroupVO = new ArrayList<>(); + // VolumeSnapshotGroupRefVO.toString + public List volumeSnapshotGroupRefVO = new ArrayList<>(); + + // key = volumeUuid + // value = VolumeSnapshotReferenceVO.toString + public Map volumeSnapshotReferenceVO = new HashMap<>(); + // key = volumeUuid + // value = VolumeSnapshotReferenceTreeVO.toString + public Map volumeSnapshotReferenceTreeVO = new HashMap<>(); + + // key = volumeUuid vmUuid + // value = vo + public Map EncryptedResourceKeyRefVO = new HashMap<>(); +} diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java index 92e76ede2c5..882b1a3b322 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java @@ -4716,4 +4716,11 @@ public void setMemoryUsage(long memoryUsage) { this.memoryUsage = memoryUsage; } } + + public static class UpdateVmInstanceMetadataCmd extends AgentCommand { + public String vmInstanceMetadata; + } + + public static class UpdateVmInstanceMetadataRsp extends AgentResponse { + } } diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java index 7cd78c36c93..6c845676ca8 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java @@ -86,6 +86,9 @@ public interface KVMConstant { String CLEAN_FIRMWARE_FLASH = "/clean/firmware/flash"; String FSTRIM_VM_PATH = "/vm/fstrim"; + String WRITE_VM_INSTANCE_METADATA_PATH = "/vm/metadata/write"; + String READ_VM_INSTANCE_METADATA_PATH = "/vm/metadata/read"; + String ISO_TO = "kvm.isoto"; String ANSIBLE_PLAYBOOK_NAME = "kvm.py"; String ANSIBLE_MODULE_PATH = "ansible/kvm"; diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java index 23d7b1cfe47..dd57ea74dbc 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java @@ -230,6 +230,8 @@ public class KVMHost extends HostBase implements Host { private String fileDownloadPath; private String fileUploadPath; private String fileDownloadProgressPath; + private String writeVmInstanceMetadataPath; + private String readVmInstanceMetadataPath; public KVMHost(KVMHostVO self, KVMHostContext context) { super(self); @@ -480,6 +482,14 @@ public KVMHost(KVMHostVO self, KVMHostContext context) { ub = UriComponentsBuilder.fromHttpUrl(baseUrl); ub.path(KVMConstant.KVM_HOST_FILE_DOWNLOAD_PROGRESS_PATH); fileDownloadProgressPath = ub.build().toString(); + + ub = UriComponentsBuilder.fromHttpUrl(baseUrl); + ub.path(KVMConstant.WRITE_VM_INSTANCE_METADATA_PATH); + writeVmInstanceMetadataPath = ub.build().toString(); + + ub = UriComponentsBuilder.fromHttpUrl(baseUrl); + ub.path(KVMConstant.READ_VM_INSTANCE_METADATA_PATH); + readVmInstanceMetadataPath = ub.build().toString(); } static { @@ -738,6 +748,10 @@ protected void handleLocalMessage(Message msg) { handle((GetFileDownloadProgressMsg) msg); } else if (msg instanceof RestartKvmAgentMsg) { handle((RestartKvmAgentMsg) msg); + } else if (msg instanceof UpdateVmInstanceMetadataOnHypervisorMsg) { + handle((UpdateVmInstanceMetadataOnHypervisorMsg) msg); + } else if (msg instanceof ReadVmInstanceMetadataOnHypervisorMsg) { + handle((ReadVmInstanceMetadataOnHypervisorMsg) msg); } else { super.handleLocalMessage(msg); } @@ -4145,6 +4159,47 @@ public void handle(ErrorCode errorCode, Map data) { }).start(); } + private void handle(UpdateVmInstanceMetadataOnHypervisorMsg msg) { + UpdateVmInstanceMetadataReply reply = new UpdateVmInstanceMetadataReply(); + UpdateVmInstanceMetadataCmd cmd = new UpdateVmInstanceMetadataCmd(); + cmd.vmInstanceMetadata = msg.getVmInstanceMetadata(); + new Http<>(writeVmInstanceMetadataPath, cmd, UpdateVmInstanceMetadataRsp.class).call(new ReturnValueCompletion(msg) { + @Override + public void success(UpdateVmInstanceMetadataRsp ret) { + if (!ret.isSuccess()) { + reply.setError(operr("operation error, because:%s", ret.getError())); + } + bus.reply(msg, reply); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + + private void handle(ReadVmInstanceMetadataOnHypervisorMsg msg) { + UpdateVmInstanceMetadataReply reply = new UpdateVmInstanceMetadataReply(); + UpdateVmInstanceMetadataCmd cmd = new UpdateVmInstanceMetadataCmd(); + new Http<>(readVmInstanceMetadataPath, cmd, UpdateVmInstanceMetadataRsp.class).call(new ReturnValueCompletion(msg) { + @Override + public void success(UpdateVmInstanceMetadataRsp ret) { + if (!ret.isSuccess()) { + reply.setError(operr("operation error, because:%s", ret.getError())); + } + bus.reply(msg, reply); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + private void handle(final UpdateSpiceChannelConfigMsg msg) { UpdateSpiceChannelConfigReply reply = new UpdateSpiceChannelConfigReply(); UpdateSpiceChannelConfigCmd cmd = new UpdateSpiceChannelConfigCmd();