diff --git a/compute/src/main/java/org/zstack/compute/vm/CleanupVmInstanceMetadataOnPrimaryStorageGC.java b/compute/src/main/java/org/zstack/compute/vm/CleanupVmInstanceMetadataOnPrimaryStorageGC.java new file mode 100644 index 00000000000..2b0d904419d --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/CleanupVmInstanceMetadataOnPrimaryStorageGC.java @@ -0,0 +1,72 @@ +package org.zstack.compute.vm; + +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.header.host.HostVO; +import org.zstack.header.message.MessageReply; +import org.zstack.header.storage.primary.CleanupVmInstanceMetadataOnPrimaryStorageMsg; +import org.zstack.header.storage.primary.PrimaryStorageConstant; +import org.zstack.header.storage.primary.PrimaryStorageVO; +import org.zstack.utils.Utils; +import org.zstack.utils.logging.CLogger; + +public class CleanupVmInstanceMetadataOnPrimaryStorageGC extends TimeBasedGarbageCollector { + private static final CLogger logger = Utils.getLogger(CleanupVmInstanceMetadataOnPrimaryStorageGC.class); + + @GC + public String primaryStorageUuid; + @GC + public String vmUuid; + @GC + public String rootVolumeUuid; + @GC + public String metadataPath; + @GC + public String hostUuid; + + public static String getGCName(String vmUuid) { + return String.format("gc-cleanup-vm-metadata-%s", vmUuid); + } + + @Override + protected void triggerNow(GCCompletion completion) { + if (!dbf.isExist(primaryStorageUuid, PrimaryStorageVO.class)) { + logger.debug(String.format("[MetadataCleanupGC] primary storage[uuid:%s] no longer exists, " + + "cancel gc for vm[uuid:%s]", primaryStorageUuid, vmUuid)); + completion.cancel(); + return; + } + + if (hostUuid != null && !dbf.isExist(hostUuid, HostVO.class)) { + logger.debug(String.format("[MetadataCleanupGC] host[uuid:%s] no longer exists, " + + "cancel gc for vm[uuid:%s]", hostUuid, vmUuid)); + completion.cancel(); + return; + } + + CleanupVmInstanceMetadataOnPrimaryStorageMsg msg = new CleanupVmInstanceMetadataOnPrimaryStorageMsg(); + msg.setPrimaryStorageUuid(primaryStorageUuid); + msg.setVmUuid(vmUuid); + msg.setRootVolumeUuid(rootVolumeUuid); + msg.setMetadataPath(metadataPath); + msg.setHostUuid(hostUuid); + + bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, primaryStorageUuid); + bus.send(msg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (reply.isSuccess()) { + logger.info(String.format("[MetadataCleanupGC] successfully cleaned up metadata " + + "for vm[uuid:%s] on ps[uuid:%s]", vmUuid, primaryStorageUuid)); + completion.success(); + } else { + logger.warn(String.format("[MetadataCleanupGC] failed to clean up metadata " + + "for vm[uuid:%s] on ps[uuid:%s]: %s", vmUuid, primaryStorageUuid, reply.getError())); + completion.fail(reply.getError()); + } + } + }); + } +} diff --git a/compute/src/main/java/org/zstack/compute/vm/VmExpungeMetadataFlow.java b/compute/src/main/java/org/zstack/compute/vm/VmExpungeMetadataFlow.java new file mode 100644 index 00000000000..a327e1c3966 --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/VmExpungeMetadataFlow.java @@ -0,0 +1,128 @@ +package org.zstack.compute.vm; + +import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; +import org.zstack.core.cloudbus.CloudBus; +import org.zstack.core.cloudbus.CloudBusCallBack; +import org.zstack.core.componentloader.PluginRegistry; +import org.zstack.core.db.Q; +import org.zstack.header.core.workflow.FlowTrigger; +import org.zstack.header.core.workflow.NoRollbackFlow; +import org.zstack.header.message.MessageReply; +import org.zstack.header.storage.primary.CleanupVmInstanceMetadataOnPrimaryStorageMsg; +import org.zstack.header.storage.primary.PrimaryStorageConstant; +import org.zstack.header.storage.primary.PrimaryStorageVO; +import org.zstack.header.storage.primary.PrimaryStorageVO_; +import org.zstack.header.vm.VmInstanceConstant; +import org.zstack.header.vm.VmInstanceSpec; +import org.zstack.header.vm.metadata.VmMetadataPathBuildExtensionPoint; +import org.zstack.header.volume.VolumeInventory; +import org.zstack.utils.Utils; +import org.zstack.utils.logging.CLogger; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) +public class VmExpungeMetadataFlow extends NoRollbackFlow { + private static final CLogger logger = Utils.getLogger(VmExpungeMetadataFlow.class); + + @Autowired + private CloudBus bus; + @Autowired + private PluginRegistry pluginRgty; + + @Override + public void run(FlowTrigger trigger, Map data) { + final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); + if (spec == null || spec.getVmInventory() == null) { + logger.warn("[MetadataExpunge] missing VmInstanceSpec or VmInventory, skip metadata cleanup"); + trigger.next(); + return; + } + + final String vmUuid = spec.getVmInventory().getUuid(); + + VolumeInventory rootVolume = spec.getVmInventory().getRootVolume(); + String psUuid = rootVolume != null ? rootVolume.getPrimaryStorageUuid() : null; + if (psUuid == null) { + logger.debug(String.format("[MetadataExpunge] vm[uuid:%s] root volume has no primaryStorageUuid, " + + "skipping metadata cleanup", vmUuid)); + trigger.next(); + return; + } + + + String psType = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.type).eq(PrimaryStorageVO_.uuid, psUuid).findValue(); + if (psType == null) { + logger.warn(String.format("[MetadataExpunge] primary storage[uuid:%s] not found for vm[uuid:%s], " + + "skip metadata cleanup", psUuid, vmUuid)); + trigger.next(); + return; + } + + VmMetadataPathBuildExtensionPoint ext = pluginRgty.getExtensionFromMap(psType, VmMetadataPathBuildExtensionPoint.class); + if (ext == null) { + logger.warn(String.format("[MetadataExpunge] no VmMetadataPathBuildExtensionPoint found for ps[uuid:%s, type:%s], " + + "skip metadata cleanup", psUuid, psType)); + trigger.next(); + return; + } + final String metadataPath; + try { + metadataPath = ext.buildVmMetadataPath(psUuid, vmUuid); + } catch (Exception e) { + logger.warn(String.format("[MetadataExpunge] failed to build metadata path for vm[uuid:%s] on ps[uuid:%s], " + + "skip metadata cleanup: %s", vmUuid, psUuid, e.getMessage())); + trigger.next(); + return; + } + + String rootVolumeUuid = rootVolume.getUuid(); + CleanupVmInstanceMetadataOnPrimaryStorageMsg cmsg = new CleanupVmInstanceMetadataOnPrimaryStorageMsg(); + cmsg.setPrimaryStorageUuid(psUuid); + cmsg.setVmUuid(vmUuid); + cmsg.setMetadataPath(metadataPath); + cmsg.setRootVolumeUuid(rootVolumeUuid); + + String hostUuid = spec.getVmInventory().getHostUuid(); + if (hostUuid == null) { + hostUuid = spec.getVmInventory().getLastHostUuid(); + } + cmsg.setHostUuid(hostUuid); + + final String finalPsUuid = psUuid; + final String finalHostUuid = hostUuid; + + bus.makeTargetServiceIdByResourceUuid(cmsg, PrimaryStorageConstant.SERVICE_ID, psUuid); + bus.send(cmsg, new CloudBusCallBack(trigger) { + @Override + public void run(MessageReply reply) { + if (reply.isSuccess()) { + logger.info(String.format("[MetadataExpunge] successfully deleted metadata for vm[uuid:%s] on ps[uuid:%s]", + vmUuid, finalPsUuid)); + } else { + logger.warn(String.format("[MetadataExpunge] failed to delete metadata for vm[uuid:%s] on ps[uuid:%s]: %s, " + + "submitting GC job for retry", vmUuid, finalPsUuid, reply.getError())); + submitGC(finalPsUuid, vmUuid, rootVolumeUuid, metadataPath, finalHostUuid); + } + trigger.next(); + } + }); + } + + private void submitGC(String psUuid, String vmUuid, String rootVolumeUuid, String metadataPath, String hostUuid) { + CleanupVmInstanceMetadataOnPrimaryStorageGC gc = new CleanupVmInstanceMetadataOnPrimaryStorageGC(); + gc.NAME = CleanupVmInstanceMetadataOnPrimaryStorageGC.getGCName(vmUuid); + gc.primaryStorageUuid = psUuid; + gc.vmUuid = vmUuid; + gc.rootVolumeUuid = rootVolumeUuid; + gc.metadataPath = metadataPath; + gc.hostUuid = hostUuid; + long gcIntervalSec = TimeUnit.HOURS.toSeconds(VmGlobalConfig.VM_METADATA_CLEANUP_GC_INTERVAL.value(Long.class)); + gc.deduplicateSubmit(gcIntervalSec, TimeUnit.SECONDS); + + logger.info(String.format("[MetadataExpunge] submitted GC job [%s] for vm[uuid:%s] on ps[uuid:%s]", gc.NAME, vmUuid, psUuid)); + } +} 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..bfb9b7f5116 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,40 @@ public class VmGlobalConfig { @GlobalConfigValidation(validValues = {"None", "AuthenticAMD"}) @BindResourceConfig(value = {VmInstanceVO.class}) public static GlobalConfig VM_CPUID_VENDOR = new GlobalConfig(CATEGORY, "vm.cpuid.vendor"); + + @GlobalConfigValidation(validValues = {"true", "false"}) + public static GlobalConfig VM_METADATA_ENABLED = new GlobalConfig(CATEGORY, "vm.metadata.enabled"); + + @GlobalConfigValidation() + public static GlobalConfig VM_METADATA_LAST_REFRESH_VERSION = new GlobalConfig(CATEGORY, "vm.metadata.lastRefreshVersion"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 100) + public static GlobalConfig VM_METADATA_FLUSH_CONCURRENCY = new GlobalConfig(CATEGORY, "vm.metadata.flush.concurrency"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 300) + public static GlobalConfig VM_METADATA_FLUSH_POLL_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.flush.pollInterval"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 1000) + public static GlobalConfig VM_METADATA_FLUSH_BATCH_SIZE = new GlobalConfig(CATEGORY, "vm.metadata.flush.batchSize"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 168) + public static GlobalConfig VM_METADATA_CLEANUP_GC_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.cleanup.gc.interval"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 100) + public static GlobalConfig VM_METADATA_FLUSH_MAX_RETRY = new GlobalConfig(CATEGORY, "vm.metadata.flush.maxRetry"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 120) + public static GlobalConfig VM_METADATA_FLUSH_ZOMBIE_CLAIM_THRESHOLD = new GlobalConfig(CATEGORY, "vm.metadata.flush.zombieClaimThreshold"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 86400) + public static GlobalConfig VM_METADATA_MAINTENANCE_CONTENT_DRIFT_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.maintenance.contentDriftInterval"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 86400) + public static GlobalConfig VM_METADATA_MAINTENANCE_STALE_RECOVERY_INTERVAL = new GlobalConfig(CATEGORY, "vm.metadata.maintenance.staleRecoveryInterval"); + + @GlobalConfigValidation(numberGreaterThan = 0, numberLessThan = 1000) + public static GlobalConfig VM_METADATA_MAINTENANCE_STALE_RECOVERY_MAX_CYCLES = new GlobalConfig(CATEGORY, "vm.metadata.maintenance.staleRecoveryMaxCycles"); + + @GlobalConfigValidation(numberGreaterThan = 0) + public static GlobalConfig VM_METADATA_PAYLOAD_REJECT_THRESHOLD = new GlobalConfig(CATEGORY, "vm.metadata.payload.rejectThreshold"); } diff --git a/compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java b/compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java index c17cf5d5179..154e8bea23e 100755 --- a/compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java @@ -23,14 +23,19 @@ import org.zstack.header.network.l3.*; import org.zstack.header.storage.primary.PrimaryStorageClusterRefVO; import org.zstack.header.storage.primary.PrimaryStorageClusterRefVO_; +import org.zstack.header.storage.primary.PrimaryStorageVO; +import org.zstack.header.storage.primary.PrimaryStorageVO_; import org.zstack.header.storage.snapshot.VolumeSnapshotVO; import org.zstack.header.storage.snapshot.VolumeSnapshotVO_; import org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO; import org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO_; import org.zstack.header.vm.*; import org.zstack.header.vm.cdrom.*; +import org.zstack.header.vm.metadata.APIRegisterVmInstanceFromMetadataMsg; import org.zstack.header.vm.devices.VmInstanceResourceMetadataGroupVO; import org.zstack.header.vm.devices.VmInstanceResourceMetadataGroupVO_; +import org.zstack.header.vm.metadata.VmInstanceMetadataConstants; +import org.zstack.header.vm.metadata.VmMetadataPathBuildExtensionPoint; import org.zstack.header.volume.*; import org.zstack.network.l2.L2NetworkHostUtils; import org.zstack.resourceconfig.ResourceConfigFacade; @@ -166,6 +171,8 @@ else if (msg instanceof APIAttachVmNicToVmMsg) { validate((APIConvertTemplatedVmInstanceToVmInstanceMsg) msg); } else if (msg instanceof APIDeleteTemplatedVmInstanceMsg) { validate((APIDeleteTemplatedVmInstanceMsg) msg); + } else if (msg instanceof APIRegisterVmInstanceFromMetadataMsg) { + validate((APIRegisterVmInstanceFromMetadataMsg) msg); } if (msg instanceof NewVmInstanceMessage2) { @@ -1318,4 +1325,29 @@ private void validate(APIFstrimVmMsg msg) { } msg.setHostUuid(t.get(1, String.class)); } + + private void validate(APIRegisterVmInstanceFromMetadataMsg msg) { + String path = msg.getMetadataPath(); + if (StringUtils.isEmpty(path)) { + throw new ApiMessageInterceptionException(argerr("metadataPath cannot be empty or null")); + } + + // Delegate path validation to the storage-type-specific extension + String psUuid = msg.getPrimaryStorageUuid(); + String psType = Q.New(PrimaryStorageVO.class) + .select(PrimaryStorageVO_.type) + .eq(PrimaryStorageVO_.uuid, psUuid) + .findValue(); + VmMetadataPathBuildExtensionPoint ext = (psType != null) + ? pluginRgty.getExtensionFromMap(psType, VmMetadataPathBuildExtensionPoint.class) : null; + if (ext == null) { + throw new ApiMessageInterceptionException(argerr( + "primary storage[uuid:%s, type:%s] does not support vm metadata", psUuid, psType)); + } + + String error = ext.validateMetadataPath(psUuid, path); + if (error != null) { + throw new ApiMessageInterceptionException(argerr(error)); + } + } } diff --git a/compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java b/compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java index 52aa2282c87..89b56e921b7 100755 --- a/compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java @@ -307,4 +307,8 @@ public String desensitizeTag(SystemTag systemTag, String tag) { } public static PatternedSystemTag VM_STATE_PAUSED_AFTER_MIGRATE = new PatternedSystemTag(("vmPausedAfterMigrate"), VmInstanceVO.class); + + public static String VM_METADATA_REGISTERING_MN_UUID_TOKEN = "registeringMnUuid"; + public static PatternedSystemTag VM_METADATA_REGISTERING_MN_UUID = new PatternedSystemTag( + String.format("vmMetadata::registeringMnUuid::{%s}", VM_METADATA_REGISTERING_MN_UUID_TOKEN), VmInstanceVO.class); } diff --git a/conf/db/upgrade/V5.0.0__schema.sql b/conf/db/upgrade/V5.0.0__schema.sql new file mode 100644 index 00000000000..4b437190e72 --- /dev/null +++ b/conf/db/upgrade/V5.0.0__schema.sql @@ -0,0 +1,26 @@ +CREATE TABLE IF NOT EXISTS `zstack`.`VmMetadataDirtyVO` ( + `vmInstanceUuid` VARCHAR(32) NOT NULL, + `managementNodeUuid` VARCHAR(32) DEFAULT NULL, + `dirtyVersion` BIGINT NOT NULL DEFAULT 1, + `lastClaimTime` TIMESTAMP NULL DEFAULT NULL, + `storageStructureChange` TINYINT(1) NOT NULL DEFAULT 0, + `retryCount` INT NOT NULL DEFAULT 0, + `nextRetryTime` TIMESTAMP NULL DEFAULT NULL, + `lastOpDate` timestamp on update CURRENT_TIMESTAMP, + `createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59', + PRIMARY KEY (`vmInstanceUuid`), + CONSTRAINT `fkVmMetadataDirtyVOVmInstanceEO` FOREIGN KEY (`vmInstanceUuid`) REFERENCES `VmInstanceEO` (`uuid`) ON DELETE CASCADE, + CONSTRAINT `fkVmMetadataDirtyVOManagementNodeVO` FOREIGN KEY (`managementNodeUuid`) REFERENCES `ManagementNodeVO` (`uuid`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `zstack`.`VmMetadataFingerprintVO` ( + `vmInstanceUuid` VARCHAR(32) NOT NULL, + `metadataSnapshot` LONGTEXT, + `lastFlushTime` TIMESTAMP NULL DEFAULT NULL, + `lastFlushFailed` TINYINT(1) NOT NULL DEFAULT 0, + `staleRecoveryCount` INT NOT NULL DEFAULT 0, + `lastOpDate` timestamp on update CURRENT_TIMESTAMP, + `createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59', + PRIMARY KEY (`vmInstanceUuid`), + CONSTRAINT `fkVmMetadataFingerprintVOVmInstanceEO` FOREIGN KEY (`vmInstanceUuid`) REFERENCES `VmInstanceEO` (`uuid`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/conf/globalConfig/vm.xml b/conf/globalConfig/vm.xml index 8563169b335..1e8cd59f237 100755 --- a/conf/globalConfig/vm.xml +++ b/conf/globalConfig/vm.xml @@ -317,4 +317,101 @@ java.lang.Boolean false + + + vm + vm.metadata.enabled + enable vm metadata + java.lang.Boolean + false + + + + vm + vm.metadata.lastRefreshVersion + Last completed upgrade refresh version, prevents duplicate triggers across MNs. Internal use only + java.lang.String + 5.0.0 + + + + vm + vm.metadata.flush.concurrency + Max concurrent metadata flush tasks per management node + java.lang.Integer + 10 + + + + vm + vm.metadata.flush.pollInterval + Metadata dirty poller interval in seconds + java.lang.Long + 5 + + + + vm + vm.metadata.flush.batchSize + Max dirty rows claimed per poll cycle + java.lang.Integer + 20 + + + + vm + vm.metadata.cleanup.gc.interval + Metadata cleanup GC retry interval in hours + java.lang.Long + 8 + + + + vm + vm.metadata.flush.maxRetry + Max flush retry count before marking VM metadata as stale + java.lang.Integer + 5 + + + + vm + vm.metadata.flush.zombieClaimThreshold + Minutes before an uncompleted flush claim is considered zombie and released + java.lang.Long + 15 + + + + vm + vm.metadata.maintenance.contentDriftInterval + Content drift detection interval in seconds. Drift detection is a fallback safety net; the primary mechanism is dirty marking via interceptors. Default 86400 (24h) to reduce DB pressure in large-scale environments. + java.lang.Long + 86400 + + + + vm + vm.metadata.maintenance.staleRecoveryInterval + Stale VM metadata recovery interval in seconds + java.lang.Long + 1800 + + + + vm + vm.metadata.maintenance.staleRecoveryMaxCycles + Max stale recovery cycles before entering permanent-stale state + java.lang.Integer + 10 + + + + vm + vm.metadata.payload.rejectThreshold + Max allowed VM metadata payload size in bytes. VMs exceeding this are skipped. + java.lang.Long + 33554432 + + diff --git a/conf/persistence.xml b/conf/persistence.xml index aa295bcb365..27d8646e46b 100755 --- a/conf/persistence.xml +++ b/conf/persistence.xml @@ -211,5 +211,7 @@ org.zstack.header.resourceattribute.entity.ResourceAttributeKeyResourceTypeVO org.zstack.header.resourceattribute.entity.ResourceAttributeConstraintVO org.zstack.softwarePackage.header.SoftwarePackageVO + org.zstack.header.vm.metadata.VmMetadataDirtyVO + org.zstack.header.vm.metadata.VmMetadataFingerprintVO diff --git a/conf/serviceConfig/primaryStorage.xml b/conf/serviceConfig/primaryStorage.xml index 337ce4eaac3..88597aa6ebb 100755 --- a/conf/serviceConfig/primaryStorage.xml +++ b/conf/serviceConfig/primaryStorage.xml @@ -84,4 +84,7 @@ org.zstack.header.storage.primary.APIAddStorageProtocolMsg - + + org.zstack.header.storage.primary.APIScanVmInstanceMetadataFromPrimaryStorageMsg + + \ No newline at end of file diff --git a/conf/serviceConfig/vmInstance.xml b/conf/serviceConfig/vmInstance.xml index ab73fb6bea9..afe05d3184a 100755 --- a/conf/serviceConfig/vmInstance.xml +++ b/conf/serviceConfig/vmInstance.xml @@ -264,4 +264,16 @@ org.zstack.header.vm.APIDeleteTemplatedVmInstanceMsg + + org.zstack.header.vm.metadata.APICleanupVmInstanceMetadataMsg + + + org.zstack.header.vm.metadata.APIRegisterVmInstanceFromMetadataMsg + + + org.zstack.header.vm.metadata.APIUpdateVmInstanceMetadataMsg + + + org.zstack.header.vm.metadata.APIGetVmInstanceMetadataFromPrimaryStorageMsg + diff --git a/conf/springConfigXml/VmInstanceManager.xml b/conf/springConfigXml/VmInstanceManager.xml index 20e094378aa..fb60868aaeb 100755 --- a/conf/springConfigXml/VmInstanceManager.xml +++ b/conf/springConfigXml/VmInstanceManager.xml @@ -118,6 +118,7 @@ org.zstack.compute.vm.VmExpungeRootVolumeFlow org.zstack.compute.vm.VmExpungeMemoryVolumeFlow org.zstack.compute.vm.VmExpungeCacheVolumeFlow + org.zstack.compute.vm.VmExpungeMetadataFlow diff --git a/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageEvent.java b/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageEvent.java new file mode 100644 index 00000000000..f3c5adad2fc --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageEvent.java @@ -0,0 +1,33 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.APIEvent; +import org.zstack.header.rest.RestResponse; + +import java.util.ArrayList; +import java.util.List; + +@RestResponse(fieldsTo = {"all"}) +public class APIScanVmInstanceMetadataFromPrimaryStorageEvent extends APIEvent { + private List vmInstanceMetadata = new ArrayList<>(); + + public APIScanVmInstanceMetadataFromPrimaryStorageEvent() { + super(null); + } + + public APIScanVmInstanceMetadataFromPrimaryStorageEvent(String apiId) { + super(apiId); + } + + public List getVmInstanceMetadata() { + return vmInstanceMetadata; + } + + public void setVmInstanceMetadata(List vmInstanceMetadata) { + this.vmInstanceMetadata = vmInstanceMetadata == null ? new ArrayList<>() : vmInstanceMetadata; + } + + public static APIScanVmInstanceMetadataFromPrimaryStorageEvent __example__() { + APIScanVmInstanceMetadataFromPrimaryStorageEvent evt = new APIScanVmInstanceMetadataFromPrimaryStorageEvent(); + return evt; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageEventDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageEventDoc_zh_cn.groovy new file mode 100644 index 00000000000..692e48f08a0 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageEventDoc_zh_cn.groovy @@ -0,0 +1,32 @@ +package org.zstack.header.storage.primary + + +import org.zstack.header.errorcode.ErrorCode + +doc { + + title "扫描主存储上的云主机元数据返回" + + ref { + name "vmInstanceMetadata" + path "org.zstack.header.storage.primary.APIScanVmInstanceMetadataFromPrimaryStorageEvent.vmInstanceMetadata" + desc "云主机元数据摘要列表" + type "List" + since "5.0.0" + clz VmMetadataScanEntry.class + } + field { + name "success" + desc "操作是否成功" + type "boolean" + since "5.0.0" + } + ref { + name "error" + path "org.zstack.header.storage.primary.APIScanVmInstanceMetadataFromPrimaryStorageEvent.error" + desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null" + type "ErrorCode" + since "5.0.0" + clz ErrorCode.class + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageMsg.java new file mode 100644 index 00000000000..411a568d5a0 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageMsg.java @@ -0,0 +1,35 @@ +package org.zstack.header.storage.primary; + +import org.springframework.http.HttpMethod; +import org.zstack.header.message.APIMessage; +import org.zstack.header.message.APIParam; +import org.zstack.header.rest.RestRequest; + +@RestRequest( + path = "/primary-storage/vm-instances/metadata/scan", + method = HttpMethod.GET, + responseClass = APIScanVmInstanceMetadataFromPrimaryStorageEvent.class +) +public class APIScanVmInstanceMetadataFromPrimaryStorageMsg extends APIMessage implements PrimaryStorageMessage { + @APIParam(resourceType = PrimaryStorageVO.class) + private String uuid; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getPrimaryStorageUuid() { + return uuid; + } + + public static APIScanVmInstanceMetadataFromPrimaryStorageMsg __example__() { + APIScanVmInstanceMetadataFromPrimaryStorageMsg msg = new APIScanVmInstanceMetadataFromPrimaryStorageMsg(); + msg.setUuid(uuid()); + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy new file mode 100644 index 00000000000..090f2a36075 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy @@ -0,0 +1,58 @@ +package org.zstack.header.storage.primary + +import org.zstack.header.storage.primary.APIScanVmInstanceMetadataFromPrimaryStorageEvent + +doc { + title "扫描主存储上的云主机元数据" + + category "主存储" + + desc """扫描指定主存储上所有云主机元数据文件,返回元数据摘要列表""" + + rest { + request { + url "GET /v1/primary-storage/vm-instances/metadata/scan" + + header (Authorization: 'OAuth the-session-uuid') + + clz APIScanVmInstanceMetadataFromPrimaryStorageMsg.class + + desc """""" + + params { + + column { + name "uuid" + enclosedIn "" + desc "主存储UUID" + location "query" + type "String" + optional false + since "5.0.0" + } + column { + name "systemTags" + enclosedIn "" + desc "系统标签" + location "query" + type "List" + optional true + since "5.0.0" + } + column { + name "userTags" + enclosedIn "" + desc "用户标签" + location "query" + type "List" + optional true + since "5.0.0" + } + } + } + + response { + clz APIScanVmInstanceMetadataFromPrimaryStorageEvent.class + } + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/storage/primary/CleanupVmInstanceMetadataOnPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/CleanupVmInstanceMetadataOnPrimaryStorageMsg.java new file mode 100644 index 00000000000..7a23e2cdcd4 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/CleanupVmInstanceMetadataOnPrimaryStorageMsg.java @@ -0,0 +1,52 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.NeedReplyMessage; + +public class CleanupVmInstanceMetadataOnPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage { + private String primaryStorageUuid; + private String vmUuid; + private String rootVolumeUuid; + private String metadataPath; + private String hostUuid; + + @Override + public String getPrimaryStorageUuid() { + return primaryStorageUuid; + } + + public void setPrimaryStorageUuid(String primaryStorageUuid) { + this.primaryStorageUuid = primaryStorageUuid; + } + + public String getVmUuid() { + return vmUuid; + } + + public void setVmUuid(String vmUuid) { + this.vmUuid = vmUuid; + } + + public String getRootVolumeUuid() { + return rootVolumeUuid; + } + + public void setRootVolumeUuid(String rootVolumeUuid) { + this.rootVolumeUuid = rootVolumeUuid; + } + + public String getMetadataPath() { + return metadataPath; + } + + public void setMetadataPath(String metadataPath) { + this.metadataPath = metadataPath; + } + + public String getHostUuid() { + return hostUuid; + } + + public void setHostUuid(String hostUuid) { + this.hostUuid = hostUuid; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/CleanupVmInstanceMetadataOnPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/storage/primary/CleanupVmInstanceMetadataOnPrimaryStorageReply.java new file mode 100644 index 00000000000..05bba3ac430 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/CleanupVmInstanceMetadataOnPrimaryStorageReply.java @@ -0,0 +1,6 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.MessageReply; + +public class CleanupVmInstanceMetadataOnPrimaryStorageReply extends MessageReply { +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/GetVmInstanceMetadataFromPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/GetVmInstanceMetadataFromPrimaryStorageMsg.java new file mode 100644 index 00000000000..67ea49e5e18 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/GetVmInstanceMetadataFromPrimaryStorageMsg.java @@ -0,0 +1,44 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.NeedReplyMessage; + + +public class GetVmInstanceMetadataFromPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage { + private String primaryStorageUuid; + private String metadataPath; + private String rootVolumeUuid; + private String hostUuid; + + @Override + public String getPrimaryStorageUuid() { + return primaryStorageUuid; + } + + public void setPrimaryStorageUuid(String primaryStorageUuid) { + this.primaryStorageUuid = primaryStorageUuid; + } + + public String getMetadataPath() { + return metadataPath; + } + + public void setMetadataPath(String metadataPath) { + this.metadataPath = metadataPath; + } + + public String getRootVolumeUuid() { + return rootVolumeUuid; + } + + public void setRootVolumeUuid(String rootVolumeUuid) { + this.rootVolumeUuid = rootVolumeUuid; + } + + public String getHostUuid() { + return hostUuid; + } + + public void setHostUuid(String hostUuid) { + this.hostUuid = hostUuid; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/GetVmInstanceMetadataFromPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/storage/primary/GetVmInstanceMetadataFromPrimaryStorageReply.java new file mode 100644 index 00000000000..c164a99792d --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/GetVmInstanceMetadataFromPrimaryStorageReply.java @@ -0,0 +1,15 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.MessageReply; + +public class GetVmInstanceMetadataFromPrimaryStorageReply extends MessageReply { + private String metadata; + + public String getMetadata() { + return metadata; + } + + public void setMetadata(String metadata) { + this.metadata = metadata; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/ReadVmInstanceMetadataMsg.java b/header/src/main/java/org/zstack/header/storage/primary/ReadVmInstanceMetadataMsg.java new file mode 100644 index 00000000000..ed09f15eb4d --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/ReadVmInstanceMetadataMsg.java @@ -0,0 +1,21 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.NeedReplyMessage; +import org.zstack.header.vm.VmInstanceMessage; + +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/storage/primary/ReadVmInstanceMetadataReply.java b/header/src/main/java/org/zstack/header/storage/primary/ReadVmInstanceMetadataReply.java new file mode 100644 index 00000000000..04462f849ad --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/ReadVmInstanceMetadataReply.java @@ -0,0 +1,15 @@ +package org.zstack.header.storage.primary; + +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/storage/primary/RebaseVolumeBackingFileOnPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/RebaseVolumeBackingFileOnPrimaryStorageMsg.java new file mode 100644 index 00000000000..f9dd68d2934 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/RebaseVolumeBackingFileOnPrimaryStorageMsg.java @@ -0,0 +1,70 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.NeedReplyMessage; + +import java.util.List; + +/** + * 请求目标主存储对指定文件做 backing file 前缀替换(prefix rebase)。 + *

+ * 各存储插件(LocalStorage / SharedBlock / NFS)自行选择 host、构造 agent command 并发送。 + */ +public class RebaseVolumeBackingFileOnPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage { + private String primaryStorageUuid; + + /** + * volume + snapshot 的 installPath 列表(已做路径替换的逻辑路径)。 + * LocalStorage / NFS 下即绝对路径;SharedBlock 下为 sharedblock:// scheme 路径,由插件内部转绝对路径。 + */ + private List installPaths; + + /** 旧路径前缀 */ + private String oldPrefix; + + /** 新路径前缀 */ + private String newPrefix; + + /** 注册请求指定的 hostUuid(LocalStorage 需要,其他存储可忽略) */ + private String hostUuid; + + @Override + public String getPrimaryStorageUuid() { + return primaryStorageUuid; + } + + public void setPrimaryStorageUuid(String primaryStorageUuid) { + this.primaryStorageUuid = primaryStorageUuid; + } + + public List getInstallPaths() { + return installPaths; + } + + public void setInstallPaths(List installPaths) { + this.installPaths = installPaths; + } + + public String getOldPrefix() { + return oldPrefix; + } + + public void setOldPrefix(String oldPrefix) { + this.oldPrefix = oldPrefix; + } + + public String getNewPrefix() { + return newPrefix; + } + + public void setNewPrefix(String newPrefix) { + this.newPrefix = newPrefix; + } + + public String getHostUuid() { + return hostUuid; + } + + public void setHostUuid(String hostUuid) { + this.hostUuid = hostUuid; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/RebaseVolumeBackingFileOnPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/storage/primary/RebaseVolumeBackingFileOnPrimaryStorageReply.java new file mode 100644 index 00000000000..44043421f09 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/RebaseVolumeBackingFileOnPrimaryStorageReply.java @@ -0,0 +1,15 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.MessageReply; + +public class RebaseVolumeBackingFileOnPrimaryStorageReply extends MessageReply { + private int rebasedCount; + + public int getRebasedCount() { + return rebasedCount; + } + + public void setRebasedCount(int rebasedCount) { + this.rebasedCount = rebasedCount; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/ScanVmInstanceMetadataFromPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/ScanVmInstanceMetadataFromPrimaryStorageMsg.java new file mode 100644 index 00000000000..052e77f7507 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/ScanVmInstanceMetadataFromPrimaryStorageMsg.java @@ -0,0 +1,25 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.NeedReplyMessage; + +public class ScanVmInstanceMetadataFromPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage { + private String primaryStorageUuid; + private String metadataDir; + + @Override + public String getPrimaryStorageUuid() { + return primaryStorageUuid; + } + + public void setPrimaryStorageUuid(String primaryStorageUuid) { + this.primaryStorageUuid = primaryStorageUuid; + } + + public String getMetadataDir() { + return metadataDir; + } + + public void setMetadataDir(String metadataDir) { + this.metadataDir = metadataDir; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/ScanVmInstanceMetadataFromPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/storage/primary/ScanVmInstanceMetadataFromPrimaryStorageReply.java new file mode 100644 index 00000000000..a78db671a30 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/ScanVmInstanceMetadataFromPrimaryStorageReply.java @@ -0,0 +1,18 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.MessageReply; + +import java.util.ArrayList; +import java.util.List; + +public class ScanVmInstanceMetadataFromPrimaryStorageReply extends MessageReply { + private List vmInstanceMetadata = new ArrayList<>(); + + public List getVmInstanceMetadata() { + return vmInstanceMetadata; + } + + public void setVmInstanceMetadata(List vmInstanceMetadata) { + this.vmInstanceMetadata = vmInstanceMetadata == null ? new ArrayList<>() : vmInstanceMetadata; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntry.java b/header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntry.java new file mode 100644 index 00000000000..9859bc2aca7 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntry.java @@ -0,0 +1,94 @@ +package org.zstack.header.storage.primary; + +public class VmMetadataScanEntry { + private String vmUuid; + private String vmName; + private String vmCategory; + private String architecture; + private String schemaVersion; + private String metadataPath; + private String hostUuid; + private long sizeBytes; + private long lastUpdateTime; + private boolean incomplete; + + public String getVmUuid() { + return vmUuid; + } + + public void setVmUuid(String vmUuid) { + this.vmUuid = vmUuid; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public String getVmCategory() { + return vmCategory; + } + + public void setVmCategory(String vmCategory) { + this.vmCategory = vmCategory; + } + + public String getArchitecture() { + return architecture; + } + + public void setArchitecture(String architecture) { + this.architecture = architecture; + } + + public String getSchemaVersion() { + return schemaVersion; + } + + public void setSchemaVersion(String schemaVersion) { + this.schemaVersion = schemaVersion; + } + + public String getMetadataPath() { + return metadataPath; + } + + public void setMetadataPath(String metadataPath) { + this.metadataPath = metadataPath; + } + + public String getHostUuid() { + return hostUuid; + } + + public void setHostUuid(String hostUuid) { + this.hostUuid = hostUuid; + } + + public long getSizeBytes() { + return sizeBytes; + } + + public void setSizeBytes(long sizeBytes) { + this.sizeBytes = sizeBytes; + } + + public long getLastUpdateTime() { + return lastUpdateTime; + } + + public void setLastUpdateTime(long lastUpdateTime) { + this.lastUpdateTime = lastUpdateTime; + } + + public boolean isIncomplete() { + return incomplete; + } + + public void setIncomplete(boolean incomplete) { + this.incomplete = incomplete; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntryDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntryDoc_zh_cn.groovy new file mode 100644 index 00000000000..101eddf8515 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntryDoc_zh_cn.groovy @@ -0,0 +1,4 @@ +package org.zstack.header.storage.primary + +class VmMetadataScanEntryDoc_zh_cn { +} diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/APIDeleteVolumeSnapshotMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/APIDeleteVolumeSnapshotMsg.java index 966a7d1030c..4711fa5de74 100755 --- a/header/src/main/java/org/zstack/header/storage/snapshot/APIDeleteVolumeSnapshotMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/APIDeleteVolumeSnapshotMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.*; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.List; import java.util.concurrent.TimeUnit; @@ -44,6 +45,7 @@ responseClass = APIDeleteVolumeSnapshotEvent.class ) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 6) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "SnapshotUuidToVmUuidResolver", field = "uuid", updateOnFailure = true) public class APIDeleteVolumeSnapshotMsg extends APIDeleteMessage implements DeleteVolumeSnapshotMessage { /** * @desc volume snapshot uuid diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/APIRevertVolumeFromSnapshotMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/APIRevertVolumeFromSnapshotMsg.java index 744f13038b6..6ae3008e03e 100755 --- a/header/src/main/java/org/zstack/header/storage/snapshot/APIRevertVolumeFromSnapshotMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/APIRevertVolumeFromSnapshotMsg.java @@ -8,6 +8,7 @@ import org.zstack.header.other.APIAuditor; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.header.volume.VolumeVO; import java.util.concurrent.TimeUnit; @@ -46,6 +47,7 @@ responseClass = APIRevertVolumeFromSnapshotEvent.class ) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 24) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "SnapshotUuidToVmUuidResolver", field = "uuid", updateOnFailure = true) public class APIRevertVolumeFromSnapshotMsg extends APIMessage implements RevertVolumeSnapshotMessage, APIAuditor { /** * @desc volume snapshot uuid diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/APIShrinkVolumeSnapshotMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/APIShrinkVolumeSnapshotMsg.java index d5f00be9237..f8a1a670023 100644 --- a/header/src/main/java/org/zstack/header/storage/snapshot/APIShrinkVolumeSnapshotMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/APIShrinkVolumeSnapshotMsg.java @@ -6,6 +6,7 @@ import org.zstack.header.message.DefaultTimeout; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.concurrent.TimeUnit; @@ -20,6 +21,7 @@ isAction = true ) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 24) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "SnapshotUuidToVmUuidResolver", field = "uuid") public class APIShrinkVolumeSnapshotMsg extends APIMessage implements VolumeSnapshotMessage { @APIParam(resourceType = VolumeSnapshotVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/APIUpdateVolumeSnapshotMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/APIUpdateVolumeSnapshotMsg.java index 870a9ceacd4..6daf7b61649 100755 --- a/header/src/main/java/org/zstack/header/storage/snapshot/APIUpdateVolumeSnapshotMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/APIUpdateVolumeSnapshotMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 6/14/2015. @@ -15,6 +16,7 @@ isAction = true, responseClass = APIUpdateVolumeSnapshotEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "SnapshotUuidToVmUuidResolver", field = "uuid") public class APIUpdateVolumeSnapshotMsg extends APIMessage implements VolumeSnapshotMessage { @APIParam(resourceType = VolumeSnapshotVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/group/APIDeleteVolumeSnapshotGroupMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/group/APIDeleteVolumeSnapshotGroupMsg.java index 4afc5170734..cb1f8dde454 100644 --- a/header/src/main/java/org/zstack/header/storage/snapshot/group/APIDeleteVolumeSnapshotGroupMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/group/APIDeleteVolumeSnapshotGroupMsg.java @@ -7,6 +7,7 @@ import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; import org.zstack.header.storage.snapshot.SnapshotBackendOperation; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.concurrent.TimeUnit; @@ -19,6 +20,7 @@ responseClass = APIDeleteVolumeSnapshotGroupEvent.class ) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 3) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "SnapshotGroupUuidToVmUuidResolver", field = "uuid", updateOnFailure = true) public class APIDeleteVolumeSnapshotGroupMsg extends APIDeleteMessage implements VolumeSnapshotGroupMessage { @APIParam(resourceType = VolumeSnapshotGroupVO.class, successIfResourceNotExisting = true) private String uuid; diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/group/APIRevertVmFromSnapshotGroupMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/group/APIRevertVmFromSnapshotGroupMsg.java index d042ee6229f..28752daf8b0 100644 --- a/header/src/main/java/org/zstack/header/storage/snapshot/group/APIRevertVmFromSnapshotGroupMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/group/APIRevertVmFromSnapshotGroupMsg.java @@ -14,6 +14,7 @@ import org.zstack.header.rest.RestRequest; import org.zstack.header.storage.snapshot.SnapshotBackendOperation; import org.zstack.header.vm.VmInstanceVO; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.concurrent.TimeUnit; @@ -25,6 +26,7 @@ ) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 24) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "SnapshotGroupUuidToVmUuidResolver", field = "uuid", updateOnFailure = true) public class APIRevertVmFromSnapshotGroupMsg extends APIMessage implements VolumeSnapshotGroupMessage, APIAuditor { @APIParam(resourceType = VolumeSnapshotGroupVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/group/APIUngroupVolumeSnapshotGroupMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/group/APIUngroupVolumeSnapshotGroupMsg.java index daa39380ac1..6428bde5269 100644 --- a/header/src/main/java/org/zstack/header/storage/snapshot/group/APIUngroupVolumeSnapshotGroupMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/group/APIUngroupVolumeSnapshotGroupMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; import org.zstack.header.storage.snapshot.SnapshotBackendOperation; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by MaJin on 2019/7/9. @@ -14,6 +15,7 @@ method = HttpMethod.DELETE, responseClass = APIUngroupVolumeSnapshotGroupEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "SnapshotGroupUuidToVmUuidResolver", field = "uuid") public class APIUngroupVolumeSnapshotGroupMsg extends APIMessage implements VolumeSnapshotGroupMessage { @APIParam(resourceType = VolumeSnapshotGroupVO.class, successIfResourceNotExisting = true) private String uuid; diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/group/APIUpdateVolumeSnapshotGroupMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/group/APIUpdateVolumeSnapshotGroupMsg.java index 0cee28cd6ea..68deeb2b635 100644 --- a/header/src/main/java/org/zstack/header/storage/snapshot/group/APIUpdateVolumeSnapshotGroupMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/group/APIUpdateVolumeSnapshotGroupMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.header.storage.snapshot.SnapshotBackendOperation; /** @@ -15,6 +16,7 @@ isAction = true, responseClass = APIUpdateVolumeSnapshotGroupEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "SnapshotGroupUuidToVmUuidResolver", field = "uuid") public class APIUpdateVolumeSnapshotGroupMsg extends APIMessage implements VolumeSnapshotGroupMessage { @APIParam(required = false) private String name; diff --git a/header/src/main/java/org/zstack/header/tag/APICreateSystemTagMsg.java b/header/src/main/java/org/zstack/header/tag/APICreateSystemTagMsg.java index a02a6001cf1..aaa350d471a 100755 --- a/header/src/main/java/org/zstack/header/tag/APICreateSystemTagMsg.java +++ b/header/src/main/java/org/zstack/header/tag/APICreateSystemTagMsg.java @@ -2,6 +2,7 @@ import org.springframework.http.HttpMethod; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** */ @@ -11,6 +12,7 @@ responseClass = APICreateSystemTagEvent.class, parameterName = "params" ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "ResourceUuidToVmUuidResolver", field = "resourceUuid") public class APICreateSystemTagMsg extends APIAbstractCreateTagMsg { public static APICreateSystemTagMsg __example__() { diff --git a/header/src/main/java/org/zstack/header/tag/APICreateSystemTagsMsg.java b/header/src/main/java/org/zstack/header/tag/APICreateSystemTagsMsg.java index 170076094d9..c2fa8085b91 100644 --- a/header/src/main/java/org/zstack/header/tag/APICreateSystemTagsMsg.java +++ b/header/src/main/java/org/zstack/header/tag/APICreateSystemTagsMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.header.vo.ResourceVO; import java.util.List; @@ -16,6 +17,7 @@ responseClass = APICreateSystemTagsEvent.class, parameterName = "params" ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "ResourceUuidToVmUuidResolver", field = "resourceUuid") public class APICreateSystemTagsMsg extends APIMessage { @APIParam private String resourceType; diff --git a/header/src/main/java/org/zstack/header/tag/APIDeleteTagMsg.java b/header/src/main/java/org/zstack/header/tag/APIDeleteTagMsg.java index b1159b20945..59597ac6901 100755 --- a/header/src/main/java/org/zstack/header/tag/APIDeleteTagMsg.java +++ b/header/src/main/java/org/zstack/header/tag/APIDeleteTagMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIDeleteMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** */ @@ -12,6 +13,7 @@ method = HttpMethod.DELETE, responseClass = APIDeleteTagEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "SystemTagUuidToVmUuidResolver", field = "uuid") public class APIDeleteTagMsg extends APIDeleteMessage { @APIParam private String uuid; diff --git a/header/src/main/java/org/zstack/header/tag/APIUpdateSystemTagMsg.java b/header/src/main/java/org/zstack/header/tag/APIUpdateSystemTagMsg.java index 2962ce07061..65173855747 100755 --- a/header/src/main/java/org/zstack/header/tag/APIUpdateSystemTagMsg.java +++ b/header/src/main/java/org/zstack/header/tag/APIUpdateSystemTagMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 8/17/2015. @@ -14,6 +15,7 @@ isAction = true, method = HttpMethod.PUT ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "SystemTagUuidToVmUuidResolver", field = "uuid") public class APIUpdateSystemTagMsg extends APIMessage { @APIParam(resourceType = SystemTagVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIAttachIsoToVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIAttachIsoToVmInstanceMsg.java index c28f55a3c6c..e630c877e5c 100755 --- a/header/src/main/java/org/zstack/header/vm/APIAttachIsoToVmInstanceMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIAttachIsoToVmInstanceMsg.java @@ -6,6 +6,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 10/17/2015. @@ -16,6 +17,7 @@ responseClass = APIAttachIsoToVmInstanceEvent.class, parameterName = "null" ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIAttachIsoToVmInstanceMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIAttachL3NetworkToVmMsg.java b/header/src/main/java/org/zstack/header/vm/APIAttachL3NetworkToVmMsg.java index 7bab9be06f9..681a04c6b51 100755 --- a/header/src/main/java/org/zstack/header/vm/APIAttachL3NetworkToVmMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIAttachL3NetworkToVmMsg.java @@ -6,6 +6,7 @@ import org.zstack.header.network.l3.L3NetworkVO; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.utils.network.NicIpAddressInfo; import java.util.List; @@ -45,6 +46,7 @@ method = HttpMethod.POST, responseClass = APIAttachL3NetworkToVmEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIAttachL3NetworkToVmMsg extends APIMessage implements VmInstanceMessage { /** * @desc vm uuid diff --git a/header/src/main/java/org/zstack/header/vm/APIAttachVmNicToVmMsg.java b/header/src/main/java/org/zstack/header/vm/APIAttachVmNicToVmMsg.java index 05b86a2bedf..5313e83aa0e 100644 --- a/header/src/main/java/org/zstack/header/vm/APIAttachVmNicToVmMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIAttachVmNicToVmMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{vmInstanceUuid}/nices/{vmNicUuid}", @@ -11,6 +12,7 @@ responseClass = APIAttachVmNicToVmEvent.class, parameterName = "params" ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIAttachVmNicToVmMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmNicVO.class) diff --git a/header/src/main/java/org/zstack/header/vm/APIChangeInstanceOfferingMsg.java b/header/src/main/java/org/zstack/header/vm/APIChangeInstanceOfferingMsg.java index aa0e68f9211..d157868793f 100755 --- a/header/src/main/java/org/zstack/header/vm/APIChangeInstanceOfferingMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIChangeInstanceOfferingMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 7/16/2015. @@ -15,6 +16,7 @@ method = HttpMethod.PUT, responseClass = APIChangeInstanceOfferingEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIChangeInstanceOfferingMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIChangeVmNicNetworkMsg.java b/header/src/main/java/org/zstack/header/vm/APIChangeVmNicNetworkMsg.java index 60ded8149d2..bfedeb43306 100644 --- a/header/src/main/java/org/zstack/header/vm/APIChangeVmNicNetworkMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIChangeVmNicNetworkMsg.java @@ -6,6 +6,7 @@ import org.zstack.header.network.l3.L3NetworkVO; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.List; import java.util.Map; @@ -16,6 +17,7 @@ method = HttpMethod.POST, responseClass = APIChangeVmNicNetworkEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "NicUuidToVmUuidResolver", field = "vmNicUuid") public class APIChangeVmNicNetworkMsg extends APIMessage implements VmInstanceMessage{ @APIParam(resourceType = VmNicVO.class) private String vmNicUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIChangeVmNicStateMsg.java b/header/src/main/java/org/zstack/header/vm/APIChangeVmNicStateMsg.java index 6c1594fed23..1a38b709b25 100644 --- a/header/src/main/java/org/zstack/header/vm/APIChangeVmNicStateMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIChangeVmNicStateMsg.java @@ -9,6 +9,7 @@ import org.zstack.header.other.APIMultiAuditor; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.ArrayList; import java.util.List; @@ -22,6 +23,7 @@ responseClass = APIChangeVmNicStateEvent.class, isAction = true ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "NicUuidToVmUuidResolver", field = "vmNicUuid") public class APIChangeVmNicStateMsg extends APIMessage implements VmInstanceMessage, APIMultiAuditor { @APIParam(resourceType = VmNicVO.class) private String vmNicUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIConvertTemplatedVmInstanceToVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIConvertTemplatedVmInstanceToVmInstanceMsg.java index 1a128bfaf84..0327fdbc896 100644 --- a/header/src/main/java/org/zstack/header/vm/APIConvertTemplatedVmInstanceToVmInstanceMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIConvertTemplatedVmInstanceToVmInstanceMsg.java @@ -7,6 +7,7 @@ import org.zstack.header.other.APIAuditor; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{templatedVmInstanceUuid}/convert-to-vmInstance", @@ -14,6 +15,7 @@ responseClass = APIConvertTemplatedVmInstanceToVmInstanceEvent.class, parameterName = "params" ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "templatedVmInstanceUuid") public class APIConvertTemplatedVmInstanceToVmInstanceMsg extends APIMessage implements VmInstanceMessage, APIAuditor { @APIParam(resourceType = TemplatedVmInstanceVO.class) private String templatedVmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIConvertVmInstanceToTemplatedVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIConvertVmInstanceToTemplatedVmInstanceMsg.java index 2b9191824b7..d83709a1703 100644 --- a/header/src/main/java/org/zstack/header/vm/APIConvertVmInstanceToTemplatedVmInstanceMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIConvertVmInstanceToTemplatedVmInstanceMsg.java @@ -6,6 +6,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.other.APIAuditor; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{vmInstanceUuid}/convert-to-templatedVmInstance", @@ -13,6 +14,7 @@ responseClass = APIConvertVmInstanceToTemplatedVmInstanceEvent.class, parameterName = "params" ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIConvertVmInstanceToTemplatedVmInstanceMsg extends APIMessage implements VmInstanceMessage, APIAuditor { @APIParam(resourceType = VmInstanceVO.class) private String vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIDeleteVmBootModeMsg.java b/header/src/main/java/org/zstack/header/vm/APIDeleteVmBootModeMsg.java index a2b73a69527..27d27afa839 100644 --- a/header/src/main/java/org/zstack/header/vm/APIDeleteVmBootModeMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIDeleteVmBootModeMsg.java @@ -4,12 +4,14 @@ import org.zstack.header.message.APIDeleteMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{uuid}/bootmode", method = HttpMethod.DELETE, responseClass = APIDeleteVmBootModeEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APIDeleteVmBootModeMsg extends APIDeleteMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIDeleteVmConsolePasswordMsg.java b/header/src/main/java/org/zstack/header/vm/APIDeleteVmConsolePasswordMsg.java index e9d6152c7ef..f7b0cb056a1 100755 --- a/header/src/main/java/org/zstack/header/vm/APIDeleteVmConsolePasswordMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIDeleteVmConsolePasswordMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by root on 8/2/16. @@ -13,6 +14,7 @@ method = HttpMethod.DELETE, responseClass = APIDeleteVmConsolePasswordEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APIDeleteVmConsolePasswordMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIDeleteVmHostnameMsg.java b/header/src/main/java/org/zstack/header/vm/APIDeleteVmHostnameMsg.java index 611c1c13da4..89aaa4a1e95 100755 --- a/header/src/main/java/org/zstack/header/vm/APIDeleteVmHostnameMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIDeleteVmHostnameMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIDeleteMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 2/26/2016. @@ -13,6 +14,7 @@ method = HttpMethod.DELETE, responseClass = APIDeleteVmHostnameEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APIDeleteVmHostnameMsg extends APIDeleteMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIDeleteVmSshKeyMsg.java b/header/src/main/java/org/zstack/header/vm/APIDeleteVmSshKeyMsg.java index 0372c7526ab..f10c7ed7e83 100755 --- a/header/src/main/java/org/zstack/header/vm/APIDeleteVmSshKeyMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIDeleteVmSshKeyMsg.java @@ -3,6 +3,7 @@ import org.springframework.http.HttpMethod; import org.zstack.header.message.APIMessage; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by luchukun on 8/4/16. @@ -12,6 +13,7 @@ method = HttpMethod.DELETE, responseClass = APIDeleteVmSshKeyEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APIDeleteVmSshKeyMsg extends APIMessage implements VmInstanceMessage { private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIDeleteVmStaticIpMsg.java b/header/src/main/java/org/zstack/header/vm/APIDeleteVmStaticIpMsg.java index 15d11f96647..2f1312fb903 100755 --- a/header/src/main/java/org/zstack/header/vm/APIDeleteVmStaticIpMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIDeleteVmStaticIpMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIDeleteMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 2/26/2016. @@ -13,6 +14,7 @@ method = HttpMethod.DELETE, responseClass = APIDeleteVmStaticIpEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIDeleteVmStaticIpMsg extends APIDeleteMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIDestroyVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIDestroyVmInstanceMsg.java index b93c5eb4361..9fa37d94788 100755 --- a/header/src/main/java/org/zstack/header/vm/APIDestroyVmInstanceMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIDestroyVmInstanceMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIDeleteMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.List; @@ -41,6 +42,7 @@ method = HttpMethod.DELETE, responseClass = APIDestroyVmInstanceEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APIDestroyVmInstanceMsg extends APIDeleteMessage implements VmInstanceMessage { /** * @desc vm uuid diff --git a/header/src/main/java/org/zstack/header/vm/APIDetachIsoFromVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIDetachIsoFromVmInstanceMsg.java index 52d17b61fec..3cffd78123a 100755 --- a/header/src/main/java/org/zstack/header/vm/APIDetachIsoFromVmInstanceMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIDetachIsoFromVmInstanceMsg.java @@ -7,6 +7,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.other.APIAuditor; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 10/17/2015. @@ -16,6 +17,7 @@ method = HttpMethod.DELETE, responseClass = APIDetachIsoFromVmInstanceEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIDetachIsoFromVmInstanceMsg extends APIMessage implements VmInstanceMessage, APIAuditor { @APIParam(resourceType = VmInstanceVO.class) private String vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIDetachL3NetworkFromVmMsg.java b/header/src/main/java/org/zstack/header/vm/APIDetachL3NetworkFromVmMsg.java index 0d2c7d2d38f..e226e8f962b 100755 --- a/header/src/main/java/org/zstack/header/vm/APIDetachL3NetworkFromVmMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIDetachL3NetworkFromVmMsg.java @@ -9,6 +9,7 @@ import org.zstack.header.other.APIMultiAuditor; import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.ArrayList; import java.util.List; @@ -21,6 +22,7 @@ method = HttpMethod.DELETE, responseClass = APIDetachL3NetworkFromVmEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "NicUuidToVmUuidResolver", field = "vmNicUuid") public class APIDetachL3NetworkFromVmMsg extends APIMessage implements VmInstanceMessage, APIMultiAuditor { @APIParam(resourceType = VmNicVO.class) private String vmNicUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIMigrateVmMsg.java b/header/src/main/java/org/zstack/header/vm/APIMigrateVmMsg.java index e6ed3bbf44b..1fa0f34f2f5 100755 --- a/header/src/main/java/org/zstack/header/vm/APIMigrateVmMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIMigrateVmMsg.java @@ -6,6 +6,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.message.DefaultTimeout; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.concurrent.TimeUnit; @@ -44,6 +45,7 @@ ) @SkipVmTracer(replyClass = APIMigrateVmEvent.class) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 1) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIMigrateVmMsg extends APIMessage implements VmInstanceMessage, MigrateVmMessage, CheckAttachedVolumesMessage { /** * @desc vm uuid diff --git a/header/src/main/java/org/zstack/header/vm/APIRecoverVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIRecoverVmInstanceMsg.java index 7de84b5dccd..fd0f4c44622 100755 --- a/header/src/main/java/org/zstack/header/vm/APIRecoverVmInstanceMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIRecoverVmInstanceMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 11/12/2015. @@ -14,6 +15,7 @@ method = HttpMethod.PUT, responseClass = APIRecoverVmInstanceEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APIRecoverVmInstanceMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIReimageVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIReimageVmInstanceMsg.java index 53ad2c26f4f..dc78a69c631 100755 --- a/header/src/main/java/org/zstack/header/vm/APIReimageVmInstanceMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIReimageVmInstanceMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by miao on 11/3/16. @@ -15,6 +16,7 @@ responseClass = APIReimageVmInstanceEvent.class, category = "vmInstance" ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIReimageVmInstanceMsg extends APIMessage implements VmInstanceMessage { public String getVmInstanceUuid() { return vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmBootModeMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmBootModeMsg.java index 0aa6e025c3c..e96b38cd64b 100644 --- a/header/src/main/java/org/zstack/header/vm/APISetVmBootModeMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmBootModeMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{uuid}/actions", @@ -11,6 +12,7 @@ isAction = true, responseClass = APISetVmBootModeEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APISetVmBootModeMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmBootOrderMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmBootOrderMsg.java index fb3d980e807..7400901f35c 100755 --- a/header/src/main/java/org/zstack/header/vm/APISetVmBootOrderMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmBootOrderMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.List; @@ -18,6 +19,7 @@ method = HttpMethod.PUT, responseClass = APISetVmBootOrderEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APISetVmBootOrderMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmBootVolumeMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmBootVolumeMsg.java index a72619161e0..bf1d090f845 100644 --- a/header/src/main/java/org/zstack/header/vm/APISetVmBootVolumeMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmBootVolumeMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.header.volume.VolumeVO; /** @@ -16,6 +17,7 @@ method = HttpMethod.PUT, responseClass = APISetVmBootVolumeEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APISetVmBootVolumeMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmClockTrackMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmClockTrackMsg.java index 1f25eead1db..2aa0026eb79 100644 --- a/header/src/main/java/org/zstack/header/vm/APISetVmClockTrackMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmClockTrackMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{uuid}/actions", @@ -11,6 +12,7 @@ method = HttpMethod.PUT, responseClass = APISetVmClockTrackEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APISetVmClockTrackMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmConsolePasswordMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmConsolePasswordMsg.java index 0812289e799..30c886d2833 100755 --- a/header/src/main/java/org/zstack/header/vm/APISetVmConsolePasswordMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmConsolePasswordMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.io.Serializable; @@ -18,6 +19,7 @@ method = HttpMethod.PUT, responseClass = APISetVmConsolePasswordEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APISetVmConsolePasswordMsg extends APIMessage implements VmInstanceMessage, Serializable { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmHostnameMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmHostnameMsg.java index 2b2e54afabc..2ea204e2f29 100755 --- a/header/src/main/java/org/zstack/header/vm/APISetVmHostnameMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmHostnameMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 2/26/2016. @@ -14,6 +15,7 @@ isAction = true, responseClass = APISetVmHostnameEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APISetVmHostnameMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmQxlMemoryMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmQxlMemoryMsg.java index c5d7ebd8e72..e83d4662015 100644 --- a/header/src/main/java/org/zstack/header/vm/APISetVmQxlMemoryMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmQxlMemoryMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{uuid}/actions", @@ -11,6 +12,7 @@ method = HttpMethod.PUT, responseClass = APISetVmQxlMemoryEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APISetVmQxlMemoryMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmSoundTypeMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmSoundTypeMsg.java index 686697b009c..3c348568865 100644 --- a/header/src/main/java/org/zstack/header/vm/APISetVmSoundTypeMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmSoundTypeMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{uuid}/actions", @@ -11,6 +12,7 @@ method = HttpMethod.PUT, responseClass = APISetVmSoundTypeEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APISetVmSoundTypeMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmSshKeyMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmSshKeyMsg.java index 3b2d315ea06..fc3bdf81f03 100755 --- a/header/src/main/java/org/zstack/header/vm/APISetVmSshKeyMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmSshKeyMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by luchukun on 8/4/16. @@ -15,6 +16,7 @@ method = HttpMethod.PUT, responseClass = APISetVmSshKeyEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APISetVmSshKeyMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APISetVmStaticIpMsg.java b/header/src/main/java/org/zstack/header/vm/APISetVmStaticIpMsg.java index 094f9d4a54f..c82b4064519 100755 --- a/header/src/main/java/org/zstack/header/vm/APISetVmStaticIpMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APISetVmStaticIpMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.network.l3.L3NetworkVO; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 2/26/2016. @@ -15,6 +16,7 @@ method = HttpMethod.PUT, responseClass = APISetVmStaticIpEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APISetVmStaticIpMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMsg.java b/header/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMsg.java index 60e9343ff38..d9b9d333198 100755 --- a/header/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.network.l3.L3NetworkVO; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 6/14/2015. @@ -15,6 +16,7 @@ isAction = true, responseClass = APIUpdateVmInstanceEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APIUpdateVmInstanceMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIUpdateVmNicDriverMsg.java b/header/src/main/java/org/zstack/header/vm/APIUpdateVmNicDriverMsg.java index de2fd27289c..1863f7c224e 100644 --- a/header/src/main/java/org/zstack/header/vm/APIUpdateVmNicDriverMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIUpdateVmNicDriverMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * @ Author : yh.w @@ -15,6 +16,7 @@ isAction = true, responseClass = APIUpdateVmNicDriverEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "NicUuidToVmUuidResolver", field = "vmNicUuid") public class APIUpdateVmNicDriverMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String vmInstanceUuid; diff --git a/header/src/main/java/org/zstack/header/vm/APIUpdateVmPriorityMsg.java b/header/src/main/java/org/zstack/header/vm/APIUpdateVmPriorityMsg.java index a8c6f821450..bab95b4fafa 100644 --- a/header/src/main/java/org/zstack/header/vm/APIUpdateVmPriorityMsg.java +++ b/header/src/main/java/org/zstack/header/vm/APIUpdateVmPriorityMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; @RestRequest( path = "/vm-instances/{uuid}/actions", @@ -11,6 +12,7 @@ isAction = true, responseClass = APIUpdateVmPriorityEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "uuid") public class APIUpdateVmPriorityMsg extends APIMessage implements VmInstanceMessage { @APIParam(resourceType = VmInstanceVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceState.java b/header/src/main/java/org/zstack/header/vm/VmInstanceState.java index b71d8f3be45..8c63a79c443 100755 --- a/header/src/main/java/org/zstack/header/vm/VmInstanceState.java +++ b/header/src/main/java/org/zstack/header/vm/VmInstanceState.java @@ -30,7 +30,8 @@ public enum VmInstanceState { Error(null), NoState(VmInstanceStateEvent.noState), Unknown(VmInstanceStateEvent.unknown), - Crashed(VmInstanceStateEvent.crashed); + Crashed(VmInstanceStateEvent.crashed), + Registering(null); public static List intermediateStates = new ArrayList<>(); @@ -52,6 +53,7 @@ public enum VmInstanceState { offlineStates.add(Destroyed); offlineStates.add(VolumeMigrating); offlineStates.add(Crashed); + offlineStates.add(Registering); Created.transactions( new Transaction(VmInstanceStateEvent.starting, VmInstanceState.Starting), @@ -189,6 +191,11 @@ public enum VmInstanceState { new Transaction(VmInstanceStateEvent.noState, VmInstanceState.NoState), new Transaction(VmInstanceStateEvent.destroying, VmInstanceState.Destroying) ); + Registering.transactions( + new Transaction(VmInstanceStateEvent.stopped, VmInstanceState.Stopped), + new Transaction(VmInstanceStateEvent.destroying, VmInstanceState.Destroying), + new Transaction(VmInstanceStateEvent.destroyed, VmInstanceState.Destroyed) + ); NoState.transactions( new Transaction(VmInstanceStateEvent.running, VmInstanceState.Running), new Transaction(VmInstanceStateEvent.unknown, VmInstanceState.Unknown), diff --git a/header/src/main/java/org/zstack/header/vm/cdrom/APISetVmInstanceDefaultCdRomMsg.java b/header/src/main/java/org/zstack/header/vm/cdrom/APISetVmInstanceDefaultCdRomMsg.java index f5f9b0afa76..0e1a8c93430 100644 --- a/header/src/main/java/org/zstack/header/vm/cdrom/APISetVmInstanceDefaultCdRomMsg.java +++ b/header/src/main/java/org/zstack/header/vm/cdrom/APISetVmInstanceDefaultCdRomMsg.java @@ -8,6 +8,7 @@ import org.zstack.header.rest.RestRequest; import org.zstack.header.vm.VmInstanceMessage; import org.zstack.header.vm.VmInstanceVO; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Create by lining at 2018/12/29 @@ -18,6 +19,7 @@ isAction = true, responseClass = APISetVmInstanceDefaultCdRomEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APISetVmInstanceDefaultCdRomMsg extends APIMessage implements VmInstanceMessage, APIAuditor { @APIParam(resourceType = VmCdRomVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataEvent.java b/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataEvent.java new file mode 100644 index 00000000000..39313f598cb --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataEvent.java @@ -0,0 +1,53 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.message.APIEvent; +import org.zstack.header.rest.RestResponse; + +import java.util.List; + +@RestResponse(fieldsTo = {"all"}) +public class APICleanupVmInstanceMetadataEvent extends APIEvent { + private Integer totalCleaned; + private Integer totalFailed; + private List failedVmUuids; + + public APICleanupVmInstanceMetadataEvent() { + super(null); + } + + public APICleanupVmInstanceMetadataEvent(String apiId) { + super(apiId); + } + + public Integer getTotalCleaned() { + return totalCleaned; + } + + public void setTotalCleaned(Integer totalCleaned) { + this.totalCleaned = totalCleaned; + } + + public Integer getTotalFailed() { + return totalFailed; + } + + public void setTotalFailed(Integer totalFailed) { + this.totalFailed = totalFailed; + } + + public List getFailedVmUuids() { + return failedVmUuids; + } + + public void setFailedVmUuids(List failedVmUuids) { + this.failedVmUuids = failedVmUuids; + } + + public static APICleanupVmInstanceMetadataEvent __example__() { + APICleanupVmInstanceMetadataEvent evt = new APICleanupVmInstanceMetadataEvent(); + evt.totalCleaned = 5; + evt.totalFailed = 0; + evt.failedVmUuids = java.util.Collections.emptyList(); + return evt; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataEventDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataEventDoc_zh_cn.groovy new file mode 100644 index 00000000000..3a3c398dde0 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataEventDoc_zh_cn.groovy @@ -0,0 +1,42 @@ +package org.zstack.header.vm.metadata + +import java.lang.Integer +import org.zstack.header.errorcode.ErrorCode + +doc { + + title "清理云主机元数据返回" + + field { + name "totalCleaned" + desc "成功清理的元数据数量" + type "Integer" + since "5.0.0" + } + field { + name "totalFailed" + desc "清理失败的元数据数量" + type "Integer" + since "5.0.0" + } + field { + name "failedVmUuids" + desc "清理失败的云主机UUID列表" + type "List" + since "5.0.0" + } + field { + name "success" + desc "操作是否成功" + type "boolean" + since "5.0.0" + } + ref { + name "error" + path "org.zstack.header.vm.metadata.APICleanupVmInstanceMetadataEvent.error" + desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null" + type "ErrorCode" + since "5.0.0" + clz ErrorCode.class + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataMsg.java b/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataMsg.java new file mode 100644 index 00000000000..5823f66b78f --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataMsg.java @@ -0,0 +1,34 @@ +package org.zstack.header.vm.metadata; + +import org.springframework.http.HttpMethod; +import org.zstack.header.message.APIMessage; +import org.zstack.header.message.APIParam; +import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.VmInstanceVO; + +import java.util.List; + +@RestRequest( + path = "/vm-instances/metadata/cleanup", + method = HttpMethod.PUT, + responseClass = APICleanupVmInstanceMetadataEvent.class, + isAction = true +) +public class APICleanupVmInstanceMetadataMsg extends APIMessage { + @APIParam(resourceType = VmInstanceVO.class, nonempty = true) + private List vmUuids; + + public List getVmUuids() { + return vmUuids; + } + + public void setVmUuids(List vmUuids) { + this.vmUuids = vmUuids; + } + + public static APICleanupVmInstanceMetadataMsg __example__() { + APICleanupVmInstanceMetadataMsg msg = new APICleanupVmInstanceMetadataMsg(); + msg.vmUuids = java.util.Arrays.asList(uuid(), uuid()); + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataMsgDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataMsgDoc_zh_cn.groovy new file mode 100644 index 00000000000..0c29f0d6106 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APICleanupVmInstanceMetadataMsgDoc_zh_cn.groovy @@ -0,0 +1,58 @@ +package org.zstack.header.vm.metadata + +import org.zstack.header.vm.metadata.APICleanupVmInstanceMetadataEvent + +doc { + title "清理云主机元数据" + + category "云主机" + + desc """清理指定云主机在主存储上的元数据文件""" + + rest { + request { + url "PUT /v1/vm-instances/metadata/cleanup" + + header (Authorization: 'OAuth the-session-uuid') + + clz APICleanupVmInstanceMetadataMsg.class + + desc """""" + + params { + + column { + name "vmUuids" + enclosedIn "cleanupVmInstanceMetadata" + desc "需要清理元数据的云主机UUID列表" + location "body" + type "List" + optional false + since "5.0.0" + } + column { + name "systemTags" + enclosedIn "" + desc "系统标签" + location "body" + type "List" + optional true + since "5.0.0" + } + column { + name "userTags" + enclosedIn "" + desc "用户标签" + location "body" + type "List" + optional true + since "5.0.0" + } + } + } + + response { + clz APICleanupVmInstanceMetadataEvent.class + } + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageEvent.java b/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageEvent.java new file mode 100644 index 00000000000..6dded8354a1 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageEvent.java @@ -0,0 +1,30 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.message.APIEvent; +import org.zstack.header.rest.RestResponse; + +@RestResponse(fieldsTo = {"all"}) +public class APIGetVmInstanceMetadataFromPrimaryStorageEvent extends APIEvent { + private String metadata; + + public APIGetVmInstanceMetadataFromPrimaryStorageEvent() { + super(null); + } + + public APIGetVmInstanceMetadataFromPrimaryStorageEvent(String apiId) { + super(apiId); + } + + public String getMetadata() { + return metadata; + } + + public void setMetadata(String metadata) { + this.metadata = metadata; + } + + public static APIGetVmInstanceMetadataFromPrimaryStorageEvent __example__() { + APIGetVmInstanceMetadataFromPrimaryStorageEvent evt = new APIGetVmInstanceMetadataFromPrimaryStorageEvent(); + return evt; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageEventDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageEventDoc_zh_cn.groovy new file mode 100644 index 00000000000..2944ee0ed4f --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageEventDoc_zh_cn.groovy @@ -0,0 +1,29 @@ +package org.zstack.header.vm.metadata + +import org.zstack.header.errorcode.ErrorCode + +doc { + + title "获取云主机元数据返回" + + field { + name "metadata" + desc "云主机元数据内容" + type "String" + since "5.0.0" + } + field { + name "success" + desc "操作是否成功" + type "boolean" + since "5.0.0" + } + ref { + name "error" + path "org.zstack.header.vm.metadata.APIGetVmInstanceMetadataFromPrimaryStorageEvent.error" + desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null" + type "ErrorCode" + since "5.0.0" + clz ErrorCode.class + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageMsg.java new file mode 100644 index 00000000000..5b4c46853a9 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageMsg.java @@ -0,0 +1,37 @@ +package org.zstack.header.vm.metadata; + +import org.springframework.http.HttpMethod; +import org.zstack.header.message.APIMessage; +import org.zstack.header.message.APIParam; +import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.VmInstanceMessage; +import org.zstack.header.vm.VmInstanceVO; + +@RestRequest( + path = "/primary-storage/vm-instances/metadata", + method = HttpMethod.GET, + responseClass = APIGetVmInstanceMetadataFromPrimaryStorageEvent.class +) +public class APIGetVmInstanceMetadataFromPrimaryStorageMsg extends APIMessage implements VmInstanceMessage { + @APIParam(resourceType = VmInstanceVO.class) + private String uuid; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getVmInstanceUuid() { + return uuid; + } + + public static APIGetVmInstanceMetadataFromPrimaryStorageMsg __example__() { + APIGetVmInstanceMetadataFromPrimaryStorageMsg msg = new APIGetVmInstanceMetadataFromPrimaryStorageMsg(); + msg.setUuid(uuid(VmInstanceVO.class)); + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy new file mode 100644 index 00000000000..d20565dd003 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy @@ -0,0 +1,58 @@ +package org.zstack.header.vm.metadata + +import org.zstack.header.vm.metadata.APIGetVmInstanceMetadataFromPrimaryStorageEvent + +doc { + title "获取云主机元数据" + + category "主存储" + + desc """从主存储获取指定云主机的元数据内容""" + + rest { + request { + url "GET /v1/primary-storage/vm-instances/metadata" + + header (Authorization: 'OAuth the-session-uuid') + + clz APIGetVmInstanceMetadataFromPrimaryStorageMsg.class + + desc """""" + + params { + + column { + name "uuid" + enclosedIn "" + desc "云主机UUID" + location "query" + type "String" + optional false + since "5.0.0" + } + column { + name "systemTags" + enclosedIn "" + desc "系统标签" + location "query" + type "List" + optional true + since "5.0.0" + } + column { + name "userTags" + enclosedIn "" + desc "用户标签" + location "query" + type "List" + optional true + since "5.0.0" + } + } + } + + response { + clz APIGetVmInstanceMetadataFromPrimaryStorageEvent.class + } + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataEvent.java b/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataEvent.java new file mode 100644 index 00000000000..b1dc83a0c3b --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataEvent.java @@ -0,0 +1,35 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.message.APIEvent; +import org.zstack.header.rest.RestResponse; +import org.zstack.header.vm.VmInstanceInventory; + +@RestResponse(fieldsTo = {"all"}) +public class APIRegisterVmInstanceFromMetadataEvent extends APIEvent { + private VmInstanceInventory inventory; + + public APIRegisterVmInstanceFromMetadataEvent() { + super(null); + } + + public APIRegisterVmInstanceFromMetadataEvent(String apiId) { + super(apiId); + } + + public VmInstanceInventory getInventory() { + return inventory; + } + + public void setInventory(VmInstanceInventory inventory) { + this.inventory = inventory; + } + + public static APIRegisterVmInstanceFromMetadataEvent __example__() { + APIRegisterVmInstanceFromMetadataEvent evt = new APIRegisterVmInstanceFromMetadataEvent(); + VmInstanceInventory vm = new VmInstanceInventory(); + vm.setUuid(uuid()); + vm.setName("recovered-vm"); + evt.setInventory(vm); + return evt; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataEventDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataEventDoc_zh_cn.groovy new file mode 100644 index 00000000000..e4f286b8ddc --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataEventDoc_zh_cn.groovy @@ -0,0 +1,32 @@ +package org.zstack.header.vm.metadata + +import org.zstack.header.vm.VmInstanceInventory +import org.zstack.header.errorcode.ErrorCode + +doc { + + title "从元数据注册云主机返回" + + ref { + name "inventory" + path "org.zstack.header.vm.metadata.APIRegisterVmInstanceFromMetadataEvent.inventory" + desc "云主机详情" + type "VmInstanceInventory" + since "5.0.0" + clz VmInstanceInventory.class + } + field { + name "success" + desc "操作是否成功" + type "boolean" + since "5.0.0" + } + ref { + name "error" + path "org.zstack.header.vm.metadata.APIRegisterVmInstanceFromMetadataEvent.error" + desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null" + type "ErrorCode" + since "5.0.0" + clz ErrorCode.class + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataMsg.java b/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataMsg.java new file mode 100644 index 00000000000..ec862f4d3c7 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataMsg.java @@ -0,0 +1,102 @@ +package org.zstack.header.vm.metadata; + +import org.springframework.http.HttpMethod; +import org.zstack.header.message.APIMessage; +import org.zstack.header.message.APIParam; +import org.zstack.header.message.DefaultTimeout; +import org.zstack.header.rest.RestRequest; +import org.zstack.header.storage.primary.PrimaryStorageVO; +import org.zstack.header.vm.VmInstanceVO; +import org.zstack.header.zone.ZoneVO; +import org.zstack.header.cluster.ClusterVO; +import org.zstack.header.host.HostVO; +import org.zstack.header.tag.TagResourceType; + +import java.util.concurrent.TimeUnit; + +@TagResourceType(VmInstanceVO.class) +@RestRequest( + path = "/vm-instances/metadata/register", + method = HttpMethod.POST, + responseClass = APIRegisterVmInstanceFromMetadataEvent.class, + parameterName = "params" +) +@DefaultTimeout(timeunit = TimeUnit.HOURS, value = 1) +public class APIRegisterVmInstanceFromMetadataMsg extends APIMessage { + @APIParam(nonempty = true, maxLength = 2048) + private String metadataPath; + + @APIParam(resourceType = PrimaryStorageVO.class) + private String primaryStorageUuid; + + @APIParam(resourceType = ZoneVO.class) + private String zoneUuid; + + @APIParam(resourceType = ClusterVO.class) + private String clusterUuid; + + @APIParam(resourceType = HostVO.class) + private String hostUuid; + + @APIParam(required = false, maxLength = 255) + private String name; + + public String getMetadataPath() { + return metadataPath; + } + + public void setMetadataPath(String metadataPath) { + this.metadataPath = metadataPath; + } + + public String getPrimaryStorageUuid() { + return primaryStorageUuid; + } + + public void setPrimaryStorageUuid(String primaryStorageUuid) { + this.primaryStorageUuid = primaryStorageUuid; + } + + public String getZoneUuid() { + return zoneUuid; + } + + public void setZoneUuid(String zoneUuid) { + this.zoneUuid = zoneUuid; + } + + 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 getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static APIRegisterVmInstanceFromMetadataMsg __example__() { + APIRegisterVmInstanceFromMetadataMsg msg = new APIRegisterVmInstanceFromMetadataMsg(); + msg.metadataPath = "/mnt/ps/vm-metadata/vm-uuid.vmmeta"; + msg.primaryStorageUuid = uuid(PrimaryStorageVO.class); + msg.zoneUuid = uuid(ZoneVO.class); + msg.clusterUuid = uuid(ClusterVO.class); + msg.hostUuid = uuid(HostVO.class); + msg.name = "my-restored-vm"; + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataMsgDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataMsgDoc_zh_cn.groovy new file mode 100644 index 00000000000..8e604678819 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIRegisterVmInstanceFromMetadataMsgDoc_zh_cn.groovy @@ -0,0 +1,112 @@ +package org.zstack.header.vm.metadata + +import org.zstack.header.vm.metadata.APIRegisterVmInstanceFromMetadataEvent + +doc { + title "从元数据注册云主机(RegisterVmInstanceFromMetadata)" + + category "云主机" + + desc """根据主存储上的元数据文件注册(恢复)一台云主机""" + + rest { + request { + url "POST /v1/vm-instances/metadata/register" + + header (Authorization: 'OAuth the-session-uuid') + + clz APIRegisterVmInstanceFromMetadataMsg.class + + desc """""" + + params { + + column { + name "metadataPath" + enclosedIn "params" + desc "元数据文件在主存储上的路径" + location "body" + type "String" + optional false + since "5.0.0" + } + column { + name "primaryStorageUuid" + enclosedIn "params" + desc "目标主存储UUID" + location "body" + type "String" + optional false + since "5.0.0" + } + column { + name "zoneUuid" + enclosedIn "params" + desc "区域UUID" + location "body" + type "String" + optional false + since "5.0.0" + } + column { + name "clusterUuid" + enclosedIn "params" + desc "集群UUID" + location "body" + type "String" + optional false + since "5.0.0" + } + column { + name "hostUuid" + enclosedIn "params" + desc "物理机UUID" + location "body" + type "String" + optional false + since "5.0.0" + } + column { + name "name" + enclosedIn "params" + desc "云主机名称" + location "body" + type "String" + optional true + since "5.0.0" + } + column { + name "tagUuids" + enclosedIn "params" + desc "标签UUID列表" + location "body" + type "List" + optional true + since "5.0.0" + } + column { + name "systemTags" + enclosedIn "" + desc "系统标签" + location "body" + type "List" + optional true + since "5.0.0" + } + column { + name "userTags" + enclosedIn "" + desc "用户标签" + location "body" + type "List" + optional true + since "5.0.0" + } + } + } + + response { + clz APIRegisterVmInstanceFromMetadataEvent.class + } + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataEvent.java b/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataEvent.java new file mode 100644 index 00000000000..897f3a0ef4e --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataEvent.java @@ -0,0 +1,19 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.message.APIEvent; +import org.zstack.header.rest.RestResponse; + +@RestResponse(fieldsTo = {"all"}) +public class APIUpdateVmInstanceMetadataEvent extends APIEvent { + public APIUpdateVmInstanceMetadataEvent() { + super(null); + } + + public APIUpdateVmInstanceMetadataEvent(String apiId) { + super(apiId); + } + + public static APIUpdateVmInstanceMetadataEvent __example__() { + return new APIUpdateVmInstanceMetadataEvent(); + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovy new file mode 100644 index 00000000000..e6d90be232e --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovy @@ -0,0 +1,23 @@ +package org.zstack.header.vm.metadata + +import org.zstack.header.errorcode.ErrorCode + +doc { + + title "更新云主机元数据返回" + + field { + name "success" + desc "操作是否成功" + type "boolean" + since "5.0.0" + } + ref { + name "error" + path "org.zstack.header.vm.metadata.APIUpdateVmInstanceMetadataEvent.error" + desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null" + type "ErrorCode" + since "5.0.0" + clz ErrorCode.class + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataMsg.java b/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataMsg.java new file mode 100644 index 00000000000..46e529b4fbd --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataMsg.java @@ -0,0 +1,35 @@ +package org.zstack.header.vm.metadata; + +import org.springframework.http.HttpMethod; +import org.zstack.header.message.APIMessage; +import org.zstack.header.message.APIParam; +import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.VmInstanceVO; + +import java.util.Collections; +import java.util.List; + +@RestRequest( + path = "/vm-instances/metadata/actions", + method = HttpMethod.PUT, + responseClass = APIUpdateVmInstanceMetadataEvent.class, + isAction = true +) +public class APIUpdateVmInstanceMetadataMsg extends APIMessage { + @APIParam(resourceType = VmInstanceVO.class, nonempty = true) + private List vmUuids; + + public List getVmUuids() { + return vmUuids; + } + + public void setVmUuids(List vmUuids) { + this.vmUuids = vmUuids; + } + + public static APIUpdateVmInstanceMetadataMsg __example__() { + APIUpdateVmInstanceMetadataMsg msg = new APIUpdateVmInstanceMetadataMsg(); + msg.vmUuids = Collections.singletonList(uuid()); + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataMsgDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataMsgDoc_zh_cn.groovy new file mode 100644 index 00000000000..04330f73fe3 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/APIUpdateVmInstanceMetadataMsgDoc_zh_cn.groovy @@ -0,0 +1,58 @@ +package org.zstack.header.vm.metadata + +import org.zstack.header.vm.metadata.APIUpdateVmInstanceMetadataEvent + +doc { + title "更新云主机元数据" + + category "云主机" + + desc """立即触发指定云主机的元数据更新""" + + rest { + request { + url "PUT /v1/vm-instances/metadata/actions" + + header (Authorization: 'OAuth the-session-uuid') + + clz APIUpdateVmInstanceMetadataMsg.class + + desc """""" + + params { + + column { + name "vmUuids" + enclosedIn "updateVmInstanceMetadata" + desc "需要更新元数据的云主机UUID列表" + location "body" + type "List" + optional false + since "5.0.0" + } + column { + name "systemTags" + enclosedIn "" + desc "系统标签" + location "body" + type "List" + optional true + since "5.0.0" + } + column { + name "userTags" + enclosedIn "" + desc "用户标签" + location "body" + type "List" + optional true + since "5.0.0" + } + } + } + + response { + clz APIUpdateVmInstanceMetadataEvent.class + } + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.java b/header/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.java new file mode 100644 index 00000000000..198156c7259 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.java @@ -0,0 +1,39 @@ +package org.zstack.header.vm.metadata; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MetadataImpact { + Impact value(); + + /** + * Spring bean name of the {@link VmUuidFromApiResolver} that converts the + * field value(s) into VM UUID(s). + * + *

Must be specified for {@link Impact#CONFIG} and {@link Impact#STORAGE}; + * ignored for {@link Impact#NONE}.

+ */ + String resolver() default ""; + + /** + * The field name in the API message whose value will be extracted (via getter + * reflection) and passed to the resolver. + * + *

For example, {@code field = "volumeUuid"} means the interceptor will call + * {@code msg.getVolumeUuid()} and pass the result to + * {@link VmUuidFromApiResolver#resolveVmUuids(String)}.

+ * + *

The field value may be a {@code String} or {@code List}.

+ */ + String field() default ""; + + boolean updateOnFailure() default false; + + enum Impact { + NONE, + CONFIG, + STORAGE + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/ResourceMetadata.java b/header/src/main/java/org/zstack/header/vm/metadata/ResourceMetadata.java new file mode 100644 index 00000000000..e450d65f3f6 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/ResourceMetadata.java @@ -0,0 +1,40 @@ +package org.zstack.header.vm.metadata; + +public class ResourceMetadata { + private String resourceUuid; + private String vo; + private String systemTags; + private String resourceConfigs; + + public String getResourceUuid() { + return resourceUuid; + } + + public void setResourceUuid(String resourceUuid) { + this.resourceUuid = resourceUuid; + } + + public String getVo() { + return vo; + } + + public void setVo(String vo) { + this.vo = vo; + } + + public String getSystemTags() { + return systemTags; + } + + public void setSystemTags(String systemTags) { + this.systemTags = systemTags; + } + + public String getResourceConfigs() { + return resourceConfigs; + } + + public void setResourceConfigs(String resourceConfigs) { + this.resourceConfigs = resourceConfigs; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataMsg.java b/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataMsg.java new file mode 100644 index 00000000000..bf3956f41fc --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataMsg.java @@ -0,0 +1,26 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.message.NeedReplyMessage; +import org.zstack.header.vm.VmInstanceMessage; + +public class UpdateVmInstanceMetadataMsg extends NeedReplyMessage implements VmInstanceMessage { + private String vmInstanceUuid; + private boolean storageStructureChange; + + @Override + public String getVmInstanceUuid() { + return vmInstanceUuid; + } + + public void setVmInstanceUuid(String vmInstanceUuid) { + this.vmInstanceUuid = vmInstanceUuid; + } + + public boolean isStorageStructureChange() { + return storageStructureChange; + } + + public void setStorageStructureChange(boolean storageStructureChange) { + this.storageStructureChange = storageStructureChange; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataOnPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataOnPrimaryStorageMsg.java new file mode 100644 index 00000000000..ba12e4b918e --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataOnPrimaryStorageMsg.java @@ -0,0 +1,98 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.message.NeedReplyMessage; +import org.zstack.header.storage.primary.PrimaryStorageMessage; + +public class UpdateVmInstanceMetadataOnPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage { + private String primaryStorageUuid; + private String vmInstanceUuid; + private String rootVolumeUuid; + private String vmInstanceName; + private String vmCategory; + private String architecture; + private String metadata; + private String schemaVersion; + private boolean storageStructureChange; + private String metadataPath; + + @Override + public String getPrimaryStorageUuid() { + return primaryStorageUuid; + } + + public void setPrimaryStorageUuid(String primaryStorageUuid) { + this.primaryStorageUuid = primaryStorageUuid; + } + + public String getVmInstanceUuid() { + return vmInstanceUuid; + } + + public void setVmInstanceUuid(String vmInstanceUuid) { + this.vmInstanceUuid = vmInstanceUuid; + } + + public String getRootVolumeUuid() { + return rootVolumeUuid; + } + + public void setRootVolumeUuid(String rootVolumeUuid) { + this.rootVolumeUuid = rootVolumeUuid; + } + + public String getVmInstanceName() { + return vmInstanceName; + } + + public void setVmInstanceName(String vmInstanceName) { + this.vmInstanceName = vmInstanceName; + } + + public String getVmCategory() { + return vmCategory; + } + + public void setVmCategory(String vmCategory) { + this.vmCategory = vmCategory; + } + + public String getArchitecture() { + return architecture; + } + + public void setArchitecture(String architecture) { + this.architecture = architecture; + } + + public String getMetadata() { + return metadata; + } + + public void setMetadata(String metadata) { + this.metadata = metadata; + } + + public String getSchemaVersion() { + return schemaVersion; + } + + public void setSchemaVersion(String schemaVersion) { + this.schemaVersion = schemaVersion; + } + + public boolean isStorageStructureChange() { + return storageStructureChange; + } + + public void setStorageStructureChange(boolean storageStructureChange) { + this.storageStructureChange = storageStructureChange; + } + + public String getMetadataPath() { + return metadataPath; + } + + public void setMetadataPath(String metadataPath) { + this.metadataPath = metadataPath; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataOnPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataOnPrimaryStorageReply.java new file mode 100644 index 00000000000..d8323171909 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataOnPrimaryStorageReply.java @@ -0,0 +1,6 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.message.MessageReply; + +public class UpdateVmInstanceMetadataOnPrimaryStorageReply extends MessageReply { +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataReply.java b/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataReply.java new file mode 100644 index 00000000000..98296a42b3c --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataReply.java @@ -0,0 +1,15 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.message.MessageReply; + +public class UpdateVmInstanceMetadataReply extends MessageReply { + private String metadata; + + public String getMetadata() { + return metadata; + } + + public void setMetadata(String metadata) { + this.metadata = metadata; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataConstants.java b/header/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataConstants.java new file mode 100644 index 00000000000..a47f05e0157 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataConstants.java @@ -0,0 +1,27 @@ +package org.zstack.header.vm.metadata; + +public class VmInstanceMetadataConstants { + private VmInstanceMetadataConstants() { + } + + public static final String SBLK_METADATA_LV_SUFFIX = "_vmmeta"; + public static final String FILE_METADATA_SUFFIX = ".vmmeta"; + public static final String METADATA_DIR_NAME = "vm-metadata"; + + private static final String[] STORAGE_PATH_MARKERS = { + "/rootVolumes/", "/dataVolumes/", "/volumeSnapshots/", "/memory/" + }; + + public static String extractOldPrefix(String path) { + if (path == null || !path.startsWith("/")) { + return null; + } + for (String marker : STORAGE_PATH_MARKERS) { + int idx = path.indexOf(marker); + if (idx >= 0) { + return path.substring(0, idx + 1); + } + } + return null; + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataDTO.java b/header/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataDTO.java new file mode 100644 index 00000000000..311ddbde784 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataDTO.java @@ -0,0 +1,78 @@ +package org.zstack.header.vm.metadata; + +import java.util.List; + +public class VmInstanceMetadataDTO { + private String schemaVersion; + private VmMetadataCategory vmCategory; + private ResourceMetadata vm; + private List volumes; + private List nics; + private List snapshots; + private List snapshotGroups; + private List snapshotGroupRefs; + + public String getSchemaVersion() { + return schemaVersion; + } + + public void setSchemaVersion(String schemaVersion) { + this.schemaVersion = schemaVersion; + } + + public VmMetadataCategory getVmCategory() { + return vmCategory; + } + + public void setVmCategory(VmMetadataCategory vmCategory) { + this.vmCategory = vmCategory; + } + + public ResourceMetadata getVm() { + return vm; + } + + public void setVm(ResourceMetadata vm) { + this.vm = vm; + } + + public List getVolumes() { + return volumes; + } + + public void setVolumes(List volumes) { + this.volumes = volumes; + } + + public List getNics() { + return nics; + } + + public void setNics(List nics) { + this.nics = nics; + } + + public List getSnapshots() { + return snapshots; + } + + public void setSnapshots(List snapshots) { + this.snapshots = snapshots; + } + + public List getSnapshotGroups() { + return snapshotGroups; + } + + public void setSnapshotGroups(List snapshotGroups) { + this.snapshotGroups = snapshotGroups; + } + + public List getSnapshotGroupRefs() { + return snapshotGroupRefs; + } + + public void setSnapshotGroupRefs(List snapshotGroupRefs) { + this.snapshotGroupRefs = snapshotGroupRefs; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataCategory.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataCategory.java new file mode 100644 index 00000000000..a2a09028d27 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataCategory.java @@ -0,0 +1,7 @@ +package org.zstack.header.vm.metadata; + +public enum VmMetadataCategory { + VM, + VM_TEMPLATE, + VM_TEMPLATE_CACHE +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyService.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyService.java new file mode 100644 index 00000000000..ff17ffcf7f4 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyService.java @@ -0,0 +1,29 @@ +package org.zstack.header.vm.metadata; + +/** + * Service interface for marking VM metadata as dirty. + *

+ * Implementations live in the premium module (VmMetadataDirtyMarker). + * The zstack core module uses this interface via {@code @Autowired(required = false)} + * so that the feature degrades gracefully when the premium module is not loaded. + */ +public interface VmMetadataDirtyService { + /** + * Mark the VM's metadata as dirty so that it will be flushed + * to primary storage on the next poll cycle. + * + * @param vmInstanceUuid the VM whose metadata changed + * @return true if the mark was actually written (feature enabled), false otherwise + */ + boolean markDirty(String vmInstanceUuid); + + /** + * Mark the VM's metadata as dirty, optionally flagging a storage-structure change + * (e.g. volume attach/detach, snapshot, migration). + * + * @param vmInstanceUuid the VM whose metadata changed + * @param storageStructureChange true if the change affects storage topology + * @return true if the mark was actually written (feature enabled), false otherwise + */ + boolean markDirty(String vmInstanceUuid, boolean storageStructureChange); +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyVO.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyVO.java new file mode 100644 index 00000000000..f313699b82d --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyVO.java @@ -0,0 +1,121 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.managementnode.ManagementNodeVO; +import org.zstack.header.vm.VmInstanceEO; +import org.zstack.header.vm.VmInstanceVO; +import org.zstack.header.vo.ForeignKey; +import org.zstack.header.vo.ForeignKey.ReferenceOption; + +import javax.persistence.*; +import java.sql.Timestamp; + +@Entity +@Table +public class VmMetadataDirtyVO { + @Id + @Column + @ForeignKey(parentEntityClass = VmInstanceEO.class, onDeleteAction = ReferenceOption.CASCADE) + private String vmInstanceUuid; + + @Column + @ForeignKey(parentEntityClass = ManagementNodeVO.class, onDeleteAction = ReferenceOption.SET_NULL) + private String managementNodeUuid; + + @Column + private long dirtyVersion; + + @Column + private Timestamp lastClaimTime; + + @Column + private boolean storageStructureChange; + + @Column + private int retryCount; + + @Column + private Timestamp nextRetryTime; + + @Column + private Timestamp createDate; + + @Column + private Timestamp lastOpDate; + + @PreUpdate + private void preUpdate() { + lastOpDate = null; + } + + public String getVmInstanceUuid() { + return vmInstanceUuid; + } + + public void setVmInstanceUuid(String vmInstanceUuid) { + this.vmInstanceUuid = vmInstanceUuid; + } + + public String getManagementNodeUuid() { + return managementNodeUuid; + } + + public void setManagementNodeUuid(String managementNodeUuid) { + this.managementNodeUuid = managementNodeUuid; + } + + public long getDirtyVersion() { + return dirtyVersion; + } + + public void setDirtyVersion(long dirtyVersion) { + this.dirtyVersion = dirtyVersion; + } + + public Timestamp getLastClaimTime() { + return lastClaimTime; + } + + public void setLastClaimTime(Timestamp lastClaimTime) { + this.lastClaimTime = lastClaimTime; + } + + public boolean isStorageStructureChange() { + return storageStructureChange; + } + + public void setStorageStructureChange(boolean storageStructureChange) { + this.storageStructureChange = storageStructureChange; + } + + public int getRetryCount() { + return retryCount; + } + + public void setRetryCount(int retryCount) { + this.retryCount = retryCount; + } + + public Timestamp getNextRetryTime() { + return nextRetryTime; + } + + public void setNextRetryTime(Timestamp nextRetryTime) { + this.nextRetryTime = nextRetryTime; + } + + public Timestamp getCreateDate() { + return createDate; + } + + public void setCreateDate(Timestamp createDate) { + this.createDate = createDate; + } + + public Timestamp getLastOpDate() { + return lastOpDate; + } + + public void setLastOpDate(Timestamp lastOpDate) { + this.lastOpDate = lastOpDate; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyVO_.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyVO_.java new file mode 100644 index 00000000000..059ff6ad20f --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyVO_.java @@ -0,0 +1,18 @@ +package org.zstack.header.vm.metadata; + +import javax.persistence.metamodel.SingularAttribute; +import javax.persistence.metamodel.StaticMetamodel; +import java.sql.Timestamp; + +@StaticMetamodel(VmMetadataDirtyVO.class) +public class VmMetadataDirtyVO_ { + public static volatile SingularAttribute vmInstanceUuid; + public static volatile SingularAttribute managementNodeUuid; + public static volatile SingularAttribute dirtyVersion; + public static volatile SingularAttribute storageStructureChange; + public static volatile SingularAttribute retryCount; + public static volatile SingularAttribute nextRetryTime; + public static volatile SingularAttribute lastClaimTime; + public static volatile SingularAttribute createDate; + public static volatile SingularAttribute lastOpDate; +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataErrors.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataErrors.java new file mode 100644 index 00000000000..f06ad8362dd --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataErrors.java @@ -0,0 +1,28 @@ +package org.zstack.header.vm.metadata; + +public enum VmMetadataErrors { + METADATA_INVALID_FORMAT(1300), + METADATA_SCHEMA_VERSION_MISMATCH(1301), + METADATA_UUID_CONFLICT(1302), + METADATA_STORAGE_NOT_SUPPORTED(1303), + METADATA_CROSS_STORAGE_FORBIDDEN(1304), + METADATA_INSTALL_PATH_NOT_FOUND(1305), + METADATA_CACHE_VM_NOT_REGISTERABLE(1306), + METADATA_VM_REGISTERING(1307), + METADATA_READ_CORRUPTED(1308), + METADATA_PAYLOAD_TOO_LARGE(1309), + METADATA_PS_UNREACHABLE(1310), + METADATA_FEATURE_DISABLED(1311), + ; + + private String code; + + private VmMetadataErrors(int id) { + code = String.format("VM_METADATA.%s", id); + } + + @Override + public String toString() { + return code; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataFingerprintVO.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataFingerprintVO.java new file mode 100644 index 00000000000..dba4fae3e71 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataFingerprintVO.java @@ -0,0 +1,97 @@ +package org.zstack.header.vm.metadata; + +import org.zstack.header.vm.VmInstanceEO; +import org.zstack.header.vo.ForeignKey; +import org.zstack.header.vo.ForeignKey.ReferenceOption; + +import javax.persistence.*; +import java.sql.Timestamp; + +@Entity +@Table +public class VmMetadataFingerprintVO { + @Id + @Column + @ForeignKey(parentEntityClass = VmInstanceEO.class, onDeleteAction = ReferenceOption.CASCADE) + private String vmInstanceUuid; + + @Column + private Timestamp lastFlushTime; + + @Column + private boolean lastFlushFailed; + + @Column + private int staleRecoveryCount; + + @Column + @Lob + private String metadataSnapshot; + + @Column + private Timestamp createDate; + + @Column + private Timestamp lastOpDate; + + @PreUpdate + private void preUpdate() { + lastOpDate = null; + } + + public String getVmInstanceUuid() { + return vmInstanceUuid; + } + + public void setVmInstanceUuid(String vmInstanceUuid) { + this.vmInstanceUuid = vmInstanceUuid; + } + + public Timestamp getLastFlushTime() { + return lastFlushTime; + } + + public void setLastFlushTime(Timestamp lastFlushTime) { + this.lastFlushTime = lastFlushTime; + } + + public boolean isLastFlushFailed() { + return lastFlushFailed; + } + + public void setLastFlushFailed(boolean lastFlushFailed) { + this.lastFlushFailed = lastFlushFailed; + } + + public int getStaleRecoveryCount() { + return staleRecoveryCount; + } + + public void setStaleRecoveryCount(int staleRecoveryCount) { + this.staleRecoveryCount = staleRecoveryCount; + } + + public String getMetadataSnapshot() { + return metadataSnapshot; + } + + public void setMetadataSnapshot(String metadataSnapshot) { + this.metadataSnapshot = metadataSnapshot; + } + + public Timestamp getCreateDate() { + return createDate; + } + + public void setCreateDate(Timestamp createDate) { + this.createDate = createDate; + } + + public Timestamp getLastOpDate() { + return lastOpDate; + } + + public void setLastOpDate(Timestamp lastOpDate) { + this.lastOpDate = lastOpDate; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataFingerprintVO_.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataFingerprintVO_.java new file mode 100644 index 00000000000..d2a2876f4f1 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataFingerprintVO_.java @@ -0,0 +1,16 @@ +package org.zstack.header.vm.metadata; + +import javax.persistence.metamodel.SingularAttribute; +import javax.persistence.metamodel.StaticMetamodel; +import java.sql.Timestamp; + +@StaticMetamodel(VmMetadataFingerprintVO.class) +public class VmMetadataFingerprintVO_ { + public static volatile SingularAttribute vmInstanceUuid; + public static volatile SingularAttribute lastFlushTime; + public static volatile SingularAttribute lastFlushFailed; + public static volatile SingularAttribute staleRecoveryCount; + public static volatile SingularAttribute metadataSnapshot; + public static volatile SingularAttribute createDate; + public static volatile SingularAttribute lastOpDate; +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathBuildExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathBuildExtensionPoint.java new file mode 100644 index 00000000000..f7921b11c8d --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathBuildExtensionPoint.java @@ -0,0 +1,8 @@ +package org.zstack.header.vm.metadata; + +public interface VmMetadataPathBuildExtensionPoint { + String getPrimaryStorageType(); + String buildVmMetadataPath(String primaryStorageUuid, String vmInstanceUuid); + String buildMetadataDir(String primaryStorageUuid); + String validateMetadataPath(String primaryStorageUuid, String path); +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathReplacementExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathReplacementExtensionPoint.java new file mode 100644 index 00000000000..989175ffe97 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathReplacementExtensionPoint.java @@ -0,0 +1,42 @@ +package org.zstack.header.vm.metadata; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public interface VmMetadataPathReplacementExtensionPoint { + String getPrimaryStorageType(); + PathReplacementResult calculatePathReplacements(String targetPsUuid, List allOldPaths); + class PathReplacementResult { + private Map metadataToCurrentPathMap = new HashMap<>(); + private String oldPrefix; + private String newPrefix; + + public PathReplacementResult() { + } + + public Map getMetadataToCurrentPathMap() { + return metadataToCurrentPathMap; + } + + public void setMetadataToCurrentPathMap(Map metadataToCurrentPathMap) { + this.metadataToCurrentPathMap = metadataToCurrentPathMap; + } + + public String getOldPrefix() { + return oldPrefix; + } + + public void setOldPrefix(String oldPrefix) { + this.oldPrefix = oldPrefix; + } + + public String getNewPrefix() { + return newPrefix; + } + + public void setNewPrefix(String newPrefix) { + this.newPrefix = newPrefix; + } + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataResourcePersistExtensionPoint.java b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataResourcePersistExtensionPoint.java new file mode 100644 index 00000000000..bda1d0a09ed --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmMetadataResourcePersistExtensionPoint.java @@ -0,0 +1,11 @@ +package org.zstack.header.vm.metadata; + +import java.sql.Timestamp; + +public interface VmMetadataResourcePersistExtensionPoint { + String getPrimaryStorageType(); + void afterVolumePersist(String primaryStorageUuid, String resourceUuid, + String resourceType, String hostUuid, long size, Timestamp now); + void afterSnapshotPersist(String primaryStorageUuid, String resourceUuid, + String resourceType, String hostUuid, long size, Timestamp now); +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java b/header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java new file mode 100644 index 00000000000..afc0f4f475d --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java @@ -0,0 +1,25 @@ +package org.zstack.header.vm.metadata; + +import java.util.ArrayList; +import java.util.List; + +public interface VmUuidFromApiResolver { + String resolveVmUuid(String fieldValue); + + default List batchResolveVmUuids(List fieldValues) { + List result = new ArrayList<>(); + if (fieldValues == null || fieldValues.isEmpty()) { + return result; + } + for (String v : fieldValues) { + if (v == null) { + continue; + } + String vmUuid = resolveVmUuid(v); + if (vmUuid != null) { + result.add(vmUuid); + } + } + return result; + } +} diff --git a/header/src/main/java/org/zstack/header/vm/metadata/VolumeResourceMetadata.java b/header/src/main/java/org/zstack/header/vm/metadata/VolumeResourceMetadata.java new file mode 100644 index 00000000000..76ff75474e1 --- /dev/null +++ b/header/src/main/java/org/zstack/header/vm/metadata/VolumeResourceMetadata.java @@ -0,0 +1,22 @@ +package org.zstack.header.vm.metadata; + +public class VolumeResourceMetadata extends ResourceMetadata { + private String snapshotReference; + private String snapshotReferenceTree; + + public String getSnapshotReference() { + return snapshotReference; + } + + public void setSnapshotReference(String snapshotReference) { + this.snapshotReference = snapshotReference; + } + + public String getSnapshotReferenceTree() { + return snapshotReferenceTree; + } + + public void setSnapshotReferenceTree(String snapshotReferenceTree) { + this.snapshotReferenceTree = snapshotReferenceTree; + } +} diff --git a/header/src/main/java/org/zstack/header/volume/APIAttachDataVolumeToVmMsg.java b/header/src/main/java/org/zstack/header/volume/APIAttachDataVolumeToVmMsg.java index 6ae817723f4..b01ff703a4c 100755 --- a/header/src/main/java/org/zstack/header/volume/APIAttachDataVolumeToVmMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIAttachDataVolumeToVmMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; import org.zstack.header.vm.VmInstanceVO; +import org.zstack.header.vm.metadata.MetadataImpact; /** * @api api event for message :ref:`APIAttachVolumeToVmEvent` @@ -39,6 +40,7 @@ parameterName = "params", responseClass = APIAttachDataVolumeToVmEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VmUuidDirectResolver", field = "vmInstanceUuid") public class APIAttachDataVolumeToVmMsg extends APIMessage implements VolumeMessage { /** * @desc vm uuid. see :ref:`VmInstanceInventory` diff --git a/header/src/main/java/org/zstack/header/volume/APIChangeVolumeStateMsg.java b/header/src/main/java/org/zstack/header/volume/APIChangeVolumeStateMsg.java index 30478454c67..464436a6345 100755 --- a/header/src/main/java/org/zstack/header/volume/APIChangeVolumeStateMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIChangeVolumeStateMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * @api change data volume state @@ -38,6 +39,7 @@ method = HttpMethod.PUT, responseClass = APIChangeVolumeStateEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VolumeUuidToVmUuidResolver", field = "uuid") public class APIChangeVolumeStateMsg extends APIMessage implements VolumeMessage { /** * @desc data volume uuid diff --git a/header/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotGroupMsg.java b/header/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotGroupMsg.java index 9b497a73fe4..13434495343 100644 --- a/header/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotGroupMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotGroupMsg.java @@ -13,6 +13,7 @@ import org.zstack.header.storage.snapshot.group.VolumeSnapshotGroupVO; import org.zstack.header.vm.VmInstanceInventory; import org.zstack.header.vm.VmInstanceVO; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.ArrayList; import java.util.List; @@ -27,6 +28,7 @@ responseClass = APICreateVolumeSnapshotGroupEvent.class, parameterName = "params" ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "rootVolumeUuid", updateOnFailure = true) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 3) public class APICreateVolumeSnapshotGroupMsg extends APICreateMessage implements VolumeMessage, CreateVolumeSnapshotGroupMessage, APIMultiAuditor { /** diff --git a/header/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotMsg.java b/header/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotMsg.java index 35f57d205f6..ca92ecf5034 100755 --- a/header/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.other.APIAuditor; import org.zstack.header.rest.RestRequest; import org.zstack.header.storage.snapshot.VolumeSnapshotVO; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.concurrent.TimeUnit; @@ -45,6 +46,7 @@ parameterName = "params" ) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 3) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "volumeUuid", updateOnFailure = true) public class APICreateVolumeSnapshotMsg extends APICreateMessage implements VolumeMessage, APIAuditor { /** * @desc volume uuid. See :ref:`VolumeInventory` diff --git a/header/src/main/java/org/zstack/header/volume/APIDeleteDataVolumeMsg.java b/header/src/main/java/org/zstack/header/volume/APIDeleteDataVolumeMsg.java index fdbb1be9847..bde41313f8e 100755 --- a/header/src/main/java/org/zstack/header/volume/APIDeleteDataVolumeMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIDeleteDataVolumeMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIDeleteMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.List; @@ -42,6 +43,7 @@ method = HttpMethod.DELETE, responseClass = APIDeleteDataVolumeEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "uuid") public class APIDeleteDataVolumeMsg extends APIDeleteMessage implements VolumeMessage { /** * @desc data volume uuid diff --git a/header/src/main/java/org/zstack/header/volume/APIDetachDataVolumeFromVmMsg.java b/header/src/main/java/org/zstack/header/volume/APIDetachDataVolumeFromVmMsg.java index 5164ffca076..65374886a04 100755 --- a/header/src/main/java/org/zstack/header/volume/APIDetachDataVolumeFromVmMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIDetachDataVolumeFromVmMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.vm.VmInstanceVO; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.header.rest.RestRequest; /** @@ -36,6 +37,7 @@ method = HttpMethod.DELETE, responseClass = APIDetachDataVolumeFromVmEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "uuid") public class APIDetachDataVolumeFromVmMsg extends APIMessage implements VolumeMessage { /** * @desc data volume uuid. See :ref:`VolumeInventory` diff --git a/header/src/main/java/org/zstack/header/volume/APIExpungeDataVolumeMsg.java b/header/src/main/java/org/zstack/header/volume/APIExpungeDataVolumeMsg.java index 272036c9378..53071f40ae8 100755 --- a/header/src/main/java/org/zstack/header/volume/APIExpungeDataVolumeMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIExpungeDataVolumeMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 11/16/2015. @@ -14,6 +15,7 @@ method = HttpMethod.PUT, responseClass = APIExpungeDataVolumeEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "uuid") public class APIExpungeDataVolumeMsg extends APIMessage implements VolumeMessage { @APIParam(resourceType = VolumeVO.class, successIfResourceNotExisting = true) private String uuid; diff --git a/header/src/main/java/org/zstack/header/volume/APIFlattenVolumeMsg.java b/header/src/main/java/org/zstack/header/volume/APIFlattenVolumeMsg.java index daeb56b44eb..624d62dd540 100644 --- a/header/src/main/java/org/zstack/header/volume/APIFlattenVolumeMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIFlattenVolumeMsg.java @@ -7,6 +7,7 @@ import org.zstack.header.message.DefaultTimeout; import org.zstack.header.other.APIAuditor; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import java.util.concurrent.TimeUnit; @@ -17,6 +18,7 @@ method = HttpMethod.PUT, responseClass = APIFlattenVolumeEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "uuid") public class APIFlattenVolumeMsg extends APIMessage implements VolumeMessage, APIAuditor { @APIParam(resourceType = VolumeVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/volume/APIRecoverDataVolumeMsg.java b/header/src/main/java/org/zstack/header/volume/APIRecoverDataVolumeMsg.java index eec77334a37..11fc833074d 100755 --- a/header/src/main/java/org/zstack/header/volume/APIRecoverDataVolumeMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIRecoverDataVolumeMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 11/12/2015. @@ -14,6 +15,7 @@ method = HttpMethod.PUT, responseClass = APIRecoverDataVolumeEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "uuid") public class APIRecoverDataVolumeMsg extends APIMessage implements VolumeMessage { @APIParam(resourceType = VolumeVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/volume/APISyncVolumeSizeMsg.java b/header/src/main/java/org/zstack/header/volume/APISyncVolumeSizeMsg.java index d4780c73be0..0eb69ce5dc4 100755 --- a/header/src/main/java/org/zstack/header/volume/APISyncVolumeSizeMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APISyncVolumeSizeMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by xing5 on 2016/4/24. @@ -14,6 +15,7 @@ responseClass = APISyncVolumeSizeEvent.class, isAction = true ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VolumeUuidToVmUuidResolver", field = "uuid") public class APISyncVolumeSizeMsg extends APIMessage implements VolumeMessage { @APIParam(resourceType = VolumeVO.class) private String uuid; diff --git a/header/src/main/java/org/zstack/header/volume/APIUndoSnapshotCreationMsg.java b/header/src/main/java/org/zstack/header/volume/APIUndoSnapshotCreationMsg.java index 0d27cfaa18a..6c6b5513525 100644 --- a/header/src/main/java/org/zstack/header/volume/APIUndoSnapshotCreationMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIUndoSnapshotCreationMsg.java @@ -5,6 +5,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; import org.zstack.header.storage.snapshot.VolumeSnapshotVO; +import org.zstack.header.vm.metadata.MetadataImpact; /** * @ Author : yh.w @@ -16,6 +17,7 @@ method = HttpMethod.PUT, responseClass = APIUndoSnapshotCreationEvent.class ) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "uuid", updateOnFailure = true) public class APIUndoSnapshotCreationMsg extends APIMessage implements VolumeMessage { @APIParam(resourceType = VolumeVO.class) diff --git a/header/src/main/java/org/zstack/header/volume/APIUpdateVolumeMsg.java b/header/src/main/java/org/zstack/header/volume/APIUpdateVolumeMsg.java index 785cf824abd..a3d67b10f73 100755 --- a/header/src/main/java/org/zstack/header/volume/APIUpdateVolumeMsg.java +++ b/header/src/main/java/org/zstack/header/volume/APIUpdateVolumeMsg.java @@ -4,6 +4,7 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; /** * Created by frank on 6/14/2015. @@ -14,6 +15,7 @@ responseClass = APIUpdateVolumeEvent.class, isAction = true ) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "VolumeUuidToVmUuidResolver", field = "uuid") public class APIUpdateVolumeMsg extends APIMessage implements VolumeMessage { @APIParam(resourceType = VolumeVO.class) private String uuid; diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/APILocalStorageMigrateVolumeMsg.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/APILocalStorageMigrateVolumeMsg.java index e1864931171..a586da2bf48 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/APILocalStorageMigrateVolumeMsg.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/APILocalStorageMigrateVolumeMsg.java @@ -10,6 +10,7 @@ import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; import org.zstack.header.storage.primary.PrimaryStorageMessage; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.header.storage.primary.PrimaryStorageVO; import org.zstack.header.volume.VolumeVO; @@ -25,6 +26,7 @@ isAction = true ) @DefaultTimeout(timeunit = TimeUnit.HOURS, value = 24) +@MetadataImpact(value = MetadataImpact.Impact.STORAGE, resolver = "VolumeUuidToVmUuidResolver", field = "volumeUuid", updateOnFailure = true) public class APILocalStorageMigrateVolumeMsg extends APIMessage implements PrimaryStorageMessage, APIAuditor { @APIParam(resourceType = VolumeVO.class) private String volumeUuid; diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java index d4665a86a06..4a75220b6d8 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java @@ -39,6 +39,8 @@ import org.zstack.header.storage.primary.VolumeSnapshotCapability.VolumeSnapshotArrangementType; import org.zstack.header.storage.snapshot.*; import org.zstack.header.vm.*; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageMsg; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageReply; import org.zstack.header.vo.ResourceVO; import org.zstack.header.volume.*; import org.zstack.storage.primary.*; @@ -65,7 +67,8 @@ import static org.zstack.core.Platform.*; import static org.zstack.storage.primary.local.LocalStorageUtils.getHostUuidFromInstallUrl; import static org.zstack.utils.CollectionDSL.*; -import static org.zstack.utils.CollectionUtils.*; +import static org.zstack.utils.CollectionUtils.toMap; +import static org.zstack.utils.CollectionUtils.transformAndRemoveNull; /** * Created by frank on 6/30/2015. @@ -902,6 +905,8 @@ public void handleLocalMessage(Message msg) { handle((CommitVolumeSnapshotOnPrimaryStorageMsg) msg); } else if (msg instanceof PullVolumeSnapshotOnPrimaryStorageMsg) { handle((PullVolumeSnapshotOnPrimaryStorageMsg) msg); + } else if (msg instanceof RebaseVolumeBackingFileOnPrimaryStorageMsg) { + handle((RebaseVolumeBackingFileOnPrimaryStorageMsg) msg); } else { super.handleLocalMessage(msg); } @@ -1640,6 +1645,24 @@ public void fail(ErrorCode errorCode) { }); } + protected void handle(RebaseVolumeBackingFileOnPrimaryStorageMsg msg) { + LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()); + LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); + bkd.handle(msg, msg.getHostUuid(), new ReturnValueCompletion(msg) { + @Override + public void success(RebaseVolumeBackingFileOnPrimaryStorageReply returnValue) { + bus.reply(msg, returnValue); + } + + @Override + public void fail(ErrorCode errorCode) { + RebaseVolumeBackingFileOnPrimaryStorageReply reply = new RebaseVolumeBackingFileOnPrimaryStorageReply(); + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + private void handle(RemoveHostFromLocalStorageMsg msg) { RemoveHostFromLocalStorageReply reply = new RemoveHostFromLocalStorageReply(); thdf.chainSubmit(new ChainTask(msg) { @@ -3329,4 +3352,189 @@ public void fail(ErrorCode errorCode) { public static class LocalStoragePhysicalCapacityUsage extends PrimaryStorageBase.PhysicalCapacityUsage { public long localStorageUsedSize; } + + @Override + protected void handle(final UpdateVmInstanceMetadataOnPrimaryStorageMsg msg) { + thdf.chainSubmit(new ChainTask(msg) { + @Override + public String getSyncSignature() { + return "update-metadata-on-ps-" + self.getUuid(); + } + + @Override + public int getSyncLevel() { + return 10; + } + + @Override + public void run(SyncTaskChain chain) { + final String hostUuid; + final LocalStorageHypervisorBackend bkd; + try { + hostUuid = getHostUuidByResourceUuid(msg.getRootVolumeUuid()); + LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); + bkd = f.getHypervisorBackend(self); + } catch (Exception e) { + UpdateVmInstanceMetadataOnPrimaryStorageReply reply = new UpdateVmInstanceMetadataOnPrimaryStorageReply(); + reply.setError(operr("failed to resolve host for vm metadata update on local primary storage[uuid:%s], rootVolumeUuid[%s]: %s", + self.getUuid(), msg.getRootVolumeUuid(), e.getMessage())); + bus.reply(msg, reply); + chain.next(); + return; + } + bkd.handle(msg, hostUuid, new ReturnValueCompletion(msg, chain) { + @Override + public void success(UpdateVmInstanceMetadataOnPrimaryStorageReply returnValue) { + bus.reply(msg, returnValue); + chain.next(); + } + + @Override + public void fail(ErrorCode errorCode) { + UpdateVmInstanceMetadataOnPrimaryStorageReply reply = new UpdateVmInstanceMetadataOnPrimaryStorageReply(); + reply.setError(errorCode); + bus.reply(msg, reply); + chain.next(); + } + }); + } + + @Override + public String getName() { + return "update-metadata-on-ps-" + self.getUuid(); + } + }); + } + + @Override + protected void handle(final GetVmInstanceMetadataFromPrimaryStorageMsg msg) { + GetVmInstanceMetadataFromPrimaryStorageReply reply = new GetVmInstanceMetadataFromPrimaryStorageReply(); + + String hostUuid = null; + if (msg.getHostUuid() != null) { + hostUuid = msg.getHostUuid(); + } + + if (hostUuid == null) { + if (msg.getRootVolumeUuid() == null) { + reply.setError(operr("cannot determine host for vm metadata get on local primary storage[uuid:%s]," + + " rootVolumeUuid is null", self.getUuid())); + bus.reply(msg, reply); + return; + } + try { + hostUuid = getHostUuidByResourceUuid(msg.getRootVolumeUuid()); + } catch (Exception e) { + reply.setError(operr("cannot determine host for vm metadata get on local primary storage[uuid:%s], rootVolumeUuid[%s]: %s", + self.getUuid(), msg.getRootVolumeUuid(), e.getMessage())); + bus.reply(msg, reply); + return; + } + } + + LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); + LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); + bkd.handle(msg, hostUuid, new ReturnValueCompletion(msg) { + @Override + public void success(GetVmInstanceMetadataFromPrimaryStorageReply returnValue) { + bus.reply(msg, returnValue); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + + @Override + protected void handle(final ScanVmInstanceMetadataFromPrimaryStorageMsg msg) { + ScanVmInstanceMetadataFromPrimaryStorageReply reply = new ScanVmInstanceMetadataFromPrimaryStorageReply(); + + List connectedHostUuids = SQL.New( + "select h.hostUuid from LocalStorageHostRefVO h, HostVO host" + + " where h.primaryStorageUuid = :psUuid" + + " and h.hostUuid = host.uuid" + + " and host.status = :hstatus", String.class) + .param("psUuid", self.getUuid()) + .param("hstatus", HostStatus.Connected) + .list(); + if (connectedHostUuids.isEmpty()) { + reply.setError(operr("no connected host found for local primary storage[uuid:%s]", self.getUuid())); + bus.reply(msg, reply); + return; + } + + List allSummaries = Collections.synchronizedList(new ArrayList<>()); + + new While<>(connectedHostUuids).all((hostUuid, com) -> { + LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); + LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); + bkd.handle(msg, hostUuid, new ReturnValueCompletion(com) { + @Override + public void success(ScanVmInstanceMetadataFromPrimaryStorageReply returnValue) { + if (returnValue.getVmInstanceMetadata() != null) { + allSummaries.addAll(returnValue.getVmInstanceMetadata()); + } + com.done(); + } + + @Override + public void fail(ErrorCode errorCode) { + logger.warn(String.format("failed to scan vm metadata from host[uuid:%s] on local primary storage[uuid:%s]: %s", + hostUuid, self.getUuid(), errorCode)); + com.addError(errorCode); + com.done(); + } + }); + }).run(new WhileDoneCompletion(msg) { + @Override + public void done(ErrorCodeList errorCodeList) { + if (!errorCodeList.getCauses().isEmpty() && errorCodeList.getCauses().size() == connectedHostUuids.size()) { + reply.setError(operr("failed to scan vm metadata from all hosts on local primary storage[uuid:%s], causes: %s", + self.getUuid(), errorCodeList)); + } else { + reply.setVmInstanceMetadata(new ArrayList<>(allSummaries)); + } + bus.reply(msg, reply); + } + }); + } + + @Override + protected void handle(final CleanupVmInstanceMetadataOnPrimaryStorageMsg msg) { + CleanupVmInstanceMetadataOnPrimaryStorageReply reply = new CleanupVmInstanceMetadataOnPrimaryStorageReply(); + + String hostUuid = msg.getHostUuid(); + if (hostUuid == null && msg.getRootVolumeUuid() != null) { + try { + hostUuid = getHostUuidByResourceUuid(msg.getRootVolumeUuid()); + } catch (Exception e) { + logger.warn(String.format("failed to get host by rootVolumeUuid[%s]: %s", msg.getRootVolumeUuid(), e.getMessage())); + } + } + + if (hostUuid == null) { + reply.setError(operr("cannot determine host for vm metadata cleanup on local primary storage[uuid:%s]," + + " rootVolumeUuid[%s], hostUuid is null", self.getUuid(), msg.getRootVolumeUuid())); + bus.reply(msg, reply); + return; + } + + LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); + LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); + bkd.handle(msg, hostUuid, new ReturnValueCompletion(msg) { + @Override + public void success(CleanupVmInstanceMetadataOnPrimaryStorageReply returnValue) { + bus.reply(msg, returnValue); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } } diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.java index 7760e28de93..d8226d932b2 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.java @@ -7,6 +7,8 @@ import org.zstack.header.image.ImageInventory; import org.zstack.header.storage.primary.*; import org.zstack.header.storage.snapshot.VolumeSnapshotInventory; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageMsg; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageReply; import org.zstack.header.volume.*; import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageMsg; import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageReply; @@ -121,4 +123,14 @@ public LocalStorageHypervisorBackend(PrimaryStorageVO self) { abstract void handle(CommitVolumeSnapshotOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); abstract void handle(PullVolumeSnapshotOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + abstract void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + abstract void handle(GetVmInstanceMetadataFromPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + abstract void handle(ScanVmInstanceMetadataFromPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + abstract void handle(CleanupVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + abstract void handle(RebaseVolumeBackingFileOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); } diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java index e8d268e518a..0346a8bfe4f 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java @@ -42,11 +42,15 @@ import org.zstack.header.rest.RESTFacade; import org.zstack.header.storage.backup.*; import org.zstack.header.storage.primary.*; -import org.zstack.header.storage.snapshot.*; +import org.zstack.header.storage.snapshot.VolumeSnapshotConstant; +import org.zstack.header.storage.snapshot.VolumeSnapshotInventory; +import org.zstack.header.storage.snapshot.VolumeSnapshotVO; import org.zstack.header.vm.VmInstanceSpec.ImageSpec; import org.zstack.header.vm.VmInstanceState; import org.zstack.header.vm.VmInstanceVO; import org.zstack.header.vm.VmInstanceVO_; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageMsg; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageReply; import org.zstack.header.volume.*; import org.zstack.identity.AccountManager; import org.zstack.kvm.*; @@ -67,9 +71,7 @@ import java.util.*; import java.util.stream.Collectors; -import static org.zstack.core.Platform.inerr; -import static org.zstack.core.Platform.multiErr; -import static org.zstack.core.Platform.operr; +import static org.zstack.core.Platform.*; import static org.zstack.utils.CollectionDSL.list; import static org.zstack.utils.CollectionUtils.transformAndRemoveNull; @@ -903,6 +905,52 @@ public void setHashValue(String hashValue) { } } + public static class WriteVmMetadataCmd extends AgentCommand { + public String metadata; + public String metadataPath; + public String vmUuid; + public String vmName; + public String vmCategory; + public String architecture; + public String schemaVersion; + } + + public static class WriteVmMetadataRsp extends AgentResponse { + } + + public static class GetVmInstanceMetadataCmd extends AgentCommand { + public String metadataPath; + } + + public static class GetVmInstanceMetadataRsp extends AgentResponse { + public String metadata; + } + + public static class ScanVmMetadataCmd extends AgentCommand { + public String metadataDir; + } + + public static class ScanVmMetadataRsp extends AgentResponse { + public List metadataEntries = new ArrayList<>(); + } + + public static class CleanupVmMetadataCmd extends AgentCommand { + public String metadataPath; + } + + public static class CleanupVmMetadataRsp extends AgentResponse { + } + + public static class PrefixRebaseBackingFilesCmd extends LocalStorageKvmBackend.AgentCommand { + public List filePaths; + public String oldPrefix; + public String newPrefix; + } + + public static class PrefixRebaseBackingFilesRsp extends LocalStorageKvmBackend.AgentResponse { + public int rebasedCount; + } + public static final String INIT_PATH = "/localstorage/init"; public static final String GET_PHYSICAL_CAPACITY_PATH = "/localstorage/getphysicalcapacity"; public static final String CREATE_EMPTY_VOLUME_PATH = "/localstorage/volume/createempty"; @@ -935,6 +983,11 @@ public void setHashValue(String hashValue) { public static final String CANCEL_DOWNLOAD_BITS_FROM_KVM_HOST_PATH = "/localstorage/kvmhost/download/cancel"; public static final String GET_DOWNLOAD_BITS_FROM_KVM_HOST_PROGRESS_PATH = "/localstorage/kvmhost/download/progress"; public static final String GET_QCOW2_HASH_VALUE_PATH = "/localstorage/getqcow2hash"; + public static final String WRITE_VM_METADATA_PATH = "/localstorage/vm/metadata/write"; + public static final String GET_VM_INSTANCE_METADATA_PATH = "/localstorage/vm/metadata/get"; + public static final String SCAN_VM_METADATA_PATH = "/localstorage/vm/metadata/scan"; + public static final String CLEANUP_VM_METADATA_PATH = "/localstorage/vm/metadata/cleanup"; + public static final String PREFIX_REBASE_BACKING_FILES_PATH = "/localstorage/snapshot/prefixrebasebackingfiles"; public LocalStorageKvmBackend() { } @@ -3797,4 +3850,114 @@ public void fail(ErrorCode errorCode) { } }); } + + @Override + void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + WriteVmMetadataCmd cmd = new WriteVmMetadataCmd(); + cmd.metadata = msg.getMetadata(); + cmd.metadataPath = msg.getMetadataPath(); + cmd.vmUuid = msg.getVmInstanceUuid(); + cmd.vmName = msg.getVmInstanceName(); + cmd.vmCategory = msg.getVmCategory(); + cmd.architecture = msg.getArchitecture(); + cmd.schemaVersion = msg.getSchemaVersion(); + + httpCall(WRITE_VM_METADATA_PATH, hostUuid, cmd, WriteVmMetadataRsp.class, new ReturnValueCompletion(completion) { + @Override + public void success(WriteVmMetadataRsp rsp) { + completion.success(new UpdateVmInstanceMetadataOnPrimaryStorageReply()); + } + + @Override + public void fail(ErrorCode errorCode) { + completion.fail(errorCode); + } + }); + } + + @Override + void handle(GetVmInstanceMetadataFromPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + GetVmInstanceMetadataCmd cmd = new GetVmInstanceMetadataCmd(); + cmd.metadataPath = msg.getMetadataPath(); + + httpCall(GET_VM_INSTANCE_METADATA_PATH, hostUuid, cmd, GetVmInstanceMetadataRsp.class, new ReturnValueCompletion(completion) { + @Override + public void success(GetVmInstanceMetadataRsp rsp) { + GetVmInstanceMetadataFromPrimaryStorageReply reply = new GetVmInstanceMetadataFromPrimaryStorageReply(); + reply.setMetadata(rsp.metadata); + completion.success(reply); + } + + @Override + public void fail(ErrorCode errorCode) { + completion.fail(errorCode); + } + }); + } + + @Override + void handle(ScanVmInstanceMetadataFromPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + ScanVmMetadataCmd cmd = new ScanVmMetadataCmd(); + cmd.metadataDir = msg.getMetadataDir(); + + httpCall(SCAN_VM_METADATA_PATH, hostUuid, cmd, ScanVmMetadataRsp.class, new ReturnValueCompletion(completion) { + @Override + public void success(ScanVmMetadataRsp rsp) { + ScanVmInstanceMetadataFromPrimaryStorageReply reply = new ScanVmInstanceMetadataFromPrimaryStorageReply(); + if (rsp.metadataEntries != null) { + rsp.metadataEntries.forEach(entry -> entry.setHostUuid(hostUuid)); + reply.setVmInstanceMetadata(rsp.metadataEntries); + } else { + reply.setVmInstanceMetadata(Collections.emptyList()); + } + completion.success(reply); + } + + @Override + public void fail(ErrorCode errorCode) { + completion.fail(errorCode); + } + }); + } + + @Override + void handle(CleanupVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + CleanupVmMetadataCmd cmd = new CleanupVmMetadataCmd(); + cmd.metadataPath = msg.getMetadataPath(); + + httpCall(CLEANUP_VM_METADATA_PATH, hostUuid, cmd, CleanupVmMetadataRsp.class, new ReturnValueCompletion(completion) { + @Override + public void success(CleanupVmMetadataRsp rsp) { + CleanupVmInstanceMetadataOnPrimaryStorageReply reply = new CleanupVmInstanceMetadataOnPrimaryStorageReply(); + completion.success(reply); + } + + @Override + public void fail(ErrorCode errorCode) { + completion.fail(errorCode); + } + }); + } + + @Override + void handle(RebaseVolumeBackingFileOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + PrefixRebaseBackingFilesCmd cmd = new PrefixRebaseBackingFilesCmd(); + cmd.filePaths = msg.getInstallPaths(); + cmd.oldPrefix = msg.getOldPrefix(); + cmd.newPrefix = msg.getNewPrefix(); + + httpCall(PREFIX_REBASE_BACKING_FILES_PATH, hostUuid, cmd, PrefixRebaseBackingFilesRsp.class, new ReturnValueCompletion(completion) { + @Override + public void success(PrefixRebaseBackingFilesRsp rsp) { + RebaseVolumeBackingFileOnPrimaryStorageReply reply = new RebaseVolumeBackingFileOnPrimaryStorageReply(); + reply.setRebasedCount(rsp.rebasedCount); + completion.success(reply); + } + + @Override + public void fail(ErrorCode errorCode) { + completion.fail(errorCode); + } + }); + } } diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulator.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulator.java index dbe774888f0..3edb2e916db 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulator.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulator.java @@ -334,6 +334,44 @@ String offlineMerge(HttpEntity entity) { return null; } + @RequestMapping(value=LocalStorageKvmBackend.WRITE_VM_METADATA_PATH, method= RequestMethod.POST) + public @ResponseBody + String writeVmMetadata(HttpEntity entity) { + WriteVmMetadataCmd cmd = JSONObjectUtil.toObject(entity.getBody(), WriteVmMetadataCmd.class); + config.writeVmMetadataCmds.add(cmd); + reply(entity, new WriteVmMetadataRsp()); + return null; + } + + @RequestMapping(value=LocalStorageKvmBackend.GET_VM_INSTANCE_METADATA_PATH, method= RequestMethod.POST) + public @ResponseBody + String getVmInstanceMetadata(HttpEntity entity) { + GetVmInstanceMetadataCmd cmd = JSONObjectUtil.toObject(entity.getBody(), GetVmInstanceMetadataCmd.class); + config.getVmInstanceMetadataCmds.add(cmd); + GetVmInstanceMetadataRsp rsp = new GetVmInstanceMetadataRsp(); + reply(entity, rsp); + return null; + } + + @RequestMapping(value=LocalStorageKvmBackend.SCAN_VM_METADATA_PATH, method= RequestMethod.POST) + public @ResponseBody + String scanVmMetadata(HttpEntity entity) { + ScanVmMetadataCmd cmd = JSONObjectUtil.toObject(entity.getBody(), ScanVmMetadataCmd.class); + config.scanVmMetadataCmds.add(cmd); + ScanVmMetadataRsp rsp = new ScanVmMetadataRsp(); + reply(entity, rsp); + return null; + } + + @RequestMapping(value=LocalStorageKvmBackend.CLEANUP_VM_METADATA_PATH, method= RequestMethod.POST) + public @ResponseBody + String cleanupVmMetadata(HttpEntity entity) { + CleanupVmMetadataCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CleanupVmMetadataCmd.class); + config.cleanupVmMetadataCmds.add(cmd); + reply(entity, new CleanupVmMetadataRsp()); + return null; + } + @ExceptionHandler(Exception.class) public ModelAndView handleAllException(Exception ex) { logger.warn(ex.getMessage(), ex); diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulatorConfig.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulatorConfig.java index 53a02339833..34463b46c6e 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulatorConfig.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulatorConfig.java @@ -59,4 +59,9 @@ public static class Capacity { public List getQCOW2ReferenceCmds = new ArrayList<>(); public List getQCOW2ReferenceCmdReference = new ArrayList<>(); + + public List writeVmMetadataCmds = new ArrayList<>(); + public List getVmInstanceMetadataCmds = new ArrayList<>(); + public List scanVmMetadataCmds = new ArrayList<>(); + public List cleanupVmMetadataCmds = new ArrayList<>(); } diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.java new file mode 100644 index 00000000000..d721d144b88 --- /dev/null +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.java @@ -0,0 +1,146 @@ +package org.zstack.storage.primary.local; + +import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; +import org.zstack.core.db.DatabaseFacade; +import org.zstack.core.db.Q; +import org.zstack.header.exception.CloudRuntimeException; +import org.zstack.header.storage.primary.PrimaryStorageVO; +import org.zstack.header.storage.primary.PrimaryStorageVO_; +import org.zstack.header.vm.metadata.VmInstanceMetadataConstants; +import org.zstack.header.vm.metadata.VmMetadataPathBuildExtensionPoint; +import org.zstack.header.vm.metadata.VmMetadataPathReplacementExtensionPoint; +import org.zstack.header.vm.metadata.VmMetadataResourcePersistExtensionPoint; +import org.zstack.utils.Utils; +import org.zstack.utils.logging.CLogger; + +import java.sql.Timestamp; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) +public class LocalStorageVmMetadataExtension implements VmMetadataPathBuildExtensionPoint, + VmMetadataPathReplacementExtensionPoint, VmMetadataResourcePersistExtensionPoint { + private static final CLogger logger = Utils.getLogger(LocalStorageVmMetadataExtension.class); + + @Autowired + private DatabaseFacade dbf; + + @Override + public String getPrimaryStorageType() { + return LocalStorageConstants.LOCAL_STORAGE_TYPE; + } + + @Override + public String buildMetadataDir(String primaryStorageUuid) { + String url = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(PrimaryStorageVO_.uuid, primaryStorageUuid).findValue(); + if (url == null) { + throw new CloudRuntimeException(String.format("cannot find url for primary storage[uuid:%s]", primaryStorageUuid)); + } + return String.format("%s/%s", normalizeBaseDir(url), VmInstanceMetadataConstants.METADATA_DIR_NAME); + } + + @Override + public String buildVmMetadataPath(String primaryStorageUuid, String vmInstanceUuid) { + String url = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(PrimaryStorageVO_.uuid, primaryStorageUuid).findValue(); + if (url == null) { + throw new CloudRuntimeException(String.format("cannot find url for primary storage[uuid:%s]", primaryStorageUuid)); + } + return String.format("%s/%s/%s%s", normalizeBaseDir(url), VmInstanceMetadataConstants.METADATA_DIR_NAME, vmInstanceUuid, VmInstanceMetadataConstants.FILE_METADATA_SUFFIX); + } + + @Override + public PathReplacementResult calculatePathReplacements(String targetPsUuid, List allOldPaths) { + if (allOldPaths == null || allOldPaths.isEmpty()) { + PathReplacementResult result = new PathReplacementResult(); + result.setMetadataToCurrentPathMap(Collections.emptyMap()); + return result; + } + + String baseDir = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(PrimaryStorageVO_.uuid, targetPsUuid).findValue(); + if (baseDir == null) { + logger.warn(String.format("LocalStorage PS[uuid:%s] has no url, path replacement disabled", targetPsUuid)); + PathReplacementResult result = new PathReplacementResult(); + result.setMetadataToCurrentPathMap(Collections.emptyMap()); + return result; + } + String newPrefix = normalizeBaseDir(baseDir) + "/"; + + // Extract old prefix from the first recognizable path + String oldPrefix = null; + for (String path : allOldPaths) { + oldPrefix = VmInstanceMetadataConstants.extractOldPrefix(path); + if (oldPrefix != null) break; + } + + Map pathMap = new LinkedHashMap<>(); + if (oldPrefix != null) { + for (String oldPath : allOldPaths) { + if (oldPath != null && oldPath.startsWith(oldPrefix)) { + pathMap.put(oldPath, newPrefix + oldPath.substring(oldPrefix.length())); + } + } + } else { + logger.warn(String.format("cannot extract old path prefix from any path for LocalStorage PS[uuid:%s], " + + "path replacement disabled", targetPsUuid)); + } + + PathReplacementResult result = new PathReplacementResult(); + result.setMetadataToCurrentPathMap(pathMap); + result.setOldPrefix(oldPrefix); + result.setNewPrefix(newPrefix); + return result; + } + + @Override + public String validateMetadataPath(String primaryStorageUuid, String path) { + // Expected format: /.vmmeta + // e.g. /local_ps/vm-metadata/a1b2c3d4e5f6...vmmeta + String metadataDir = buildMetadataDir(primaryStorageUuid); + String prefix = metadataDir + "/"; + String suffix = VmInstanceMetadataConstants.FILE_METADATA_SUFFIX; + + if (!path.startsWith(prefix) || !path.endsWith(suffix)) { + return String.format("metadataPath[%s] does not match expected format: %s/%s", + path, metadataDir, suffix); + } + + String uuidPart = path.substring(prefix.length(), path.length() - suffix.length()); + if (!uuidPart.matches("[0-9a-f]{32}")) { + return String.format("metadataPath[%s] contains invalid uuid[%s], expected 32-char hex", path, uuidPart); + } + return null; + } + + private String normalizeBaseDir(String url) { + return url.endsWith("/") ? url.substring(0, url.length() - 1) : url; + } + + @Override + public void afterVolumePersist(String primaryStorageUuid, String resourceUuid, + String resourceType, String hostUuid, long size, Timestamp now) { + createResourceRef(primaryStorageUuid, resourceUuid, resourceType, hostUuid, size, now); + } + + @Override + public void afterSnapshotPersist(String primaryStorageUuid, String resourceUuid, + String resourceType, String hostUuid, long size, Timestamp now) { + createResourceRef(primaryStorageUuid, resourceUuid, resourceType, hostUuid, size, now); + } + + private void createResourceRef(String primaryStorageUuid, String resourceUuid, + String resourceType, String hostUuid, long size, Timestamp now) { + LocalStorageResourceRefVO ref = new LocalStorageResourceRefVO(); + ref.setPrimaryStorageUuid(primaryStorageUuid); + ref.setResourceUuid(resourceUuid); + ref.setResourceType(resourceType); + ref.setHostUuid(hostUuid); + ref.setSize(size); + ref.setCreateDate(now); + ref.setLastOpDate(now); + dbf.persist(ref); + } +} diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java index abe9ac152b6..b63e1d20597 100755 --- a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java @@ -36,7 +36,6 @@ import org.zstack.header.storage.backup.BackupStorageVO; import org.zstack.header.storage.primary.*; import org.zstack.header.storage.primary.VolumeSnapshotCapability.VolumeSnapshotArrangementType; -import org.zstack.header.storage.snapshot.DeleteVolumeSnapshotDirection; import org.zstack.header.storage.snapshot.ShrinkVolumeSnapshotOnPrimaryStorageMsg; import org.zstack.header.storage.snapshot.VolumeSnapshotConstant; import org.zstack.header.storage.snapshot.VolumeSnapshotInventory; @@ -44,23 +43,27 @@ import org.zstack.header.vm.VmInstanceState; import org.zstack.header.vm.VmInstanceVO; import org.zstack.header.vm.VmInstanceVO_; +import org.zstack.header.vm.metadata.*; import org.zstack.header.volume.*; -import org.zstack.kvm.*; -import org.zstack.storage.primary.*; +import org.zstack.kvm.KVMConstant; +import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageMsg; +import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageReply; +import org.zstack.storage.primary.PrimaryStorageBase; +import org.zstack.storage.primary.PrimaryStorageCapacityUpdater; import org.zstack.storage.snapshot.reference.VolumeSnapshotReferenceUtils; import org.zstack.storage.volume.VolumeErrors; import org.zstack.storage.volume.VolumeSystemTags; import org.zstack.tag.SystemTagCreator; -import org.zstack.utils.CollectionUtils; import org.zstack.utils.DebugUtils; import org.zstack.utils.Utils; -import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import java.io.File; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -131,6 +134,8 @@ protected void handleLocalMessage(Message msg) { handle((CommitVolumeSnapshotOnPrimaryStorageMsg) msg); } else if (msg instanceof PullVolumeSnapshotOnPrimaryStorageMsg) { handle((PullVolumeSnapshotOnPrimaryStorageMsg) msg); + } else if (msg instanceof RebaseVolumeBackingFileOnPrimaryStorageMsg) { + handle((RebaseVolumeBackingFileOnPrimaryStorageMsg) msg); } else { super.handleLocalMessage(msg); } @@ -1924,4 +1929,153 @@ private String getHostUuidFromVolume(String volumeUuid) { return hostUuid; } + + @Override + protected void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg) { + thdf.chainSubmit(new ChainTask(msg) { + @Override + public String getSyncSignature() { + return "update-metadata-on-ps-" + self.getUuid(); + } + + @Override + public int getSyncLevel() { + return 10; + } + + @Override + public void run(SyncTaskChain chain) { + UpdateVmInstanceMetadataOnPrimaryStorageReply reply = new UpdateVmInstanceMetadataOnPrimaryStorageReply(); + + String hostUuid = getHostUuidFromVolume(msg.getRootVolumeUuid()); + if (hostUuid == null || hostUuid.isEmpty()) { + reply.setError(operr("no host found for volume[uuid:%s]", msg.getRootVolumeUuid())); + bus.reply(msg, reply); + chain.next(); + return; + } + + final NfsPrimaryStorageBackend backend = getBackendByHostUuid(hostUuid); + backend.handle(msg, hostUuid, new ReturnValueCompletion(msg, chain) { + @Override + public void success(UpdateVmInstanceMetadataOnPrimaryStorageReply r) { + bus.reply(msg, r); + chain.next(); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(operr("failed to update vm metadata on NFS primary storage[uuid:%s]", self.getUuid())); + bus.reply(msg, reply); + chain.next(); + } + }); + } + + @Override + public String getName() { + return "update-metadata-on-ps-" + self.getUuid(); + } + }); + } + + @Override + protected void handle(GetVmInstanceMetadataFromPrimaryStorageMsg msg) { + GetVmInstanceMetadataFromPrimaryStorageReply reply = new GetVmInstanceMetadataFromPrimaryStorageReply(); + List connectedHosts = factory.getConnectedHostForOperation(getSelfInventory()); + if (connectedHosts.isEmpty()) { + reply.setError(operr("no connected host found for NFS primary storage[uuid:%s]", self.getUuid())); + bus.reply(msg, reply); + return; + } + String hostUuid = connectedHosts.get(0).getUuid(); + final NfsPrimaryStorageBackend backend = getBackendByHostUuid(hostUuid); + backend.handle(msg, hostUuid, new ReturnValueCompletion(msg) { + @Override + public void success(GetVmInstanceMetadataFromPrimaryStorageReply r) { + bus.reply(msg, r); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + + @Override + protected void handle(ScanVmInstanceMetadataFromPrimaryStorageMsg msg) { + ScanVmInstanceMetadataFromPrimaryStorageReply reply = new ScanVmInstanceMetadataFromPrimaryStorageReply(); + List connectedHosts = factory.getConnectedHostForOperation(getSelfInventory()); + if (connectedHosts.isEmpty()) { + reply.setError(operr("no connected host found for NFS primary storage[uuid:%s]", self.getUuid())); + bus.reply(msg, reply); + return; + } + String hostUuid = connectedHosts.get(0).getUuid(); + final NfsPrimaryStorageBackend backend = getBackendByHostUuid(hostUuid); + backend.handle(msg, hostUuid, new ReturnValueCompletion(msg) { + @Override + public void success(ScanVmInstanceMetadataFromPrimaryStorageReply r) { + bus.reply(msg, r); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + + @Override + protected void handle(CleanupVmInstanceMetadataOnPrimaryStorageMsg msg) { + CleanupVmInstanceMetadataOnPrimaryStorageReply reply = new CleanupVmInstanceMetadataOnPrimaryStorageReply(); + List connectedHosts = factory.getConnectedHostForOperation(getSelfInventory()); + if (connectedHosts.isEmpty()) { + reply.setError(operr("no connected host found for NFS primary storage[uuid:%s]", self.getUuid())); + bus.reply(msg, reply); + return; + } + String hostUuid = connectedHosts.get(0).getUuid(); + final NfsPrimaryStorageBackend backend = getBackendByHostUuid(hostUuid); + backend.handle(msg, hostUuid, new ReturnValueCompletion(msg) { + @Override + public void success(CleanupVmInstanceMetadataOnPrimaryStorageReply r) { + bus.reply(msg, r); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + + @Override + protected void handle(RebaseVolumeBackingFileOnPrimaryStorageMsg msg) { + RebaseVolumeBackingFileOnPrimaryStorageReply reply = new RebaseVolumeBackingFileOnPrimaryStorageReply(); + List connectedHosts = factory.getConnectedHostForOperation(getSelfInventory()); + if (connectedHosts.isEmpty()) { + reply.setError(operr("no connected host found for NFS primary storage[uuid:%s]", self.getUuid())); + bus.reply(msg, reply); + return; + } + String hostUuid = connectedHosts.get(0).getUuid(); + final NfsPrimaryStorageBackend backend = getBackendByHostUuid(hostUuid); + backend.handle(msg, hostUuid, new ReturnValueCompletion(msg) { + @Override + public void success(RebaseVolumeBackingFileOnPrimaryStorageReply r) { + bus.reply(msg, r); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } } diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java index 459023d7c17..35a240a6221 100755 --- a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java @@ -7,6 +7,8 @@ import org.zstack.header.image.ImageInventory; import org.zstack.header.storage.primary.*; import org.zstack.header.storage.snapshot.VolumeSnapshotInventory; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageMsg; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageReply; import org.zstack.header.volume.VolumeStats; import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageMsg; import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageReply; @@ -91,6 +93,16 @@ public interface NfsPrimaryStorageBackend { void updateMountPoint(PrimaryStorageInventory pinv, String clusterUuid, String oldMountPoint, String newMountPoint, Completion completion); + void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + void handle(GetVmInstanceMetadataFromPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + void handle(ScanVmInstanceMetadataFromPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + void handle(CleanupVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + void handle(RebaseVolumeBackingFileOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + class BitsInfo { private String installPath; private long size; diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java index 93d3d7aab99..8d979dd94bb 100755 --- a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java @@ -21,7 +21,9 @@ import org.zstack.core.timeout.ApiTimeoutManager; import org.zstack.core.trash.StorageTrash; import org.zstack.header.core.*; -import org.zstack.header.core.workflow.*; +import org.zstack.header.core.workflow.Flow; +import org.zstack.header.core.workflow.FlowTrigger; +import org.zstack.header.core.workflow.NoRollbackFlow; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.errorcode.ErrorCodeList; import org.zstack.header.errorcode.OperationFailureException; @@ -39,6 +41,8 @@ import org.zstack.header.vm.VmInstanceState; import org.zstack.header.vm.VmInstanceVO; import org.zstack.header.vm.VmInstanceVO_; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageMsg; +import org.zstack.header.vm.metadata.UpdateVmInstanceMetadataOnPrimaryStorageReply; import org.zstack.header.volume.*; import org.zstack.identity.AccountManager; import org.zstack.kvm.*; @@ -129,7 +133,12 @@ public class NfsPrimaryStorageKVMBackend implements NfsPrimaryStorageBackend, public static final String GET_DOWNLOAD_BITS_FROM_KVM_HOST_PROGRESS_PATH = "/nfsprimarystorage/kvmhost/download/progress"; public static final String CREATE_VOLUME_FROM_TEMPLATE_PATH = "/nfsprimarystorage/sftp/createvolumefromtemplate"; public static final String GET_QCOW2_HASH_VALUE_PATH = "/nfsprimarystorage/getqcow2hash"; + public static final String WRITE_VM_METADATA_PATH = "/nfsprimarystorage/vm/metadata/write"; + public static final String GET_VM_INSTANCE_METADATA_PATH = "/nfsprimarystorage/vm/metadata/get"; + public static final String SCAN_VM_METADATA_PATH = "/nfsprimarystorage/vm/metadata/scan"; + public static final String CLEANUP_VM_METADATA_PATH = "/nfsprimarystorage/vm/metadata/cleanup"; + public static final String NFS_PREFIX_REBASE_BACKING_FILES_PATH = "/nfsprimarystorage/snapshot/prefixrebasebackingfiles"; //////////////// For unit test ////////////////////////// private boolean syncGetCapacity = false; @@ -2051,4 +2060,174 @@ public void run(MessageReply r) { } }); } + + @Override + public void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + WriteVmMetadataCmd cmd = new WriteVmMetadataCmd(); + cmd.setUuid(msg.getPrimaryStorageUuid()); + cmd.metadata = msg.getMetadata(); + cmd.metadataPath = msg.getMetadataPath(); + cmd.vmUuid = msg.getVmInstanceUuid(); + cmd.vmName = msg.getVmInstanceName(); + cmd.vmCategory = msg.getVmCategory(); + cmd.architecture = msg.getArchitecture(); + cmd.schemaVersion = msg.getSchemaVersion(); + + KVMHostAsyncHttpCallMsg hmsg = new KVMHostAsyncHttpCallMsg(); + hmsg.setCommand(cmd); + hmsg.setPath(WRITE_VM_METADATA_PATH); + hmsg.setHostUuid(hostUuid); + bus.makeTargetServiceIdByResourceUuid(hmsg, HostConstant.SERVICE_ID, hostUuid); + bus.send(hmsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + completion.fail(reply.getError()); + return; + } + + WriteVmMetadataRsp rsp = ((KVMHostAsyncHttpCallReply) reply).toResponse(WriteVmMetadataRsp.class); + if (!rsp.isSuccess()) { + completion.fail(operr("failed to write vm metadata on nfs via host[uuid:%s]: %s", hostUuid, rsp.getError())); + return; + } + + completion.success(new UpdateVmInstanceMetadataOnPrimaryStorageReply()); + } + }); + } + + @Override + public void handle(GetVmInstanceMetadataFromPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + NfsPrimaryStorageKVMBackendCommands.GetVmInstanceMetadataCmd cmd = new NfsPrimaryStorageKVMBackendCommands.GetVmInstanceMetadataCmd(); + cmd.setUuid(msg.getPrimaryStorageUuid()); + cmd.metadataPath = msg.getMetadataPath(); + + KVMHostAsyncHttpCallMsg hmsg = new KVMHostAsyncHttpCallMsg(); + hmsg.setCommand(cmd); + hmsg.setPath(GET_VM_INSTANCE_METADATA_PATH); + hmsg.setHostUuid(hostUuid); + bus.makeTargetServiceIdByResourceUuid(hmsg, HostConstant.SERVICE_ID, hostUuid); + bus.send(hmsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + completion.fail(reply.getError()); + return; + } + + GetVmInstanceMetadataRsp rsp = ((KVMHostAsyncHttpCallReply) reply).toResponse(GetVmInstanceMetadataRsp.class); + if (!rsp.isSuccess()) { + completion.fail(operr("failed to get vm instance metadata on nfs via host[uuid:%s]: %s", hostUuid, rsp.getError())); + return; + } + + GetVmInstanceMetadataFromPrimaryStorageReply r = new GetVmInstanceMetadataFromPrimaryStorageReply(); + r.setMetadata(rsp.metadata); + completion.success(r); + } + }); + } + + @Override + public void handle(ScanVmInstanceMetadataFromPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + NfsPrimaryStorageKVMBackendCommands.ScanVmMetadataCmd cmd = new NfsPrimaryStorageKVMBackendCommands.ScanVmMetadataCmd(); + cmd.setUuid(msg.getPrimaryStorageUuid()); + cmd.metadataDir = msg.getMetadataDir(); + + KVMHostAsyncHttpCallMsg hmsg = new KVMHostAsyncHttpCallMsg(); + hmsg.setCommand(cmd); + hmsg.setPath(SCAN_VM_METADATA_PATH); + hmsg.setHostUuid(hostUuid); + bus.makeTargetServiceIdByResourceUuid(hmsg, HostConstant.SERVICE_ID, hostUuid); + bus.send(hmsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + completion.fail(reply.getError()); + return; + } + + NfsPrimaryStorageKVMBackendCommands.ScanVmMetadataRsp rsp = ((KVMHostAsyncHttpCallReply) reply).toResponse(NfsPrimaryStorageKVMBackendCommands.ScanVmMetadataRsp.class); + if (!rsp.isSuccess()) { + completion.fail(operr("failed to scan vm instance metadata on nfs via host[uuid:%s]: %s", hostUuid, rsp.getError())); + return; + } + + ScanVmInstanceMetadataFromPrimaryStorageReply r = new ScanVmInstanceMetadataFromPrimaryStorageReply(); + if (rsp.metadataEntries != null) { + rsp.metadataEntries.forEach(entry -> entry.setHostUuid(hostUuid)); + r.setVmInstanceMetadata(rsp.metadataEntries); + } + completion.success(r); + } + }); + } + + @Override + public void handle(CleanupVmInstanceMetadataOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + CleanupVmMetadataCmd cmd = new CleanupVmMetadataCmd(); + cmd.setUuid(msg.getPrimaryStorageUuid()); + cmd.metadataPath = msg.getMetadataPath(); + + KVMHostAsyncHttpCallMsg hmsg = new KVMHostAsyncHttpCallMsg(); + hmsg.setCommand(cmd); + hmsg.setPath(CLEANUP_VM_METADATA_PATH); + hmsg.setHostUuid(hostUuid); + bus.makeTargetServiceIdByResourceUuid(hmsg, HostConstant.SERVICE_ID, hostUuid); + bus.send(hmsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + completion.fail(reply.getError()); + return; + } + + CleanupVmMetadataRsp rsp = ((KVMHostAsyncHttpCallReply) reply).toResponse(CleanupVmMetadataRsp.class); + if (!rsp.isSuccess()) { + completion.fail(operr("failed to cleanup vm metadata on nfs via host[uuid:%s]: %s", hostUuid, rsp.getError())); + return; + } + + CleanupVmInstanceMetadataOnPrimaryStorageReply r = new CleanupVmInstanceMetadataOnPrimaryStorageReply(); + completion.success(r); + } + }); + } + + @Override + public void handle(RebaseVolumeBackingFileOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + NfsPrimaryStorageKVMBackendCommands.PrefixRebaseBackingFilesCmd cmd = new NfsPrimaryStorageKVMBackendCommands.PrefixRebaseBackingFilesCmd(); + // NFS installPaths are already filesystem paths, no conversion needed + cmd.filePaths = msg.getInstallPaths(); + cmd.oldPrefix = msg.getOldPrefix(); + cmd.newPrefix = msg.getNewPrefix(); + cmd.setUuid(msg.getPrimaryStorageUuid()); + + KVMHostAsyncHttpCallMsg hmsg = new KVMHostAsyncHttpCallMsg(); + hmsg.setCommand(cmd); + hmsg.setPath(NFS_PREFIX_REBASE_BACKING_FILES_PATH); + hmsg.setHostUuid(hostUuid); + bus.makeTargetServiceIdByResourceUuid(hmsg, HostConstant.SERVICE_ID, hostUuid); + bus.send(hmsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + completion.fail(reply.getError()); + return; + } + + NfsPrimaryStorageKVMBackendCommands.PrefixRebaseBackingFilesRsp rsp = + ((KVMHostAsyncHttpCallReply) reply).toResponse(NfsPrimaryStorageKVMBackendCommands.PrefixRebaseBackingFilesRsp.class); + if (!rsp.isSuccess()) { + completion.fail(operr("failed to prefix rebase backing files on nfs via host[uuid:%s]: %s", hostUuid, rsp.getError())); + return; + } + + RebaseVolumeBackingFileOnPrimaryStorageReply r = new RebaseVolumeBackingFileOnPrimaryStorageReply(); + r.setRebasedCount(rsp.rebasedCount); + completion.success(r); + } + }); + } } diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.java index d7cebfaa4c6..12c41c054c7 100755 --- a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.java +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.java @@ -2,6 +2,7 @@ import org.zstack.header.HasThreadContext; import org.zstack.header.core.validation.Validation; +import org.zstack.header.storage.primary.VmMetadataScanEntry; import org.zstack.kvm.KVMAgentCommands; import org.zstack.kvm.KVMAgentCommands.AgentCommand; import org.zstack.kvm.KVMAgentCommands.AgentResponse; @@ -948,4 +949,51 @@ public void setHashValue(String hashValue) { this.hashValue = hashValue; } } + + public static class WriteVmMetadataCmd extends NfsPrimaryStorageAgentCommand { + public String metadata; + public String metadataPath; + public String vmUuid; + public String vmName; + public String vmCategory; + public String architecture; + public String schemaVersion; + } + + public static class WriteVmMetadataRsp extends NfsPrimaryStorageAgentResponse { + } + + + public static class GetVmInstanceMetadataCmd extends NfsPrimaryStorageAgentCommand { + public String metadataPath; + } + + public static class GetVmInstanceMetadataRsp extends NfsPrimaryStorageAgentResponse { + public String metadata; + } + + public static class ScanVmMetadataCmd extends NfsPrimaryStorageAgentCommand { + public String metadataDir; + } + + public static class ScanVmMetadataRsp extends NfsPrimaryStorageAgentResponse { + public List metadataEntries = new ArrayList<>(); + } + + public static class CleanupVmMetadataCmd extends NfsPrimaryStorageAgentCommand { + public String metadataPath; + } + + public static class CleanupVmMetadataRsp extends NfsPrimaryStorageAgentResponse { + } + + public static class PrefixRebaseBackingFilesCmd extends NfsPrimaryStorageAgentCommand { + public List filePaths; + public String oldPrefix; + public String newPrefix; + } + + public static class PrefixRebaseBackingFilesRsp extends NfsPrimaryStorageAgentResponse { + public int rebasedCount; + } } diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsVmMetadataExtension.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsVmMetadataExtension.java new file mode 100644 index 00000000000..6aebb36c005 --- /dev/null +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsVmMetadataExtension.java @@ -0,0 +1,117 @@ +package org.zstack.storage.primary.nfs; + +import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.beans.factory.annotation.Configurable; +import org.zstack.core.db.Q; +import org.zstack.header.exception.CloudRuntimeException; +import org.zstack.header.storage.primary.PrimaryStorageVO; +import org.zstack.header.storage.primary.PrimaryStorageVO_; +import org.zstack.header.vm.metadata.VmInstanceMetadataConstants; +import org.zstack.header.vm.metadata.VmMetadataPathBuildExtensionPoint; +import org.zstack.header.vm.metadata.VmMetadataPathReplacementExtensionPoint; +import org.zstack.utils.Utils; +import org.zstack.utils.logging.CLogger; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) +public class NfsVmMetadataExtension implements VmMetadataPathBuildExtensionPoint, VmMetadataPathReplacementExtensionPoint { + private static final CLogger logger = Utils.getLogger(NfsVmMetadataExtension.class); + + @Override + public String getPrimaryStorageType() { + return NfsPrimaryStorageConstant.NFS_PRIMARY_STORAGE_TYPE; + } + + private String resolveBaseDir(String primaryStorageUuid) { + String baseDir = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.mountPath).eq(PrimaryStorageVO_.uuid, primaryStorageUuid).findValue(); + if (baseDir == null) { + baseDir = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(PrimaryStorageVO_.uuid, primaryStorageUuid).findValue(); + } + if (baseDir == null) { + throw new CloudRuntimeException(String.format("cannot find mountPath or url for NFS primary storage[uuid:%s]", primaryStorageUuid)); + } + return baseDir.endsWith("/") ? baseDir.substring(0, baseDir.length() - 1) : baseDir; + } + + @Override + public String buildMetadataDir(String primaryStorageUuid) { + String baseDir = resolveBaseDir(primaryStorageUuid); + return String.format("%s/%s", baseDir, VmInstanceMetadataConstants.METADATA_DIR_NAME); + } + + @Override + public String buildVmMetadataPath(String primaryStorageUuid, String vmInstanceUuid) { + String baseDir = resolveBaseDir(primaryStorageUuid); + return String.format("%s/%s/%s%s", baseDir, VmInstanceMetadataConstants.METADATA_DIR_NAME, vmInstanceUuid, VmInstanceMetadataConstants.FILE_METADATA_SUFFIX); + } + + @Override + public String validateMetadataPath(String primaryStorageUuid, String path) { + // Expected format: /.vmmeta + // e.g. /mnt/pss/vm-metadata/a1b2c3d4e5f6...vmmeta + String metadataDir = buildMetadataDir(primaryStorageUuid); + String prefix = metadataDir + "/"; + String suffix = VmInstanceMetadataConstants.FILE_METADATA_SUFFIX; + + if (!path.startsWith(prefix) || !path.endsWith(suffix)) { + return String.format("metadataPath[%s] does not match expected format: %s/%s", + path, metadataDir, suffix); + } + + String uuidPart = path.substring(prefix.length(), path.length() - suffix.length()); + if (!uuidPart.matches("[0-9a-f]{32}")) { + return String.format("metadataPath[%s] contains invalid uuid[%s], expected 32-char hex", path, uuidPart); + } + return null; + } + + @Override + public PathReplacementResult calculatePathReplacements(String targetPsUuid, List allOldPaths) { + if (allOldPaths == null || allOldPaths.isEmpty()) { + PathReplacementResult result = new PathReplacementResult(); + result.setMetadataToCurrentPathMap(Collections.emptyMap()); + return result; + } + + String baseDir = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.mountPath).eq(PrimaryStorageVO_.uuid, targetPsUuid).findValue(); + if (baseDir == null) { + baseDir = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(PrimaryStorageVO_.uuid, targetPsUuid).findValue(); + } + if (baseDir == null) { + logger.warn(String.format("NFS PS[uuid:%s] has no mountPath or url, path replacement disabled", targetPsUuid)); + PathReplacementResult result = new PathReplacementResult(); + result.setMetadataToCurrentPathMap(Collections.emptyMap()); + return result; + } + String newPrefix = baseDir.endsWith("/") ? baseDir : baseDir + "/"; + + // Extract old prefix from the first recognizable path + String oldPrefix = null; + for (String path : allOldPaths) { + oldPrefix = VmInstanceMetadataConstants.extractOldPrefix(path); + if (oldPrefix != null) break; + } + + Map pathMap = new LinkedHashMap<>(); + if (oldPrefix != null) { + for (String oldPath : allOldPaths) { + if (oldPath != null && oldPath.startsWith(oldPrefix)) { + pathMap.put(oldPath, newPrefix + oldPath.substring(oldPrefix.length())); + } + } + } else { + logger.warn(String.format("cannot extract old path prefix from any path for NFS PS[uuid:%s], " + + "path replacement disabled", targetPsUuid)); + } + + PathReplacementResult result = new PathReplacementResult(); + result.setMetadataToCurrentPathMap(pathMap); + result.setOldPrefix(oldPrefix); + result.setNewPrefix(newPrefix); + return result; + } +} diff --git a/resourceconfig/src/main/java/org/zstack/resourceconfig/APIDeleteResourceConfigMsg.java b/resourceconfig/src/main/java/org/zstack/resourceconfig/APIDeleteResourceConfigMsg.java index fd43d66dac3..7733f7702b0 100644 --- a/resourceconfig/src/main/java/org/zstack/resourceconfig/APIDeleteResourceConfigMsg.java +++ b/resourceconfig/src/main/java/org/zstack/resourceconfig/APIDeleteResourceConfigMsg.java @@ -5,11 +5,13 @@ import org.zstack.header.message.APIDeleteMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.header.vo.ResourceVO; @RestRequest(path = "/resource-configurations/{category}/{name}/{resourceUuid}", method = HttpMethod.DELETE, responseClass = APIDeleteResourceConfigEvent.class) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "ResourceUuidToVmUuidResolver", field = "resourceUuid") public class APIDeleteResourceConfigMsg extends APIDeleteMessage implements ResourceConfigMessage { @APIParam private String category; diff --git a/resourceconfig/src/main/java/org/zstack/resourceconfig/APIUpdateResourceConfigMsg.java b/resourceconfig/src/main/java/org/zstack/resourceconfig/APIUpdateResourceConfigMsg.java index 1b8437d2404..fc30ba61e3b 100644 --- a/resourceconfig/src/main/java/org/zstack/resourceconfig/APIUpdateResourceConfigMsg.java +++ b/resourceconfig/src/main/java/org/zstack/resourceconfig/APIUpdateResourceConfigMsg.java @@ -5,12 +5,14 @@ import org.zstack.header.message.APIMessage; import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import org.zstack.header.vm.metadata.MetadataImpact; import org.zstack.header.vo.ResourceVO; @RestRequest(path = "/resource-configurations/{category}/{name}/{resourceUuid}/actions", method = HttpMethod.PUT, isAction = true, responseClass = APIUpdateResourceConfigEvent.class) +@MetadataImpact(value = MetadataImpact.Impact.CONFIG, resolver = "ResourceUuidToVmUuidResolver", field = "resourceUuid") public class APIUpdateResourceConfigMsg extends APIMessage implements ResourceConfigMessage { @APIParam private String category; diff --git a/sdk/src/main/java/SourceClassMap.java b/sdk/src/main/java/SourceClassMap.java index 146b132c459..fc47060a642 100644 --- a/sdk/src/main/java/SourceClassMap.java +++ b/sdk/src/main/java/SourceClassMap.java @@ -247,6 +247,7 @@ public class SourceClassMap { put("org.zstack.header.storage.primary.PrimaryStorageHostStatus", "org.zstack.sdk.PrimaryStorageHostStatus"); put("org.zstack.header.storage.primary.PrimaryStorageInventory", "org.zstack.sdk.PrimaryStorageInventory"); put("org.zstack.header.storage.primary.UsageReport", "org.zstack.sdk.UsageReport"); + put("org.zstack.header.storage.primary.VmMetadataScanEntry", "org.zstack.sdk.VmMetadataScanEntry"); put("org.zstack.header.storage.snapshot.BatchDeleteVolumeSnapshotStruct", "org.zstack.sdk.BatchDeleteVolumeSnapshotStruct"); put("org.zstack.header.storage.snapshot.ShrinkResult", "org.zstack.sdk.ShrinkResult"); put("org.zstack.header.storage.snapshot.VolumeSnapshotBackupStorageRefInventory", "org.zstack.sdk.VolumeSnapshotBackupStorageRefInventory"); @@ -1163,6 +1164,7 @@ public class SourceClassMap { put("org.zstack.sdk.VmInstanceResourceMetadataGroupInventory", "org.zstack.header.vm.devices.VmInstanceResourceMetadataGroupInventory"); put("org.zstack.sdk.VmMemoryBillingInventory", "org.zstack.billing.generator.vm.memory.VmMemoryBillingInventory"); put("org.zstack.sdk.VmMemorySpendingDetails", "org.zstack.billing.spendingcalculator.vm.VmMemorySpendingDetails"); + put("org.zstack.sdk.VmMetadataScanEntry", "org.zstack.header.storage.primary.VmMetadataScanEntry"); put("org.zstack.sdk.VmNicBandwidthSpendingDetails", "org.zstack.billing.spendingcalculator.vmnic.VmNicBandwidthSpendingDetails"); put("org.zstack.sdk.VmNicConflictEntry", "org.zstack.header.storage.snapshot.group.VmNicConflictEntry"); put("org.zstack.sdk.VmNicInventory", "org.zstack.header.vm.VmNicInventory"); diff --git a/sdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataAction.java b/sdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataAction.java new file mode 100644 index 00000000000..46586cab3c4 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataAction.java @@ -0,0 +1,101 @@ +package org.zstack.sdk; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class CleanupVmInstanceMetadataAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.CleanupVmInstanceMetadataResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s]", error.code, error.description, error.details) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = true, nullElements = false, emptyString = true, noTrim = false) + public java.util.List vmUuids; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.CleanupVmInstanceMetadataResult value = res.getResult(org.zstack.sdk.CleanupVmInstanceMetadataResult.class); + ret.value = value == null ? new org.zstack.sdk.CleanupVmInstanceMetadataResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "PUT"; + info.path = "/vm-instances/metadata/cleanup"; + info.needSession = true; + info.needPoll = true; + info.parameterName = "cleanupVmInstanceMetadata"; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataResult.java b/sdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataResult.java new file mode 100644 index 00000000000..879a409cc59 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataResult.java @@ -0,0 +1,30 @@ +package org.zstack.sdk; + + + +public class CleanupVmInstanceMetadataResult { + public java.lang.Integer totalCleaned; + public void setTotalCleaned(java.lang.Integer totalCleaned) { + this.totalCleaned = totalCleaned; + } + public java.lang.Integer getTotalCleaned() { + return this.totalCleaned; + } + + public java.lang.Integer totalFailed; + public void setTotalFailed(java.lang.Integer totalFailed) { + this.totalFailed = totalFailed; + } + public java.lang.Integer getTotalFailed() { + return this.totalFailed; + } + + public java.util.List failedVmUuids; + public void setFailedVmUuids(java.util.List failedVmUuids) { + this.failedVmUuids = failedVmUuids; + } + public java.util.List getFailedVmUuids() { + return this.failedVmUuids; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageAction.java b/sdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageAction.java new file mode 100644 index 00000000000..a0d62c682a4 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageAction.java @@ -0,0 +1,101 @@ +package org.zstack.sdk; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class GetVmInstanceMetadataFromPrimaryStorageAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.GetVmInstanceMetadataFromPrimaryStorageResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s]", error.code, error.description, error.details) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String uuid; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.GetVmInstanceMetadataFromPrimaryStorageResult value = res.getResult(org.zstack.sdk.GetVmInstanceMetadataFromPrimaryStorageResult.class); + ret.value = value == null ? new org.zstack.sdk.GetVmInstanceMetadataFromPrimaryStorageResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "GET"; + info.path = "/primary-storage/vm-instances/metadata"; + info.needSession = true; + info.needPoll = true; + info.parameterName = ""; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageResult.java b/sdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageResult.java new file mode 100644 index 00000000000..d22099fae76 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageResult.java @@ -0,0 +1,14 @@ +package org.zstack.sdk; + + + +public class GetVmInstanceMetadataFromPrimaryStorageResult { + public java.lang.String metadata; + public void setMetadata(java.lang.String metadata) { + this.metadata = metadata; + } + public java.lang.String getMetadata() { + return this.metadata; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataAction.java b/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataAction.java new file mode 100644 index 00000000000..8af44d58133 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataAction.java @@ -0,0 +1,116 @@ +package org.zstack.sdk; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class RegisterVmInstanceFromMetadataAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.RegisterVmInstanceFromMetadataResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s]", error.code, error.description, error.details) + ); + } + + return this; + } + } + + @Param(required = true, maxLength = 2048, nonempty = true, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String metadataPath; + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String primaryStorageUuid; + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String zoneUuid; + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String clusterUuid; + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String hostUuid; + + @Param(required = false, maxLength = 255, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String name; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.RegisterVmInstanceFromMetadataResult value = res.getResult(org.zstack.sdk.RegisterVmInstanceFromMetadataResult.class); + ret.value = value == null ? new org.zstack.sdk.RegisterVmInstanceFromMetadataResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "POST"; + info.path = "/vm-instances/metadata/register"; + info.needSession = true; + info.needPoll = true; + info.parameterName = "params"; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataResult.java b/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataResult.java new file mode 100644 index 00000000000..11634dcf3f1 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataResult.java @@ -0,0 +1,14 @@ +package org.zstack.sdk; + +import org.zstack.sdk.VmInstanceInventory; + +public class RegisterVmInstanceFromMetadataResult { + public VmInstanceInventory inventory; + public void setInventory(VmInstanceInventory inventory) { + this.inventory = inventory; + } + public VmInstanceInventory getInventory() { + return this.inventory; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageAction.java b/sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageAction.java new file mode 100644 index 00000000000..bdb11d011bc --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageAction.java @@ -0,0 +1,101 @@ +package org.zstack.sdk; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class ScanVmInstanceMetadataFromPrimaryStorageAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.ScanVmInstanceMetadataFromPrimaryStorageResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s]", error.code, error.description, error.details) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String uuid; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.ScanVmInstanceMetadataFromPrimaryStorageResult value = res.getResult(org.zstack.sdk.ScanVmInstanceMetadataFromPrimaryStorageResult.class); + ret.value = value == null ? new org.zstack.sdk.ScanVmInstanceMetadataFromPrimaryStorageResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "GET"; + info.path = "/primary-storage/vm-instances/metadata/scan"; + info.needSession = true; + info.needPoll = true; + info.parameterName = ""; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageResult.java b/sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageResult.java new file mode 100644 index 00000000000..4abb18ceae6 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageResult.java @@ -0,0 +1,14 @@ +package org.zstack.sdk; + + + +public class ScanVmInstanceMetadataFromPrimaryStorageResult { + public java.util.List vmInstanceMetadata; + public void setVmInstanceMetadata(java.util.List vmInstanceMetadata) { + this.vmInstanceMetadata = vmInstanceMetadata; + } + public java.util.List getVmInstanceMetadata() { + return this.vmInstanceMetadata; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/UpdateVmInstanceMetadataAction.java b/sdk/src/main/java/org/zstack/sdk/UpdateVmInstanceMetadataAction.java new file mode 100644 index 00000000000..2f2620e38e2 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/UpdateVmInstanceMetadataAction.java @@ -0,0 +1,101 @@ +package org.zstack.sdk; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class UpdateVmInstanceMetadataAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.UpdateVmInstanceMetadataResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s]", error.code, error.description, error.details) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = true, nullElements = false, emptyString = true, noTrim = false) + public java.util.List vmUuids; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.UpdateVmInstanceMetadataResult value = res.getResult(org.zstack.sdk.UpdateVmInstanceMetadataResult.class); + ret.value = value == null ? new org.zstack.sdk.UpdateVmInstanceMetadataResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "PUT"; + info.path = "/vm-instances/metadata/actions"; + info.needSession = true; + info.needPoll = true; + info.parameterName = "updateVmInstanceMetadata"; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/UpdateVmInstanceMetadataResult.java b/sdk/src/main/java/org/zstack/sdk/UpdateVmInstanceMetadataResult.java new file mode 100644 index 00000000000..d09f1fe7bf7 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/UpdateVmInstanceMetadataResult.java @@ -0,0 +1,7 @@ +package org.zstack.sdk; + + + +public class UpdateVmInstanceMetadataResult { + +} diff --git a/sdk/src/main/java/org/zstack/sdk/VmMetadataScanEntry.java b/sdk/src/main/java/org/zstack/sdk/VmMetadataScanEntry.java new file mode 100644 index 00000000000..fc5fc13f644 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/VmMetadataScanEntry.java @@ -0,0 +1,87 @@ +package org.zstack.sdk; + + + +public class VmMetadataScanEntry { + + public java.lang.String vmUuid; + public void setVmUuid(java.lang.String vmUuid) { + this.vmUuid = vmUuid; + } + public java.lang.String getVmUuid() { + return this.vmUuid; + } + + public java.lang.String vmName; + public void setVmName(java.lang.String vmName) { + this.vmName = vmName; + } + public java.lang.String getVmName() { + return this.vmName; + } + + public java.lang.String vmCategory; + public void setVmCategory(java.lang.String vmCategory) { + this.vmCategory = vmCategory; + } + public java.lang.String getVmCategory() { + return this.vmCategory; + } + + public java.lang.String architecture; + public void setArchitecture(java.lang.String architecture) { + this.architecture = architecture; + } + public java.lang.String getArchitecture() { + return this.architecture; + } + + public java.lang.String schemaVersion; + public void setSchemaVersion(java.lang.String schemaVersion) { + this.schemaVersion = schemaVersion; + } + public java.lang.String getSchemaVersion() { + return this.schemaVersion; + } + + public java.lang.String metadataPath; + public void setMetadataPath(java.lang.String metadataPath) { + this.metadataPath = metadataPath; + } + public java.lang.String getMetadataPath() { + return this.metadataPath; + } + + public java.lang.String hostUuid; + public void setHostUuid(java.lang.String hostUuid) { + this.hostUuid = hostUuid; + } + public java.lang.String getHostUuid() { + return this.hostUuid; + } + + public long sizeBytes; + public void setSizeBytes(long sizeBytes) { + this.sizeBytes = sizeBytes; + } + public long getSizeBytes() { + return this.sizeBytes; + } + + public long lastUpdateTime; + public void setLastUpdateTime(long lastUpdateTime) { + this.lastUpdateTime = lastUpdateTime; + } + public long getLastUpdateTime() { + return this.lastUpdateTime; + } + + public boolean incomplete; + public void setIncomplete(boolean incomplete) { + this.incomplete = incomplete; + } + public boolean getIncomplete() { + return this.incomplete; + } + +} diff --git a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java index b7f8cfbc24d..d31026b7014 100755 --- a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java +++ b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.transaction.annotation.Transactional; +import org.zstack.core.Platform; import org.zstack.core.asyncbatch.While; import org.zstack.core.cascade.CascadeConstant; import org.zstack.core.cascade.CascadeFacade; @@ -50,6 +51,7 @@ import org.zstack.header.storage.primary.PrimaryStorageCanonicalEvent.PrimaryStorageStatusChangedData; import org.zstack.header.storage.snapshot.*; import org.zstack.header.vm.*; +import org.zstack.header.vm.metadata.*; import org.zstack.header.volume.*; import org.zstack.storage.volume.VolumeUtils; import org.zstack.utils.CollectionDSL; @@ -417,6 +419,16 @@ protected void handleLocalMessage(Message msg) { handle((DeleteVolumeChainOnPrimaryStorageMsg) msg); } else if (msg instanceof CleanUpStorageTrashOnPrimaryStorageMsg) { handle((CleanUpStorageTrashOnPrimaryStorageMsg)msg); + } else if (msg instanceof UpdateVmInstanceMetadataOnPrimaryStorageMsg) { + handle((UpdateVmInstanceMetadataOnPrimaryStorageMsg) msg); + } else if (msg instanceof ScanVmInstanceMetadataFromPrimaryStorageMsg) { + handle((ScanVmInstanceMetadataFromPrimaryStorageMsg) msg); + } else if (msg instanceof GetVmInstanceMetadataFromPrimaryStorageMsg) { + handle((GetVmInstanceMetadataFromPrimaryStorageMsg) msg); + } else if (msg instanceof CleanupVmInstanceMetadataOnPrimaryStorageMsg) { + handle((CleanupVmInstanceMetadataOnPrimaryStorageMsg) msg); + } else if (msg instanceof RebaseVolumeBackingFileOnPrimaryStorageMsg) { + handle((RebaseVolumeBackingFileOnPrimaryStorageMsg) msg); } else { bus.dealWithUnknownMessage(msg); } @@ -935,6 +947,8 @@ protected void handleApiMessage(APIMessage msg) { handle((APICleanUpStorageTrashOnPrimaryStorageMsg) msg); } else if (msg instanceof APIAddStorageProtocolMsg) { handle((APIAddStorageProtocolMsg) msg); + } else if (msg instanceof APIScanVmInstanceMetadataFromPrimaryStorageMsg) { + handle((APIScanVmInstanceMetadataFromPrimaryStorageMsg) msg); } else { bus.dealWithUnknownMessage(msg); } @@ -1773,6 +1787,36 @@ protected void handle(UnlinkBitsOnPrimaryStorageMsg msg) { bus.reply(msg, reply); }; + protected void handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg msg) { + UpdateVmInstanceMetadataOnPrimaryStorageReply reply = new UpdateVmInstanceMetadataOnPrimaryStorageReply(); + reply.setError(operr("operation not supported")); + bus.reply(msg, reply); + } + + protected void handle(GetVmInstanceMetadataFromPrimaryStorageMsg msg) { + GetVmInstanceMetadataFromPrimaryStorageReply reply = new GetVmInstanceMetadataFromPrimaryStorageReply(); + reply.setError(operr("operation not supported")); + bus.reply(msg, reply); + } + + protected void handle(ScanVmInstanceMetadataFromPrimaryStorageMsg msg) { + ScanVmInstanceMetadataFromPrimaryStorageReply reply = new ScanVmInstanceMetadataFromPrimaryStorageReply(); + reply.setError(operr("operation not supported")); + bus.reply(msg, reply); + } + + protected void handle(CleanupVmInstanceMetadataOnPrimaryStorageMsg msg) { + CleanupVmInstanceMetadataOnPrimaryStorageReply reply = new CleanupVmInstanceMetadataOnPrimaryStorageReply(); + reply.setError(operr("operation not supported")); + bus.reply(msg, reply); + } + + protected void handle(RebaseVolumeBackingFileOnPrimaryStorageMsg msg) { + RebaseVolumeBackingFileOnPrimaryStorageReply reply = new RebaseVolumeBackingFileOnPrimaryStorageReply(); + reply.setError(operr("operation not supported")); + bus.reply(msg, reply); + } + // don't attach any cluster public boolean isUnmounted() { long count = Q.New(PrimaryStorageClusterRefVO.class) @@ -1812,4 +1856,42 @@ protected ImageCacheVO createTemporaryImageCacheFromVolumeSnapshot(ImageInventor private static String getDeduplicateError(String operationName) { return String.format("an other %s task is running, cancel this operation", operationName); } + + private void handle(APIScanVmInstanceMetadataFromPrimaryStorageMsg msg) { + APIScanVmInstanceMetadataFromPrimaryStorageEvent evt = new APIScanVmInstanceMetadataFromPrimaryStorageEvent(msg.getId()); + + String psType = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.type).eq(PrimaryStorageVO_.uuid, msg.getPrimaryStorageUuid()).findValue(); + VmMetadataPathBuildExtensionPoint ext = pluginRgty.getExtensionFromMap(psType, VmMetadataPathBuildExtensionPoint.class); + if (ext == null) { + evt.setError(Platform.operr("primary storage type %s does not support metadata", psType)); + bus.publish(evt); + return; + } + String metadataDir = ext.buildMetadataDir(msg.getPrimaryStorageUuid()); + + ScanVmInstanceMetadataFromPrimaryStorageMsg gmsg = new ScanVmInstanceMetadataFromPrimaryStorageMsg(); + gmsg.setPrimaryStorageUuid(msg.getPrimaryStorageUuid()); + gmsg.setMetadataDir(metadataDir); + bus.makeTargetServiceIdByResourceUuid(gmsg, PrimaryStorageConstant.SERVICE_ID, msg.getPrimaryStorageUuid()); + bus.send(gmsg, new CloudBusCallBack(msg) { + @Override + public void run(MessageReply r) { + if (!r.isSuccess()) { + evt.setError(r.getError()); + bus.publish(evt); + return; + } + ScanVmInstanceMetadataFromPrimaryStorageReply re = r.castReply(); + List metadata = re.getVmInstanceMetadata(); + if (metadata == null) { + metadata = Collections.emptyList(); + } + List filtered = metadata.stream() + .filter(e -> !VmMetadataCategory.VM_TEMPLATE_CACHE.name().equals(e.getVmCategory())) + .collect(Collectors.toList()); + evt.setVmInstanceMetadata(filtered); + bus.publish(evt); + } + }); + } } diff --git a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy index 07c05b73b9e..91aca4c069e 100644 --- a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy +++ b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy @@ -5606,6 +5606,33 @@ abstract class ApiHelper { } + def cleanupVmInstanceMetadata(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.CleanupVmInstanceMetadataAction.class) Closure c) { + def a = new org.zstack.sdk.CleanupVmInstanceMetadataAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def cloneVmInstance(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.CloneVmInstanceAction.class) Closure c) { def a = new org.zstack.sdk.CloneVmInstanceAction() a.sessionId = Test.currentEnvSpec?.session?.uuid @@ -6929,33 +6956,6 @@ abstract class ApiHelper { } - def updateHostname(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.UpdateHostnameAction.class) Closure c) { - def a = new org.zstack.sdk.UpdateHostnameAction() - a.sessionId = Test.currentEnvSpec?.session?.uuid - c.resolveStrategy = Closure.OWNER_FIRST - c.delegate = a - c() - - - if (System.getProperty("apipath") != null) { - if (a.apiId == null) { - a.apiId = Platform.uuid - } - - def tracker = new ApiPathTracker(a.apiId) - def out = errorOut(a.call()) - def path = tracker.getApiPath() - if (!path.isEmpty()) { - Test.apiPaths[a.class.name] = path.join(" --->\n") - } - - return out - } else { - return errorOut(a.call()) - } - } - - def createIPsecConnection(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.CreateIPsecConnectionAction.class) Closure c) { def a = new org.zstack.sdk.CreateIPsecConnectionAction() a.sessionId = Test.currentEnvSpec?.session?.uuid @@ -18566,6 +18566,33 @@ abstract class ApiHelper { } + def getVmInstanceMetadataFromPrimaryStorage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.GetVmInstanceMetadataFromPrimaryStorageAction.class) Closure c) { + def a = new org.zstack.sdk.GetVmInstanceMetadataFromPrimaryStorageAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def getVmInstanceProtectedRecoveryPoints(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.GetVmInstanceProtectedRecoveryPointsAction.class) Closure c) { def a = new org.zstack.sdk.GetVmInstanceProtectedRecoveryPointsAction() a.sessionId = Test.currentEnvSpec?.session?.uuid @@ -27406,6 +27433,33 @@ abstract class ApiHelper { } + def registerVmInstanceFromMetadata(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.RegisterVmInstanceFromMetadataAction.class) Closure c) { + def a = new org.zstack.sdk.RegisterVmInstanceFromMetadataAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def reimageVmInstance(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.ReimageVmInstanceAction.class) Closure c) { def a = new org.zstack.sdk.ReimageVmInstanceAction() a.sessionId = Test.currentEnvSpec?.session?.uuid @@ -28621,6 +28675,33 @@ abstract class ApiHelper { } + def scanVmInstanceMetadataFromPrimaryStorage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.ScanVmInstanceMetadataFromPrimaryStorageAction.class) Closure c) { + def a = new org.zstack.sdk.ScanVmInstanceMetadataFromPrimaryStorageAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def securityMachineDetectSync(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.SecurityMachineDetectSyncAction.class) Closure c) { def a = new org.zstack.sdk.SecurityMachineDetectSyncAction() a.sessionId = Test.currentEnvSpec?.session?.uuid @@ -32212,6 +32293,33 @@ abstract class ApiHelper { } + def updateHostname(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.UpdateHostnameAction.class) Closure c) { + def a = new org.zstack.sdk.UpdateHostnameAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def updateIPsecConnection(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.UpdateIPsecConnectionAction.class) Closure c) { def a = new org.zstack.sdk.UpdateIPsecConnectionAction() a.sessionId = Test.currentEnvSpec?.session?.uuid @@ -34183,6 +34291,33 @@ abstract class ApiHelper { } + def updateVmInstanceMetadata(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.UpdateVmInstanceMetadataAction.class) Closure c) { + def a = new org.zstack.sdk.UpdateVmInstanceMetadataAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def updateVmNicDriver(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.UpdateVmNicDriverAction.class) Closure c) { def a = new org.zstack.sdk.UpdateVmNicDriverAction() a.sessionId = Test.currentEnvSpec?.session?.uuid @@ -38395,8 +38530,8 @@ abstract class ApiHelper { } - def addZBox(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.AddZBoxAction.class) Closure c) { - def a = new org.zstack.sdk.zbox.AddZBoxAction() + def cleanSoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.CleanSoftwarePackageAction.class) Closure c) { + def a = new org.zstack.sdk.softwarePackage.header.CleanSoftwarePackageAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a @@ -38422,8 +38557,8 @@ abstract class ApiHelper { } - def createZBoxBackup(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.CreateZBoxBackupAction.class) Closure c) { - def a = new org.zstack.sdk.zbox.CreateZBoxBackupAction() + def getDirectoryUsage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.GetDirectoryUsageAction.class) Closure c) { + def a = new org.zstack.sdk.softwarePackage.header.GetDirectoryUsageAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a @@ -38449,8 +38584,8 @@ abstract class ApiHelper { } - def ejectZBox(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.EjectZBoxAction.class) Closure c) { - def a = new org.zstack.sdk.zbox.EjectZBoxAction() + def getUploadSoftwarePackageJobDetails(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.GetUploadSoftwarePackageJobDetailsAction.class) Closure c) { + def a = new org.zstack.sdk.softwarePackage.header.GetUploadSoftwarePackageJobDetailsAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a @@ -38476,8 +38611,8 @@ abstract class ApiHelper { } - def getZBoxBackupDetails(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.GetZBoxBackupDetailsAction.class) Closure c) { - def a = new org.zstack.sdk.zbox.GetZBoxBackupDetailsAction() + def installSoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.InstallSoftwarePackageAction.class) Closure c) { + def a = new org.zstack.sdk.softwarePackage.header.InstallSoftwarePackageAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a @@ -38503,8 +38638,8 @@ abstract class ApiHelper { } - def queryZBox(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.QueryZBoxAction.class) Closure c) { - def a = new org.zstack.sdk.zbox.QueryZBoxAction() + def querySoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.QuerySoftwarePackageAction.class) Closure c) { + def a = new org.zstack.sdk.softwarePackage.header.QuerySoftwarePackageAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a @@ -38532,15 +38667,13 @@ abstract class ApiHelper { } - def queryZBoxBackup(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.QueryZBoxBackupAction.class) Closure c) { - def a = new org.zstack.sdk.zbox.QueryZBoxBackupAction() + def uninstallSoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.UninstallSoftwarePackageAction.class) Closure c) { + def a = new org.zstack.sdk.softwarePackage.header.UninstallSoftwarePackageAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - a.conditions = a.conditions.collect { it.toString() } - if (System.getProperty("apipath") != null) { if (a.apiId == null) { @@ -38561,8 +38694,8 @@ abstract class ApiHelper { } - def syncZBoxCapacity(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.SyncZBoxCapacityAction.class) Closure c) { - def a = new org.zstack.sdk.zbox.SyncZBoxCapacityAction() + def uploadSoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.UploadSoftwarePackageAction.class) Closure c) { + def a = new org.zstack.sdk.softwarePackage.header.UploadSoftwarePackageAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a @@ -38588,25 +38721,26 @@ abstract class ApiHelper { } - def cleanSoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.CleanSoftwarePackageAction.class) Closure c) { - def a = new org.zstack.sdk.softwarePackage.header.CleanSoftwarePackageAction() + def addZBox(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.AddZBoxAction.class) Closure c) { + def a = new org.zstack.sdk.zbox.AddZBoxAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -38614,25 +38748,26 @@ abstract class ApiHelper { } - def getDirectoryUsage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.GetDirectoryUsageAction.class) Closure c) { - def a = new org.zstack.sdk.softwarePackage.header.GetDirectoryUsageAction() + def createZBoxBackup(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.CreateZBoxBackupAction.class) Closure c) { + def a = new org.zstack.sdk.zbox.CreateZBoxBackupAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -38640,25 +38775,26 @@ abstract class ApiHelper { } - def getUploadSoftwarePackageJobDetails(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.GetUploadSoftwarePackageJobDetailsAction.class) Closure c) { - def a = new org.zstack.sdk.softwarePackage.header.GetUploadSoftwarePackageJobDetailsAction() + def ejectZBox(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.EjectZBoxAction.class) Closure c) { + def a = new org.zstack.sdk.zbox.EjectZBoxAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -38666,22 +38802,26 @@ abstract class ApiHelper { } - def installSoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.InstallSoftwarePackageAction.class) Closure c) { - def a = new org.zstack.sdk.softwarePackage.header.InstallSoftwarePackageAction() + def getZBoxBackupDetails(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.GetZBoxBackupDetailsAction.class) Closure c) { + def a = new org.zstack.sdk.zbox.GetZBoxBackupDetailsAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() + + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } + return out } else { return errorOut(a.call()) @@ -38689,13 +38829,13 @@ abstract class ApiHelper { } - def querySoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.QuerySoftwarePackageAction.class) Closure c) { - def a = new org.zstack.sdk.softwarePackage.header.QuerySoftwarePackageAction() + def queryZBox(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.QueryZBoxAction.class) Closure c) { + def a = new org.zstack.sdk.zbox.QueryZBoxAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + a.conditions = a.conditions.collect { it.toString() } @@ -38703,14 +38843,14 @@ abstract class ApiHelper { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -38718,26 +38858,28 @@ abstract class ApiHelper { } - def uninstallSoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.UninstallSoftwarePackageAction.class) Closure c) { - def a = new org.zstack.sdk.softwarePackage.header.UninstallSoftwarePackageAction() + def queryZBoxBackup(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.QueryZBoxBackupAction.class) Closure c) { + def a = new org.zstack.sdk.zbox.QueryZBoxBackupAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() + + a.conditions = a.conditions.collect { it.toString() } if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -38745,25 +38887,26 @@ abstract class ApiHelper { } - def uploadSoftwarePackage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.softwarePackage.header.UploadSoftwarePackageAction.class) Closure c) { - def a = new org.zstack.sdk.softwarePackage.header.UploadSoftwarePackageAction() + def syncZBoxCapacity(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zbox.SyncZBoxCapacityAction.class) Closure c) { + def a = new org.zstack.sdk.zbox.SyncZBoxCapacityAction() a.sessionId = Test.currentEnvSpec?.session?.uuid c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) diff --git a/testlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovy b/testlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovy index 747ca4388b1..c7143282f6b 100755 --- a/testlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovy +++ b/testlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovy @@ -600,6 +600,22 @@ class LocalStorageSpec extends PrimaryStorageSpec { rsp.hashValue = cmd.installPath return rsp } + + simulator(LocalStorageKvmBackend.WRITE_VM_METADATA_PATH) { + return new LocalStorageKvmBackend.WriteVmMetadataRsp() + } + + simulator(LocalStorageKvmBackend.GET_VM_INSTANCE_METADATA_PATH) { + return new LocalStorageKvmBackend.GetVmInstanceMetadataRsp() + } + + simulator(LocalStorageKvmBackend.SCAN_VM_METADATA_PATH) { + return new LocalStorageKvmBackend.ScanVmMetadataRsp() + } + + simulator(LocalStorageKvmBackend.CLEANUP_VM_METADATA_PATH) { + return new LocalStorageKvmBackend.CleanupVmMetadataRsp() + } } } diff --git a/testlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy b/testlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy index 0dbe59bfc01..09344d42a5c 100755 --- a/testlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy +++ b/testlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy @@ -522,6 +522,22 @@ class NfsPrimaryStorageSpec extends PrimaryStorageSpec { return rsp } + simulator(NfsPrimaryStorageKVMBackend.WRITE_VM_METADATA_PATH) { + return new NfsPrimaryStorageKVMBackendCommands.WriteVmMetadataRsp() + } + + simulator(NfsPrimaryStorageKVMBackend.GET_VM_INSTANCE_METADATA_PATH) { + return new NfsPrimaryStorageKVMBackendCommands.GetVmInstanceMetadataRsp() + } + + simulator(NfsPrimaryStorageKVMBackend.SCAN_VM_METADATA_PATH) { + return new NfsPrimaryStorageKVMBackendCommands.ScanVmMetadataRsp() + } + + simulator(NfsPrimaryStorageKVMBackend.CLEANUP_VM_METADATA_PATH) { + return new NfsPrimaryStorageKVMBackendCommands.CleanupVmMetadataRsp() + } + VFS.vfsHook(NfsPrimaryStorageKVMBackend.NFS_REBASE_VOLUME_BACKING_FILE_PATH, xspec) { rsp, HttpEntity e, EnvSpec spec -> def cmd = JSONObjectUtil.toObject(e.body, NfsPrimaryStorageKVMBackendCommands.NfsRebaseVolumeBackingFileCmd.class)